You can create your own custom collections that handle sorting (lots of code out there to do that, even a recent article in MSDN I mentioned on my blog recently), and then use GetCollection to get that custom collection type. Note that I choose not to do this
for you for a couple of reaons: (1) server-side paging (which I do implement) requires server-side sorting, and (2) my goal is to handle the persistence and let you create your objects/collections.
Thanks, Paul Wilson, ASPInsider, MC**
For the best .NET code, examples, and tools, visit:
WilsonDotNet.com, WilsonWebPortal.com, ORMapper.net
Even easier than custom collections is to implement IComparable in your class and you can then sort your classes in an ArrayList or other sortable collection type. Here is an example of how I go about doing it:
------------------------------------------------------------------------------------------------
'Implement the IComparable code in you business class:
Public Shared Function CreateCompanyComparer() As CompanyComparer
Return New CompanyComparer
End Function
Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
If (TypeOf obj Is Company) Then
Dim comp As Company = DirectCast(obj, Company)
Return Me.CompanyId.CompareTo(comp.CompanyId)
Else
Throw New ArgumentException("Object is not a Company!")
End If
End Function
Public Function CompareTo(ByVal comp As Company, ByVal ct As CompanyComparer.ComparisonType) As Integer
Select Case ct
Case CompanyComparer.ComparisonType.CompanyId : Return Me.CompanyId.CompareTo(comp.CompanyId)
Case CompanyComparer.ComparisonType.AddressName : Return Me.AddressName.CompareTo(comp.AddressName)
Case CompanyComparer.ComparisonType.Name : Return Me.Name.CompareTo(comp.Name)
Case Else : Return 0
End Select
End Function
-------------------------------------------------------------------------------------
This then gives you the ability to sort by any property that you add to the switch/select in the CompareTo method. All you have to do is create the comparer and pass it to the sort method of the collection class. In this example I get the company-group Id from
a list box on an aspx page and use it to fetch all of the companies in the group (note that Data.Manager.GetObjectList is a wrapper that I have created for WORM's ObjectSpace.GetCollection):
-------------------------------------------------------------------------------------
Dim companies As ArrayList
Dim cc As Bll.CompanyComparer = Bll.Company.CreateCompanyComparer()
If (Not Me.lstGroups.SelectedValue = "") Then
cc.ComparisonField = Bll.CompanyComparer.ComparisonType.Name
companies = Data.Manager.GetObjectList(GetType(Bll.Company), "GroupId=" & Me.lstGroups.SelectedValue)
companies.Sort(cc)
End If
-------------------------------------------------------------------------------------
And here's the comparer code:
-------------------------------------------------------------------------------------
Public Class CompanyComparer : Implements IComparer
Private _comparisonField As CompanyComparer.ComparisonType
Public Property ComparisonField() As CompanyComparer.ComparisonType
Get
Return Me._comparisonField
End Get
Set(ByVal Value As CompanyComparer.ComparisonType)
Me._comparisonField = Value
End Set
End Property
Public Enum ComparisonType
CompanyId
Name
AddressName
End Enum
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
Dim f As Company = DirectCast(x, Company)
Dim s As Company = DirectCast(y, Company)
Return f.CompareTo(s, ComparisonField)
End Function
End Class
---------------------------------------------------------------------------------------
Hope this helps.
That's pretty neat. What caught my attention, though, was the mapper wrapper. I have been working to create a persistence layer that hides the ormapper, so the mapper can be substituted or vary without affecting the application. An "acceptable" solution is
to create specific persistence interfaces for each type (ICustomerDAC, IOrderDAC, IProductDAC, etc) and use a central factory to return that which is implemented using WORM, NHibernate, hand-written ADO.NET, or whatever. But I am eager to use something more
generic that takes a class as a parameter, which is a closer interface to a good ormapper. So tell me, what does your "Data.Manager" interface look like and could it be implemented using something other than WORM (such as NHibernate, EB, ????) easily?
Question to OO gurus: In this context, how much should the design of a Facade be influenced byt the features/capabilities of the underlying systems?
I have been working to create a persistence layer that hides the ormapper, so the mapper can be substituted or vary without affecting the application.
As an aside, this is really hard to do correctly and completely and in most cases is not worth the effort. There are several threads that discuss this topic but some highlights are:
1) Usually it is pretty unlikely that you'll need to switch mappers (assuming you do your homework and pick a capable one). So, you can spend a lot of time upfront trying to abstract the mapper completely or you can allot this time as the time spent on removing
the existing mapper and implementing the new mapper. By doing #2 you'll be alot more efficient in the best/average case and slightly less efficient in the worst case.
2) As stated, it is very hard to totally abstract the mapper. The reason being that mapper specific details often play into the design/architecture of the system and different mappers usually approach different problems in different ways. Ignoring these mapper
specific features because you can't support them across all mappers (and thus your abstraction layer) means that you won't be using features that are often extremely important and useful. Thona has stated that he views the O/R Mapper as more of a BLL runtime
that happens to contain a DAL than the actual DAL itself. I think this idea is a very interesting point of view that lends some perspective to the idea of abstracting an O/R Mapper.
This was a little off topic for this post, but at one time I was very interested in doing what you are discussing. However, from my experience it is really just not worth it...
An "acceptable" solution is to create specific persistence interfaces for each type (ICustomerDAC, IOrderDAC, IProductDAC, etc) and use a central factory to return that which is implemented using WORM, NHibernate, hand-written ADO.NET,
or whatever.
And how are you going to expose all the nice features that the user interface / user of objects determines? Prefetch paths, cache hints, just to name only some? Interaces throw away 90% of the "higher" functionality of capable O/R mappers.
In this context, how much should the design of a Facade be influenced byt the features/capabilities of the underlying systems?
Let me ask you the same question, worded different: Are you willing to throw away all the features & capabilities of highly optimized subsystems just to have an easy facade?
Hint: the features are there for a reason. In our PowerNodes CMS, some of the more arcane EntityBroker features make the difference between a 90 second and a 1 second network round trip. Just by providing proper hints to the query what data is being loaded
further down, or by submitting multiple queries in one round trip. A simplistic facade would not use these advanced features and make the whole power of the underlying DAL pretty much useless.
Now, if you deal with a DAL that basically is as primitve as tehy can get (where I sadly do start, in certain ascpects, putting ALL your mentioned technologies in, at least when talking about remoted scenarios), then you may find a sensible common ground.
On the subject of Paul Wilson's ORM (which I STILL haven't had a chance to take a decent look at because of deadlines at work) do you find yourself having to make very many OOP compromises? One thing I feel in CSLA is that I'm constantly having to have a less
than OOP design for various reasons (lack of inheritance support, the objects build their own queries, etc.)
However, in a more direct way, regarding how much the Facade be influenced by the unerlying system, in the ideal world one would say "not at all". However, that's pretty unrealistic.
One could hand-code a pretty significant mapper-mapper that would provide a plug-in socket for ORMappers. I bet it would have to take steps to "support" one or another ORMapper; but, it could not, out-of-the-box, support every ORMapper. That is, it could be
built saying "this mapper-mapper supports ORMapperA and ORMapperB and ORMapperC".
However, trying to say "this mapper-mapper supports ANY of the many available ORMappers" would be rather difficult and probably highly impractical. The reason for this is that ORMappers solve the same problem but they solve it in different ways.
One could however, theoretically, do an extensive analysis, make a giant correlation matrix all of the exposed functionality in every ORMapper, make a project that handles these dynamically, and then layer the parts as needed.
However, if one were to do that, would it be practical? Would it save time? Would it be stable? Would it be maintainable?
That sounds like a tough job and probably not worth the trouble.
On the subject of Paul Wilson's ORM (which I STILL haven't had a chance to take a decent look at because of deadlines at work) do you find yourself having to make very many OOP compromises? One thing I feel in CSLA is that I'm constantly having to have a less
than OOP design for various reasons (lack of inheritance support, the objects build their own queries, etc.)
Thanks,
Craig
Its so nice being in a position for R&D and never having the time to do it, isn't it. :P
Anyway, on to your question, my answer is no! Not in any way unless the application design calls for it (sometimes it is just more efficient to use direct SQL, in massive data processing routines etc for example). It seems to me that WORM has in many wasy been
designed specifically to avoid compromising the OO nature of the apps that it is used in.
On the subject of Paul Wilson's ORM... do you find yourself having to make very many OOP compromises? ...in CSLA is that I'm constantly having to have a less than OOP design for various reasons (lack of inheritance support, the objects build their own queries,
etc.)
...my team an I have had no trouble with WilsonORMapper. In fact, it is saving a lot of time. As I said in a post above, it is my firm belief that when one decides to use any kind of CASE tool, (and I mean CASE tool in ANY sense-- such as WilsonORMapper, Rose,
Sparx Enterprise Architect, Visio for EA, LLBLGen, MIA Generation, CodeSmith, MyGeneration, Constructor() MDA tool, and so on), then one must be willing to work with the tool, not against it. The tool has a job to do and one must let the tool do its job. If
one needs 100% control over every letter in every line of code in every class, then the answer is simple-- one should code by hand and one should NOT use a CASE tool. On the other hand, if one wants to use a tool to help generate and maintain some subset of
the codebase, then one should use a CASE tool.
Regarding "lack of inheritance support", there is support for embedded sub-types, keyed by a discriminator column; but, this is single-table based. At our shop, we strongly favor composition and interfaces over inheritance; so, building the objects that we
want is not really an issue. That said, if one knows CodeSmith and wants to tweak the code-generation templates for WilsonORMapper, then it is possible to create classes that inherit from whatever base class you want. The base class would need to be hand-coded.
Complicated inheritance trees, however, would need some extra work.
Regarding the issue that "objects build their own queries", I say that is GREAT. I simply love it. If I never write another line of SQL code it will be fine with me. However, I mention that as an illustration because that is only MY style. Such choices are
often a matter of architectural perspective and style. I will not start the SPs vs DynamicSql debate here; but, I will say that I once was very much pro-SP and now I am anti-SP, (thanks to Frans Bouma and other promoters of dynamic SQL). That's a long story;
but, it is a major consideration that one should carefully mull before choosing an ORMapper. In short, make sure the ORMapper that you choose will support the SP architechture that you need. I understand that the WilsonORMapper does support SPs; but, I have
never used that feature. I use the dynamic-SQL and the Object Query support in WilsonORMapper and I haven't regretted it at all.
I am comfortable making the following statement-- If one uses an ORMapper in such a way that one does not take advantage of the dynamic-query functionality of that ORMapper, then one will thereby limit the overall capabilities of the ORMapper to a significant
extent.
I actually don't use inheritance very much, but in places where an obvious hierarchy and code reuse benefits exist, it really hits the nail in the head.
As far as "objects building their own queries" -- I think you misunderstood what I was talking about. In CSLA, the business objects do not use a seperate layer: the busines object does its own sql (and that of its children) internally. There is no repository
you go to generate (or create your own) dynamic sql -- rather the business classes have static methods, where one "root" object gets all of its children, grand children, etc. i've modified the framework a bit to better support lazy loading and seperating
out the sql specific stuff from teh business objects, but they are still tightly intervoven. One of the things that attracts me to Paul's work is that you can ask the repository for the objects in a variety of different ways, rather than embedding all that
data access logic in the object hierarchy itself. The business objects seem blissfuly unaware that they are persisted at all, keeping the two concerns a lot more seperate. Also, the ability to not have to touch sql (for most things) as you mentioned.
PaulWilson
Contributor
3715 Points
745 Posts
ASPInsiders
Re: WORM, NHibernate and reviews of any other ORMappers
Jul 29, 2005 06:39 PM|LINK
For the best .NET code, examples, and tools, visit:
WilsonDotNet.com, WilsonWebPortal.com, ORMapper.net
sroughley
Participant
1404 Points
381 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 01, 2005 08:51 AM|LINK
Even easier than custom collections is to implement IComparable in your class and you can then sort your classes in an ArrayList or other sortable collection type. Here is an example of how I go about doing it:
------------------------------------------------------------------------------------------------
'Implement the IComparable code in you business class:
Public Shared Function CreateCompanyComparer() As CompanyComparer
Return New CompanyComparer
End Function
Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
If (TypeOf obj Is Company) Then
Dim comp As Company = DirectCast(obj, Company)
Return Me.CompanyId.CompareTo(comp.CompanyId)
Else
Throw New ArgumentException("Object is not a Company!")
End If
End Function
Public Function CompareTo(ByVal comp As Company, ByVal ct As CompanyComparer.ComparisonType) As Integer
Select Case ct
Case CompanyComparer.ComparisonType.CompanyId : Return Me.CompanyId.CompareTo(comp.CompanyId)
Case CompanyComparer.ComparisonType.AddressName : Return Me.AddressName.CompareTo(comp.AddressName)
Case CompanyComparer.ComparisonType.Name : Return Me.Name.CompareTo(comp.Name)
Case Else : Return 0
End Select
End Function
-------------------------------------------------------------------------------------
This then gives you the ability to sort by any property that you add to the switch/select in the CompareTo method. All you have to do is create the comparer and pass it to the sort method of the collection class. In this example I get the company-group Id from a list box on an aspx page and use it to fetch all of the companies in the group (note that Data.Manager.GetObjectList is a wrapper that I have created for WORM's ObjectSpace.GetCollection):
-------------------------------------------------------------------------------------
Dim companies As ArrayList
Dim cc As Bll.CompanyComparer = Bll.Company.CreateCompanyComparer()
If (Not Me.lstGroups.SelectedValue = "") Then
cc.ComparisonField = Bll.CompanyComparer.ComparisonType.Name
companies = Data.Manager.GetObjectList(GetType(Bll.Company), "GroupId=" & Me.lstGroups.SelectedValue)
companies.Sort(cc)
End If
-------------------------------------------------------------------------------------
And here's the comparer code:
-------------------------------------------------------------------------------------
Public Class CompanyComparer : Implements IComparer
Private _comparisonField As CompanyComparer.ComparisonType
Public Property ComparisonField() As CompanyComparer.ComparisonType
Get
Return Me._comparisonField
End Get
Set(ByVal Value As CompanyComparer.ComparisonType)
Me._comparisonField = Value
End Set
End Property
Public Enum ComparisonType
CompanyId
Name
AddressName
End Enum
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
Dim f As Company = DirectCast(x, Company)
Dim s As Company = DirectCast(y, Company)
Return f.CompareTo(s, ComparisonField)
End Function
End Class
---------------------------------------------------------------------------------------
Hope this helps.
Stephen.
ALFKI
Member
448 Points
98 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 01, 2005 03:10 PM|LINK
That's pretty neat. What caught my attention, though, was the mapper wrapper. I have been working to create a persistence layer that hides the ormapper, so the mapper can be substituted or vary without affecting the application. An "acceptable" solution is to create specific persistence interfaces for each type (ICustomerDAC, IOrderDAC, IProductDAC, etc) and use a central factory to return that which is implemented using WORM, NHibernate, hand-written ADO.NET, or whatever. But I am eager to use something more generic that takes a class as a parameter, which is a closer interface to a good ormapper. So tell me, what does your "Data.Manager" interface look like and could it be implemented using something other than WORM (such as NHibernate, EB, ????) easily?
Question to OO gurus: In this context, how much should the design of a Facade be influenced byt the features/capabilities of the underlying systems?
rsmoke21
Contributor
3931 Points
792 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 01, 2005 04:52 PM|LINK
As an aside, this is really hard to do correctly and completely and in most cases is not worth the effort. There are several threads that discuss this topic but some highlights are:
1) Usually it is pretty unlikely that you'll need to switch mappers (assuming you do your homework and pick a capable one). So, you can spend a lot of time upfront trying to abstract the mapper completely or you can allot this time as the time spent on removing the existing mapper and implementing the new mapper. By doing #2 you'll be alot more efficient in the best/average case and slightly less efficient in the worst case.
2) As stated, it is very hard to totally abstract the mapper. The reason being that mapper specific details often play into the design/architecture of the system and different mappers usually approach different problems in different ways. Ignoring these mapper specific features because you can't support them across all mappers (and thus your abstraction layer) means that you won't be using features that are often extremely important and useful. Thona has stated that he views the O/R Mapper as more of a BLL runtime that happens to contain a DAL than the actual DAL itself. I think this idea is a very interesting point of view that lends some perspective to the idea of abstracting an O/R Mapper.
This was a little off topic for this post, but at one time I was very interested in doing what you are discussing. However, from my experience it is really just not worth it...
thona
Member
20 Points
2923 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 01, 2005 04:59 PM|LINK
And how are you going to expose all the nice features that the user interface / user of objects determines? Prefetch paths, cache hints, just to name only some? Interaces throw away 90% of the "higher" functionality of capable O/R mappers.
In this context, how much should the design of a Facade be influenced byt the features/capabilities of the underlying systems?
Let me ask you the same question, worded different: Are you willing to throw away all the features & capabilities of highly optimized subsystems just to have an easy facade?
Hint: the features are there for a reason. In our PowerNodes CMS, some of the more arcane EntityBroker features make the difference between a 90 second and a 1 second network round trip. Just by providing proper hints to the query what data is being loaded further down, or by submitting multiple queries in one round trip. A simplistic facade would not use these advanced features and make the whole power of the underlying DAL pretty much useless.
Now, if you deal with a DAL that basically is as primitve as tehy can get (where I sadly do start, in certain ascpects, putting ALL your mentioned technologies in, at least when talking about remoted scenarios), then you may find a sensible common ground.
fregas
Member
406 Points
96 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 01, 2005 06:37 PM|LINK
On the subject of Paul Wilson's ORM (which I STILL haven't had a chance to take a decent look at because of deadlines at work) do you find yourself having to make very many OOP compromises? One thing I feel in CSLA is that I'm constantly having to have a less than OOP design for various reasons (lack of inheritance support, the objects build their own queries, etc.)
Thanks,
Craig
mkamoski
Contributor
5694 Points
1565 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 01, 2005 06:49 PM|LINK
Regarding this...
...I will first say that non-gurus, like myself, are usually going to just answer anyway with opinion and thoughts. I hope you don't mind.
:-)
That said, it sound like you need a mapper for your mapper.
Martin Fowler talks about this here... http://www.martinfowler.com/eaaCatalog/mapper.html
However, in a more direct way, regarding how much the Facade be influenced by the unerlying system, in the ideal world one would say "not at all". However, that's pretty unrealistic.
One could hand-code a pretty significant mapper-mapper that would provide a plug-in socket for ORMappers. I bet it would have to take steps to "support" one or another ORMapper; but, it could not, out-of-the-box, support every ORMapper. That is, it could be built saying "this mapper-mapper supports ORMapperA and ORMapperB and ORMapperC".
However, trying to say "this mapper-mapper supports ANY of the many available ORMappers" would be rather difficult and probably highly impractical. The reason for this is that ORMappers solve the same problem but they solve it in different ways.
One could however, theoretically, do an extensive analysis, make a giant correlation matrix all of the exposed functionality in every ORMapper, make a project that handles these dynamically, and then layer the parts as needed.
However, if one were to do that, would it be practical? Would it save time? Would it be stable? Would it be maintainable?
That sounds like a tough job and probably not worth the trouble.
IMHO.
--Mark Kamoski
sroughley
Participant
1404 Points
381 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 02, 2005 09:16 AM|LINK
Its so nice being in a position for R&D and never having the time to do it, isn't it. :P
Anyway, on to your question, my answer is no! Not in any way unless the application design calls for it (sometimes it is just more efficient to use direct SQL, in massive data processing routines etc for example). It seems to me that WORM has in many wasy been designed specifically to avoid compromising the OO nature of the apps that it is used in.
mkamoski
Contributor
5694 Points
1565 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 02, 2005 01:42 PM|LINK
Regarding this...
...my team an I have had no trouble with WilsonORMapper. In fact, it is saving a lot of time. As I said in a post above, it is my firm belief that when one decides to use any kind of CASE tool, (and I mean CASE tool in ANY sense-- such as WilsonORMapper, Rose, Sparx Enterprise Architect, Visio for EA, LLBLGen, MIA Generation, CodeSmith, MyGeneration, Constructor() MDA tool, and so on), then one must be willing to work with the tool, not against it. The tool has a job to do and one must let the tool do its job. If one needs 100% control over every letter in every line of code in every class, then the answer is simple-- one should code by hand and one should NOT use a CASE tool. On the other hand, if one wants to use a tool to help generate and maintain some subset of the codebase, then one should use a CASE tool.
Regarding "lack of inheritance support", there is support for embedded sub-types, keyed by a discriminator column; but, this is single-table based. At our shop, we strongly favor composition and interfaces over inheritance; so, building the objects that we want is not really an issue. That said, if one knows CodeSmith and wants to tweak the code-generation templates for WilsonORMapper, then it is possible to create classes that inherit from whatever base class you want. The base class would need to be hand-coded. Complicated inheritance trees, however, would need some extra work.
Regarding the issue that "objects build their own queries", I say that is GREAT. I simply love it. If I never write another line of SQL code it will be fine with me. However, I mention that as an illustration because that is only MY style. Such choices are often a matter of architectural perspective and style. I will not start the SPs vs DynamicSql debate here; but, I will say that I once was very much pro-SP and now I am anti-SP, (thanks to Frans Bouma and other promoters of dynamic SQL). That's a long story; but, it is a major consideration that one should carefully mull before choosing an ORMapper. In short, make sure the ORMapper that you choose will support the SP architechture that you need. I understand that the WilsonORMapper does support SPs; but, I have never used that feature. I use the dynamic-SQL and the Object Query support in WilsonORMapper and I haven't regretted it at all.
I am comfortable making the following statement-- If one uses an ORMapper in such a way that one does not take advantage of the dynamic-query functionality of that ORMapper, then one will thereby limit the overall capabilities of the ORMapper to a significant extent.
That's all for now.
HTH.
Thank you.
--Mark Kamoski
fregas
Member
406 Points
96 Posts
Re: WORM, NHibernate and reviews of any other ORMappers
Aug 02, 2005 07:35 PM|LINK
As far as "objects building their own queries" -- I think you misunderstood what I was talking about. In CSLA, the business objects do not use a seperate layer: the busines object does its own sql (and that of its children) internally. There is no repository you go to generate (or create your own) dynamic sql -- rather the business classes have static methods, where one "root" object gets all of its children, grand children, etc. i've modified the framework a bit to better support lazy loading and seperating out the sql specific stuff from teh business objects, but they are still tightly intervoven. One of the things that attracts me to Paul's work is that you can ask the repository for the objects in a variety of different ways, rather than embedding all that data access logic in the object hierarchy itself. The business objects seem blissfuly unaware that they are persisted at all, keeping the two concerns a lot more seperate. Also, the ability to not have to touch sql (for most things) as you mentioned.