AddOutValue/LoadProperty not functioning on Guid data type in Business Rule

AddOutValue/LoadProperty not functioning on Guid data type in Business Rule

Old forum URL: forums.lhotka.net/forums/t/11969.aspx


NightOwl888 posted on Sunday, May 05, 2013

I have been scratching my head for some time trying to figure out why my business rule wasn't setting a Guid property on my business object, since I had done this sort of thing before. However, it occurred to me that the other properties I was setting were strings and primitive values.

So, I copied and pasted my UserId property (a guid) and made it into a string. Then I wired up the new property to my business rule, and it works. The thing is, I need it to be a Guid.

I tried both the AddOutValue and LoadProperty techniques to set the output of my rule, but it doesn't function with Guid in either case. So is there something special I have to do to make this work, or did I find a bug?

Here are my properties (Identical in every way except datatype):

        public static readonly PropertyInfo<Guid> UserIdProperty = RegisterProperty<Guid>(c => c.UserId);
        public Guid UserId
        {
            get { return GetProperty(UserIdProperty); }
            private set { LoadProperty(UserIdProperty, value); }
        }

        public static readonly PropertyInfo<string> UserIdStringProperty = RegisterProperty<string>(c => c.UserIdString);
        public string UserIdString
        {
            get { return GetProperty(UserIdStringProperty); }
            private set { LoadProperty(UserIdStringProperty, value); }
        }

 

And this is my business rule:

    public class UserHasConfirmedAccountRule
        : PropertyRule
    {
        public UserHasConfirmedAccountRule(
            Type modelType,
            ILocalizedStringProvider localizedStringProvider,
            IPropertyInfo emailAddressOrPublicNameProperty,
            IPropertyInfo tenantIdProperty,
            IPropertyInfo userIdProperty,
            IUserAccountRepository userAccountRepository
            )
            : base(emailAddressOrPublicNameProperty, modelType, localizedStringProvider)
        {
            if (emailAddressOrPublicNameProperty == null)
                throw new ArgumentNullException("emailAddressOrPublicNameProperty");
            if (tenantIdProperty == null)
                throw new ArgumentNullException("tenantIdProperty");
            if (userIdProperty == null)
                throw new ArgumentNullException("userIdProperty");
            if (userAccountRepository == null)
                throw new ArgumentNullException("userAccountRepository");

            this.emailAddressOrPublicNameProperty = emailAddressOrPublicNameProperty;
            this.tenantIdProperty = tenantIdProperty;
            this.userIdProperty = userIdProperty;
            this.userAccountRepository = userAccountRepository;

            InputProperties = new List<IPropertyInfo> { emailAddressOrPublicNameProperty, tenantIdProperty, userIdProperty };
            AffectedProperties.Add(userIdProperty);
        }

        private readonly IPropertyInfo emailAddressOrPublicNameProperty;
        private readonly IPropertyInfo tenantIdProperty;
        private readonly IPropertyInfo userIdProperty;
        private readonly IUserAccountRepository userAccountRepository;

        protected override void Execute(CslaLibrary.Rules.RuleContext context)
        {
            var emailAddressOrPublicName = (string)context.InputPropertyValues[emailAddressOrPublicNameProperty];
            var tenantId = (int)context.InputPropertyValues[tenantIdProperty];

            bool exists = false;
            Guid userId = Guid.Empty;
            if (!string.IsNullOrEmpty(emailAddressOrPublicName) && tenantId > 0)
            {
                userId = userAccountRepository.HasConfirmedAccount(tenantId, emailAddressOrPublicName);
                if (userId != null && !userId.Equals(Guid.Empty))
                {
                    exists = true;
                    context.AddOutValue(userIdProperty, userId.ToString());

                    //var target = (ResetPassword)context.Target;
                    //LoadProperty(target, userIdProperty, userId.ToString());
                }
            }

            if (!exists)
                context.AddErrorResult(GetMessage());
        }
    }

 

Note that I am using a subclass of PropertyRule, but I changed it to the CSLA BusinessRule type and still got the same result. Also note the code above is the version that works - I wouldn't use .ToString() when trying to set a guid.

JonnyBee replied on Monday, May 06, 2013

Which version of CSLA?

When I run this code with the latest CSLA code in trunk it works as expected:

  [Serializable]
  public class Root : BusinessBase<Root>
  {
    public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);     [Required]   // Data Annotations rule for Required field     public string Name     {       get { return GetProperty(NameProperty); }       set { SetProperty(NameProperty, value); }     }
    public static readonly PropertyInfo<Guid> UserIdProperty = RegisterProperty<Guid>(c => c.UserId);     public Guid UserId     {       get { return GetProperty(UserIdProperty); }       set { LoadProperty(UserIdProperty, value); }     }

    protected override void AddBusinessRules()     {       base.AddBusinessRules();       BusinessRules.AddRule(new LookupUserId(NameProperty, UserIdProperty));     }

    private class LookupUserId : Csla.Rules.BusinessRule     {       private readonly PropertyInfo<Guid> _userIdProperty;       public LookupUserId(IPropertyInfo primary, PropertyInfo<Guid> userIdProperty)         : base(primary)       {         _userIdProperty = userIdProperty;         AffectedProperties.Add(userIdProperty);       }       protected override void Execute(RuleContext context)       {
// for this test just set a new GUID into userid property by AddOutValue         context.AddOutValue(_userIdProperty, Guid.NewGuid());       }     }

    public void CheckAllRules()     {       BusinessRules.CheckRules();     }
}

and this code in program
      var root = Root.NewEditableRoot();
// this guid property setter uses LoadProperty and will not run rules       root.UserId = Guid.NewGuid();       Debug.Print("userid={0}",root.UserId);
// call checkrules - should set a new guid in UserId       root.CheckAllRules();       Debug.Print("userid={0}", root.UserId);

prints this result:
userid=2cfa5afb-236e-41fa-9d45-1c6b629dded2
userid=9beb3875-4749-4e54-8e40-a329a602c868


This shows that
  1. LoadProperty works as expected in property setter
  2. Rules AddOutValue works as expected to load a new value into an affected property

NightOwl888 replied on Monday, May 06, 2013

I am using CSLA 4.5.20 and .NET 4.5.

NightOwl888 replied on Monday, May 06, 2013

I just noticed in your example you are using PropertyInfo<Guid> for your parameter value and I was using IPropertyInfo. I haven't tried it yet, but I suspect that could be the fix.

JonnyBee replied on Monday, May 06, 2013

Nope,

I  changed the type of the private backing field in the rule to IPropertyInfo and it still worked fine. 

NightOwl888 replied on Monday, May 06, 2013

Ok, I checked and indeed using a parameter and member variable declared as PropertyInfo<Guid> works, but when it is declared as IPropertyInfo it does not. IPropertyInfo seems to work for string and primitive types, but not for Guid.

Being that all of the examples in the book use IPropertyInfo I would still call this a bug. However, at least now I have a workaround for it other than changing to string.

Copyright (c) Marimer LLC