MarimerLLC / cslaforum

Discussion forum for CSLA .NET
https://cslanet.com
Other
31 stars 6 forks source link

very basic question #426

Open Chicagoan2016 opened 7 years ago

Chicagoan2016 commented 7 years ago

I know this question is very basic : ), I have a string read/write csla managed property and I want to have a default value for that property, I am kinda confused about the different overloads of RegisterProperty() what will be the correct syntax of Register property method for this scenario?

regards

ajj7060 commented 7 years ago

RegisterProperty(x => x.MyString, "My string friendlyname", "MyStringDefaultValue");

Chicagoan2016 commented 7 years ago

Thanks @ajj7060 , In the RuleTutorial sample, under project CompareFieldsRules there is a property declaration of public static readonly PropertyInfo StartDateProperty = RegisterProperty(c => c.StartDate, null, new SmartDate() {Date = new Func(() => DateTime.Now).Invoke()});

If I am not mistaken here the default value is the current datetime? boy,writing this kind of code does make me look like a real software developer : ) which is cool, right?

kind regards

ajj7060 commented 7 years ago

The default value will be a date time. You'd have to test it though, I suspect that doesn't do what you think it does, which is get the current date time at the time the BO containing that property definition is created. I'm betting every single instance of the BO will have the time of the first BO's creation.

i've been bitten by that once, but trying to put new MobileList<string> as the default. It didn't work, because EVERY instance of the BO was sharing a single instance of the MobileList. Which of course caused some very hard to track down problems...

Actually that'd be a nice feature, RegisterProperty which takes a Func for the default.

jonnybee commented 7 years ago

It is important to understand the difference between value type and reference types.

Default value on RegisterProperty only works as expected for value type.

SmartDate is a "struct" (which is a value type) and MobileList is a reference type.

See: https://msdn.microsoft.com/en-us/library/4d43ts61%28v=vs.90%29.aspx?f=255&MSPPError=-2147217396 and http://www.albahari.com/valuevsreftypes.aspx

jonnybee commented 7 years ago

And the reasoning behind this is that a reference type (typically a child list/child object) should either be set to a default value in DataPortal_Create/Child_Create or be loaded in DataPortal_Fetch or be lazy loaded when the property getter is called the first time.

ajj7060 commented 7 years ago

Thanks Jonny, I'm familiar with how reference types and value types work; my attempt to set MobileList was a face palm moment. But that's missing the point of this thread, which is that RegisterProperty's DefaultValue parameter only allows for what effectively becomes a constant. The confusion here is the code from the RuleTutorial sample, which seems to imply you can have a default value which is "now."

public static readonly PropertyInfo StartDateProperty = RegisterProperty(c => c.StartDate, null, new SmartDate() {Date = new Func(() => DateTime.Now).Invoke()});

Its clear that the intent of that code is for the BO to have StartDate set to whatever time it is when the BO instance gets created. What actually happens is all BOs get a StartDate which is the time the RegisterProperty call was made (which happens only once).

Consider the following program which uses the above RegisterProperty call.

using System;
using System.Threading;
using Csla;

namespace CslaDefaultValues {
    internal class Program {
        internal static void Main(string[] args) {
            var bo1 = MyBO.NewBO();
            Thread.Sleep(new TimeSpan(0, 0, 0, 5));
            var bo2 = MyBO.NewBO();

            Console.WriteLine($"MyBO1.Date = {bo1.StartDate.Date}, MyBO2.Date = {bo2.StartDate.Date}");
            Console.ReadLine();
        }
    }

    [Serializable]
    public sealed class MyBO : BusinessBase<MyBO> {
        public static readonly PropertyInfo<SmartDate> StartDateProperty = 
            RegisterProperty(c => c.StartDate, null, new SmartDate { Date = new Func<DateTime>(() => DateTime.Now).Invoke() });

        public SmartDate StartDate {
            get => GetProperty(StartDateProperty);
            set => SetProperty(StartDateProperty, value);
        }

        public static MyBO NewBO() => DataPortal.Create<MyBO>();

        protected override void DataPortal_Create() {
            BusinessRules.CheckRules();
        }
    }
}

Again, the intent of the developer would be that bo2.StartDate's value is five seconds after bo1.

My point was that this is due to the defaultValue being a T instead of Func<T>. If there were an overload of RegisterProperty which took Func<T> then we could do what is attempted here; eg:

public static readonly PropertyInfo<SmartDate> StartDateProperty = RegisterProperty(c => c.StartDate, null, () => new SmartDate { Date = DateTime.Now });

That would work as expected. But today in order to use the current value of DateTime.Now you need to do that in a XYZ_Create data portal method, and that has nothing to do with reference vs. value type. Now, maybe there is some issue with taking a Func<T> in RegisterProperty that occurs becomes of something in the framework, but the lack of such an overload is why you need to do stuff like this in the data portal method.

jonnybee commented 7 years ago

Ok, I see the issue now.

Chicagoan2016 commented 7 years ago

Thanks @ajj7060 , I am glad you explained this with sample code, I have to go back and change some code. I forgot the way RegisterProperty method works and thought the default value would work like regular non-csla properties.

Regards

rockfordlhotka commented 7 years ago

I wonder if we should look at an overload that accepts an Action that you can supply to initialize the value?

Chicagoan2016 commented 7 years ago

@rockfordlhotka , do you mean a new overload? or one of the nine overloads currently available.

kind regards

ajj7060 commented 7 years ago

@rockfordlhotka That'd be a nice addition I think, personally. Its not a huge issue I think, but the way things currently are some of the initialization is in the RegisterProperty calls, and other other in the _Create DP methods, so to see how everything overall is initialized you need to look in multiple places. An Action<T> might let people do all the defaulting in RegisterProperty, like this:

public static readonly PropertyInfo<ChildList> ChildrenProperty = RegisterProperty("Children", () => ChildList.NewChildList())

Not sure if that'd actually be a good idea though 😆

If this gets added, I can take the ticket. I need a reason to learn Github better :-/