I have made CSLA .NET version 3.5 Beta 1 online for download at www.lhotka.net/cslanet/download.aspx. Both VB and C# versions are available.
Please read the change log to get an idea of all the changes, and check out the various 3.5 related threads here on the forum as well.
At this point version 3.5 is feature complete - I'm not adding anything new.
There are probably some bugs, and any help you can provide in finding them and letting me know about them is VERY appreciated!!
Barring anyone finding some major hole in my implementation, the only changes going forward should be bug fixes and stability and performance enhancments. The public API for mainstream business object creation should be stable going forward. Advanced extensibility APIs may still be subject to change, though I hope not.
(For those working with the 3.5 preview releases, there's one big change you should know about up front. The property declaration syntax now requires you to use a RegisterProperty() method. Check the change log and/or ProjectTracker.Library to see the new syntax. I apologize for making such a change right before beta, but the 30%+ performance increase was too important...)
It’s great!
Can I build it with .Net 2.0?
Thanks
From: RockfordLhotka
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008
06:46
To: phucphlq@dsp.com.vn
Subject: [CSLA .NET] CSLA .NET 3.5
Beta 1 available
I have
made CSLA .NET version 3.5 Beta 1 online for download at www.lhotka.net/cslanet/download.aspx.
Both VB and C# versions are available.
Please
read the change log to get an idea of all the changes, and check out the
various 3.5 related threads here on the forum as well.
At this
point version 3.5 is feature complete - I'm not adding anything new.
There are
probably some bugs, and any help you can provide in finding them and letting me
know about them is VERY appreciated!!
Barring
anyone finding some major hole in my implementation, the only changes going
forward should be bug fixes and stability and performance enhancments. The
public API for mainstream business object creation should be stable going
forward. Advanced extensibility APIs may still be subject to change,
though I hope not.
(For
those working with the 3.5 preview releases, there's one big change you should
know about up front. The property declaration syntax now requires you to use a
RegisterProperty() method. Check the change log and/or ProjectTracker.Library
to see the new syntax. I apologize for making such a change right before beta,
but the 30%+ performance increase was too important...)
CSLA .NET is for .NET 3.5 only. I’m using new language
features that are only in the 3.5 C#/VB compilers.
CSLA .NET 3.0.x is the .NET 2.0/3.0 version of CSLA and will
remain so until it all fades away into the past. I’ll release 3.0.4 (bug
fixes) when I release 3.5, and will create other point releases if needed for
bug fixing, but the future is .NET 3.5.
Rocky
From: Pham Huu Le Quoc
Phuc [mailto:cslanet@lhotka.net]
Sent: Monday, January 28, 2008 7:07 PM
To: rocky@lhotka.net
Subject: RE: [CSLA .NET] CSLA .NET 3.5 Beta 1 available
It’s great!
Can I build it with .Net 2.0?
Thanks
From: RockfordLhotka
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 06:46
To: phucphlq@dsp.com.vn
Subject: [CSLA .NET] CSLA .NET 3.5 Beta 1 available
I have made CSLA .NET version 3.5 Beta 1 online for download at www.lhotka.net/cslanet/download.aspx.
Both VB and C# versions are available.
Please read the change log to get an idea of all the changes, and check out
the various 3.5 related threads here on the forum as well.
At this point version 3.5 is feature complete - I'm not adding anything new.
There are probably some bugs, and any help you can provide in finding them
and letting me know about them is VERY appreciated!!
Barring anyone finding some major hole in my implementation, the only
changes going forward should be bug fixes and stability and performance
enhancments. The public API for mainstream business object creation should be
stable going forward. Advanced extensibility APIs may still be subject to
change, though I hope not.
(For those working with the 3.5 preview releases, there's one big change you
should know about up front. The property declaration syntax now requires you to
use a RegisterProperty() method. Check the change log and/or
ProjectTracker.Library to see the new syntax. I apologize for making such a
change right before beta, but the 30%+ performance increase was too
important...)
Kind of a big change in there with the RegisterProperty at the last moment!
After having made the changes to my objects, I'm getting an interesting behavior.
For instance, with one property, it'll crash reliably upon trying to load it.
LoadProperty<
string>(_sortName, sdr.GetString(_sortName.DataColumnName));+ $exception {"Property load or set failed for property SortName (Unable to cast object of type 'System.Int32' to type 'System.String'.)"} System.Exception {Csla.PropertyLoadException}
But then, after re-trying the request, it breezes through it and works fine. But then gets caught up with another problem:
DataPortal.Fetch failed (Property load or set failed for property SystemId (Index was outside the bounds of the array.))
Again, re-trying the request I end up skipping through this error as well.
It's possible I may be running into some problems with a concern I had once I saw this change with respect to inheritance and properties that exist in generic abstract classes, but I'll know more once I look into it further. With the preview release, I was working fine with the properties. (We happen to use a derivative of PropertyInfo but it's really nothing).
I was not keen on making such a big change like that, but the
performance benefits are remarkable. There’s a very real cost to using this
managed field concept, and performance tuning is important – especially a
straight-up 30% improvement!
I could see where properties in abstract classes could be
problematic. You probably need to register them in the subclass, even though
the implementation is in the base class. Interesting problem…
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 9:48 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: CSLA .NET 3.5 Beta 1 available
Kind of a big change in there with the RegisterProperty at the last moment!
After having made the changes to my objects, I'm getting an interesting
behavior.
For instance, with one property, it'll crash reliably upon trying to load
it.
LoadProperty<string>(_sortName,
sdr.GetString(_sortName.DataColumnName));
+ $exception {"Property load or set failed for property
SortName (Unable to cast object of type 'System.Int32' to type
'System.String'.)"} System.Exception {Csla.PropertyLoadException}
But then, after re-trying the request, it breezes through it and works fine.
But then gets caught up with another problem:
DataPortal.Fetch failed (Property load or set failed for property SystemId
(Index was outside the bounds of the array.))
It's possible I may be running into some problems with a concern I had once
I saw this change with respect to inheritance and properties that exist in
generic abstract classes, but I'll know more once I look into it further. With
the preview release, I was working fine with the properties. (We happen to use
a derivative of PropertyInfo but it's really nothing).
Rocky: I could see where properties in abstract classes could be problematic. You probably need to register them in the subclass, even though the implementation is in the base class. Interesting problem…
That seems to me to be a bit of a problem... hopefully there's a way around this.
I’m open to suggestions J
The challenge of course, is that the list of properties for a
type is stored in a dictionary keyed by that type. Each PropertyInfo object has
an Index property that is set to a specific array location.
So actually registering in the subclass won’t necessarily help
either, as reuse of a PropertyInfo isn’t possible. If a PropertyInfo was used
in 2+ types the Index property would only be valid for one of them and would
mess things up in the other(s).
There’s no guarantee of the order in which static initialization
occurs either. In other words, the base class could be initialized after the subclass,
so I can’t necessarily just grab the list of PropertyInfo objects from the base
class type, because they might not yet be in the dictionary.
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 10:21 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: CSLA .NET 3.5 Beta 1 available
Rocky: I could see where properties in abstract classes could be
problematic. You probably need to register them in the subclass, even though
the implementation is in the base class. Interesting problem…
That seems to me to be a bit of a problem... hopefully there's a way
around this.
I think I have a solution – conceptually anyway.
Though you can’t guarantee the order in which static
initialization will occur, there’s no doubt that by the time the first instance
method is called all static initialization has taken place.
So what I can do is passively accumulate the PropertyInfo
objects into Type-indexed lists.
Then, when the first attempt is made to get/set a property I can
walk up the inheritance hierarchy and ensure the Index properties of all
PropertyInfo objects are set, with the top-level base class’s properties
starting at 0 and counting up, then the next subclass, and the next, all the
way out to yours.
What a cool algorithm!
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 10:21 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: CSLA .NET 3.5 Beta 1 available
Rocky: I could see where properties in abstract classes could be
problematic. You probably need to register them in the subclass, even though
the implementation is in the base class. Interesting problem…
That seems to me to be a bit of a problem... hopefully there's a way
around this.
I'm very interested to see how this might work!
I have three classes in the hierarchy I'm working with.
1) Entity<T>, which includes an Id and timestamp information for user creation and modification
2) ProfileName<T>, which encompasses organization and individual names.
3) IndividualName (which includes things such as first name, etc.)
What was interesting is that I was getting the registerproperty calls firing for #1 and #3, but the mFieldData was getting initialized then (excluding the two properties for #2) and then the register property calls for #2 would fire. Obviously this would explain why subsequent requests would work - the mFieldData got initialized properly with the entire list.
I really appreciate you taking this issue into consideration so quickly. If you have a solution you'd like me to try within my code, just tell me which files to grab from your source and I'll verify it solves the problem I was encountering. I'll watch this thread or my e-mail is chlusak@microedge.com.
Whenever you find the time, of course. As always, thanks!
Chris
p.s. I have a number of hierarchical cases like this - such as profiles including Individuals and Organizations, etc.
This is the reason for beta releases – I really appreciate
your feedback and help getting it all working!!
I’ll let you know when there’s something to test.
You might find it easiest to download TortoiseSVN and just grab the files
directly from my repository
http://www.lhotka.net/cslanet/Repository.aspx
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 11:24 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: RE: CSLA .NET 3.5 Beta 1 available
I'm very interested to see how this might work!
I have three classes in the hierarchy I'm working with.
1) Entity<T>, which includes an Id and timestamp information for user
creation and modification
2) ProfileName<T>, which encompasses organization and individual
names.
3) IndividualName (which includes things such as first name, etc.)
What was interesting is that I was getting the registerproperty calls firing
for #1 and #3, but the mFieldData was getting initialized then (excluding the
two properties for #2) and then the register property calls for #2 would fire.
Obviously this would explain why subsequent requests would work - the
mFieldData got initialized properly with the entire list.
I really appreciate you taking this issue into consideration so quickly. If
you have a solution you'd like me to try within my code, just tell me which
files to grab from your source and I'll verify it solves the problem I was
encountering. I'll watch this thread or my e-mail is chlusak@microedge.com.
Whenever you find the time, of course. As always, thanks!
Chris
I think the fix is in. Changes to files:
If you can see if this solves your issues that'd be awesome. I have a unit test with inheritance now, and it passes that unit test, so I think we're good.
Unfortunately there appear to still be issues (at least for me). It's hard to debug but I'm working through trying to understand it all. Hope to be able to provide more feedback for you soon.
Maybe I should be clear about what I made it support.
You can have a base class. The base class defines properties and
registers them all by itself. Those properties are registered against the base
class type of course:
public class Base<T> : BusinessBase<T> where T: Base<T>
{
private static PropertyInfo<string> NameProperty =
RegisterProperty<string>(typeof(Base<T>), new
PropertyInfo<string>(“Name”));
// …
}
The subclass registers its properties against its own type:
public class EndClass : Base<EndClass>
{
private static PropertyInfo<string> TitleProperty =
RegisterProperty<string>(typeof(EndClass), new
PropertyInfo<string>(“Title”));
// …
}
Then you use these:
EndClass x = new EndClass();
x.Name = “fred”;
x.Title = “CEO”;
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 2:09 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: RE: RE: CSLA .NET 3.5 Beta 1 available
Unfortunately there appear to still be issues (at least for me). It's hard
to debug but I'm working through trying to understand it all. Hope to be able
to provide more feedback for you soon.
Thank you, that's exactly the scenario I'm looking to accomplish. Hope to have some constructive feedback on what I'm experiencing once I have a grasp of exactly what that is :)
Rocky,
I’m experiencing some interesting behavior with the PropertyInfo objects’ Index property. Specifically, inside of the data portal, which has the exact same assemblies as outside of the data portal, the Index for each PropertyInfo of a business object is different. This has the ability to call throw an InvalidCastException (because a string property can’t be an int property) or worse yet, not exception at all.
I should probably mention that this is most likely due to the fact that I have a custom base class (which for lack of a better name is called BusinessBaseX) that has a single PropertyInfo object (which is common to my objects) being registered.
I read through the link you posted previously from the MSDN found here: http://msdn2.microsoft.com/en-us/library/aa645758(VS.71).aspx
If you look closely at the beginning of the article, it says that when the static fields are initialized they “are executed in the textual order in which they appear in the class declaration”. In other words, the only way that they could fire in a different order is by having different versions of an assembly on either side of the portal.
This being the case, I wonder if I might re-suggest a re-consideration of the methodology that I was using a few weeks ago before 3.5 Beta 1. This methodology was the following for the BusinessBase class.
// dictionary which maps the property to the index of an array
private static Dictionary<IPropertyInfo, int> _properties = new Dictionary< IPropertyInfo, int>();
// instance values of each property
private IFieldData[] _values;
I will keep you posted on more of the specifics of the problem as I debug as well as potential workarounds.
I very much doubt the issue is actually with the order of
properties being wrong within a specific type, because you’ll note that the
propertyinfo objects are sorted within a type. It doesn’t matter in what order
the propertyinfo fields are initialized, they end up in the same order
regardless. Unless I missed something of course J
I don’t trust that MSDN article’s assertion. I know for a fact
that C# and VB act differently in how fields/properties are managed in some
cases – VB sorts and C# does them in order (or maybe it was the other way around).
Don’t ask me why, and I don’t know that this difference includes static fields –
but order-of-initialization seems like a risky assumption to make in any case.
Hence the sorting, so there’s a guaranteed order.
My guess is that it is a different problem – specifically the
one I was fighting with the auth code, where you can’t predict the order in
which the types in your inheritance hierarchy will initialize. In fact, it is
quite possible for an intermediate type (one in the middle of the hierarchy) to
not initialize until very late in the process – after both the business
subclass and Csla.Core.BusinessBase have initialized. There appears to be no direct
solution to this problem that can be implemented purely within CSLA – the issue
is too low-level within .NET.
The solution I’ve come up with to the problem is somewhat of a
hack, and involves setting the value of a static field from within the
constructor (which solves the auth issue).
Of course deserialization doesn’t run constructors, so I’m
guessing this static field must be set within OnDeserialized() as well. That
may be true for the auth case as well (probably is now that I think about it).
If my theory is right, you can add code like this to your class:
[Serializable]
public abstract class BusinessBaseX : …
{
private static int _dummy;
private BusinessBaseX()
{
_dummy = 42;
}
protected override void OnDeserialized()
{
_dummy = 42;
}
}
That’ll force the initialization of the static fields in this
intermediate class during the deserialization process, forcing the propertyinfo
field(s) to be initialized on the app server during deserialization.
The trick is that _dummy must be declared in each specific
intermediate class, because static fields are initialized on a per-type basis.
There’s no way (short of maybe some reflection hacks – and even that would
require extra code in each intermediate class) for Csla.BusinessBase to ensure
that all static fields are initialized in the inheritance hierarchy. But this
should do the trick.
Rocky
Sure enough, having a _dummy static variable in the intermediate class took care of it. I had a dummy var in my most base class (which didn't have any PropertyInfo objects) and in my business object classes. I figured it was some .NET Framework quirk, like you said.
You may want to put this into your documentation because, per my understanding, it's still considered a CSLA best practice to have our own intermediate classes from which we inherit, rather than inheriting directly from BusinessBase.
Oh you can bet this’ll get discussed in the book J
Rocky
From: Bob Matthew
[mailto:cslanet@lhotka.net]
Sent: Wednesday, February 20, 2008 8:32 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: CSLA .NET 3.5 Beta 1 available
Sure enough, having a _dummy static variable in the intermediate class took
care of it. I had a dummy var in my most base class (which didn't have
any PropertyInfo objects) and in my business object classes. I
figured it was some .NET Framework quirk, like you said.
You may want to put this into your documentation because, per my
understanding, it's still considered a CSLA best practice to have our
own intermediate classes from which we inherit, rather than inheriting directly
from BusinessBase.
I know this is of very little help thus far but I'm still having perhaps some sort of a timing issue.
When GetConsolidatedList is getting run, it happens after 13 of 15 properties for IndividualName have been registered. 8 for the IndividualName class, 5 for Entity<T>, but not the 2 for ProfileName<T>. So, it ends up with an incomplete list.
Entity<T>(5)----ProfileName<T>(2)----IndividualName(8)
Why the five fire for Entity<T> but not the two for ProfileName<T>, you got me. I certainly haven't abandoned this and I'll give you whatever useful feedback I can.
The moment I changed my validation rules in ProfileName<T> to use the propertyinfo overloads, I started getting the registerproperties called for that class, but not Entity<T>'s register properties now. (i.e. it's registering 10 properties - 8 + 2). Don't ask me why that happened.
IndividualName ends up getting it's fieldmanager/propertylist of 10 items prior to the entity<T> class registering its properties now...so when trying to load the property of SystemId on a fetch (this is an entity<T> property) it croaks out and says:
[Csla.PropertyLoadException] = {"Property load or set failed for property SystemId (Index was outside the bounds of the array.)"}
FWIW, more information as I have it. (Clogging up your beta thread :))
No, this helps a lot.
It appears there are some odd things with static initialization
where it is possible for instance code to run before static fields have been
initialized. This flies in the face of my understanding, so I need to do some
research on this to see whether it is a .NET 3.5 compiler change or something…
But I can walk through the debugger and watch instance code
running before the static field initializers – in the same type!! Unreal!
So you are right, it is a timing issue.
One solution appears to be to put the RegisterProperty() calls
into a static constructor, because that still runs before any instance code
within a type. But that’s a pretty lame solution imo…
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 3:56 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 Beta 1 available
I know this is of very little help thus far but I'm still having perhaps
some sort of a timing issue.
When GetConsolidatedList is getting run, it happens after 13 of 15
properties for IndividualName have been registered. 8 for the IndividualName
class, 5 for Entity<T>, but not the 2 for ProfileName<T>. So, it
ends up with an incomplete list.
Entity<T>(5)----ProfileName<T>(2)----IndividualName(8)
Why the five fire for Entity<T> but not the two for
ProfileName<T>, you got me. I certainly haven't abandoned this and I'll
give you whatever useful feedback I can.
Not that i have had much of a chance to get deep down into .net 3.5 but if it is possible for instance code to run before static fields have been initialized this would be a big problem. Not only CSLA but for other things as well. There are plenty of situations that rely on the static fields to be initialized before instance code runs...
Rocky, you might have to put your hard hat on and have a talk to the guys on the hill if this is the case ;)
Well, if worse comes to worse, the static constructor seems to be an effective solution in my case.
Wow!
http://msdn2.microsoft.com/en-us/library/aa645758(VS.71).aspx
It turns out that static initializers are only guaranteed to run
before the first use of a static field. Instance code can run all it wants
before the static fields are initialized.
Another solution is to include an empty static constructor in
your base class. At least you don’t need to move the RegisterProperty()
calls there – just having an empty static ctor is enough for force the
behavior we need.
But implementing a static ctor forces the compiler to inject a
bunch of code around your class, which can be a perf issue (or so I’ve
read). So it is best to avoid static ctors if possible.
So here’s a solution I just tested that seems to work
(though it is clearly a hack). If the rule is that a static field must be used,
and we know that the instance ctors all run up the inheritance hierarchy, you
can have your instance ctors “touch” a static field in each base
class:
public class Base<T> …
{
private static int _dummy;
protected Base()
{
_dummy = 0;
}
}
As the instance ctors run in each class in your inheritance
hierarchy, you are guaranteed that this dummy field is set. That forces
initialization of all static fields in the class, thus ensuring that all the
base classes have initialized their static fields by the time your subclass’s
ctor runs, and thus before any chance of trying to get at the consolidated list
of registered properties.
Rocky
From: Rockford Lhotka
[mailto:rocky@lhotka.net]
Sent: Tuesday, January 29, 2008 4:26 PM
To: 'cslanet@lhotka.net'
Subject: RE: [CSLA .NET] CSLA .NET 3.5 Beta 1 available
No, this helps a lot.
It appears there are some odd things with static initialization
where it is possible for instance code to run before static fields have been
initialized. This flies in the face of my understanding, so I need to do some
research on this to see whether it is a .NET 3.5 compiler change or
something…
But I can walk through the debugger and watch instance code
running before the static field initializers – in the same type!! Unreal!
So you are right, it is a timing issue.
One solution appears to be to put the RegisterProperty() calls
into a static constructor, because that still runs before any instance code
within a type. But that’s a pretty lame solution imo…
Rocky
From: skagen00
[mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 3:56 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 Beta 1 available
I know this is of very little help thus far but I'm still having perhaps
some sort of a timing issue.
When GetConsolidatedList is getting run, it happens after 13 of 15
properties for IndividualName have been registered. 8 for the IndividualName
class, 5 for Entity<T>, but not the 2 for ProfileName<T>. So, it
ends up with an incomplete list.
Entity<T>(5)----ProfileName<T>(2)----IndividualName(8)
Why the five fire for Entity<T> but not the two for
ProfileName<T>, you got me. I certainly haven't abandoned this and I'll
give you whatever useful feedback I can.
I’m not sure I can shed a lot of light. You should find
Brad Abram’s blog post(s) on this topic to get the real scoop. The
details are complex and subtle.
Rocky
From: ajj3085
[mailto:cslanet@lhotka.net]
Sent: Wednesday, March 05, 2008 1:11 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: CSLA .NET 3.5 Beta 1 available
Rocky,
I was hoping you could shed some more light on this, because I hit a wierd
problem with static fields. I was trying to eliminate a static
constructor in my application, and I found something weird.
Having a field declared as such:
private static List<object> myList = new List<object>();
I would expect to hit the problem you describe. The only cavet is that
when I checked with Reflector.Net to ensure my static ctor was gone, I found
that the above produces this code:
private static List<Object> myList;
static MyClass() {
myList = new List<object>();
}
So is that actually what the compiler is doing, or is that how Reflector is
interperating it? I'm confused now, because based on your comments above
I wouldn't expect the code genereated above. So something is fishy.
Thanks
Andy
You tell me…
Clearly this stuff is complex, and there may be differences
between .NET 1, 2 and 3?
I know there is a perf consequence to having a static constructor
(cctor) in a class, and so they should be avoided.
It would appear then, that you can force initialization in two
ways:
1.
Implement a cctor and pay the perf cost
2.
Somehow set a static field to a value
The _dummy field is an attempt (that works) to go with option
#2.
Of course this doesn’t appear to match with what reflector
is showing (Andy was it?), so that’s confusing – and I don’t
have an answer for that…
Rocky
From: Skafa
[mailto:cslanet@lhotka.net]
Sent: Thursday, March 06, 2008 4:24 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: CSLA .NET 3.5 Beta 1 available
Rocky,
The link you gave us (http://msdn2.microsoft.com/en-us/library/aa645758(VS.71).aspx),
says:
If a static constructor exists in the class, execution of
the static field initializers occurs immediately prior to executing that static
constructor. Otherwise, the static field initializers are executed at an
implementation-dependent time prior to the first use of a static field of that
class.
and gives an example with an empty static constructor..
so... what's with that _dummy field ;-)
I don’t want to declare and initialize propertyinfo in
different locations. Or at least I don’t want to force it to be done that
way. The whole point of this was/is to reduce and simplify code.
Rocky
I personally like the single line declaration of managed properties partially because of the late cycle - we started architecting around the last preview and have altered course due to a change prior to beta 1. While I'm not thrilled with the hack it's really a pretty inconsequential thing - it's only being required on cases where inheritence is being used.
The declaration we end up using is pretty streamlined - we leverage a method in our base class to accomodate passing in the type - after all it's just T.
private static CompanyPropertyInfo<string> _profileName = RegisterCompanyProperty<string>("ProfileName", "Profile Name");
CompanyPropertyInfo isn't the actual name of our property info but it extends the base property info and provides some additional behavior. To me, the above line is very simple and the simple inclusion of the hack for my abstract base classes, I can deal with that.
So I would throw a vote in for not changing the current approach, with all due respect to Andres.
Chris
xal:I finally found it!
Another way to solve it may be to use the pattern in the rules manager: have an overridable method like.
That would probably work.
However, I think it is important to be relatively consistent with Microsoft. Microsoft has defined the dependency property concept for WPF and WF, and I modeled this approach after what they did. For better or worse, they require passing in the type of the container.
You should be aware that nothing forces you to call RegisterProperty() on the field declaration. If you want to call it in some other location you surely could. Other locations might include a static constructor, or in the Initialize() method (though you'd need to make sure to only do the initialization once per AppDomain. Something like:
public class Foo : BusinessBase<Foo>
{
private static bool _propertiesLoaded;
protected override void Initialize()
{
base.Initialize();
if (!_propertiesLoaded)
{
_propertiesLoaded = true;
// register properties here
}
}
}
Of course the reality is that setting _propertiesLoaded would trigger all the RegisterProperty() calls on static field declarations too
Skafa is exactly right.
In an inheritance scenario, your end subclass (the actual business type) gets initialized because it is the type being created, and so the code almost certainly interacts with one or more static fields in that class.
And the CSLA base classes (Core.BusinessBase in particular) gets initialized because it interacts with its own static fields.
But no code interacts with any static fields in any classes between Core.BusinessBase and your end business class. So there's no guaranteed trigger that would cause those static fields to initialize prior to instance code getting to run.
Nor can CSLA really automate that process without some expensive use of reflection. The only thing I can think of would be to reflect against every class in the inheritance hierarchy and try to arbitrarily set the first static field I find in each class. That'd be both expensive and VERY error-prone (probably would break many apps).
You can force it by implementing either a static constructor, or using the _dummy field technique. Either one will force the static fields to initialize in that class.
Xal is right, I could implement a dual solution - what we have now (which I like) and something more like validation rules. That's double the effort in testing, maintenance and support for me.
I'm already nervous about the explosion in options for doing things - I feel like Microsoft... Look at all the ways to declare a property now - and I need to support, maintain and regression test all of them each time I change something in the framework. A couple more expansion changes like that and I'll be unable to keep up.
And ultimately there's my schedule. I'm so booked between now and mid-May that doing something like Xal proposes would probably have to wait until then. And it is a big change, so it would require a Beta 3. So then 3.5 wouldn't come out until mid-June. That will make for some unhappy people I'm sure.
Additionally, I am starting the 2008 book, and this would delay that too - which would push the book beyond the realistic publication date, so the book would be canceled.
In short, I'm not making any changes of this magnitude to solve an edge case that already has a couple solutions - not when it means a multi-month delay in release and cancelation of the 2008 book (remember - that's how I fund all this work - no book = no sales = no funding = bad!).
Rocky,
One thing we've noticed with the new ContextManager class is the "new()" requirement for the template parameter.
We have found that a DataContext need not have a public, parameterless constructor, which is contrary to the "new()" condition of the ContextManager. Specifically, when you create a new Linq To Sql class and then drag some tables/stored procs, etc to the designer, you can then set the Connection property under the Properties section to "(None)". This removes the default, parameterless constructor from the DataContext.
It would seem that stipulating a DataContext as a template condition, which you already do, would suffice.
On a different note, on the "nice to have" side of things, it would be great to have a GetManager overload that specified the "name" of the connection to retrieve from the configuration, rather than the connection string itself.
A simple overload like this would perhaps serve the purpose:
GetManager(string connectionString, bool isConnectionName);
The function would then "resolve" the provide string from a name to the actual connection string and then provide that to your existing GetManager function.
Other than that, CSLA 3.5 is working great!
Hey Rocky if you have not already you may want to take a look at this thread in regards to a possible feature that would be of use and a possible problem with the change that was made to the loading of type auth rules in regards to inheritance. Note i have not tested to see if it is an issue but thinking it through it possible could be.
Anthony
Copyright (c) Marimer LLC