Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do any .NET ORMs use constructors "properly"?

Tags:

.net

orm

This is related conceptually to my question here. However, I've been playing around with NHibernate, and realized what the real core of my question is.

In classic OO design, to properly encapsulate data, it's a common pattern to have values passed in to an object's constructor that are stored in data members (fields). Those values which should not be changed are exposed with only accessors (read-only properties). Those which it is permissible to change have both accessors and mutators (read-write properties). It seems to me that a proper O/RM should respect these conventions, and use the available constructors when creating an object. Relying on read-write properties, reflection, or other, hackish (IMHO) methods seems...wrong.

Is there a .NET O/RM solution out there that does this?

EDIT

To address Praveen's point, I know that there are projects that have a 'default' algorithm for choosing a constructor - StructureMap, for example, always uses the constructor with the most arguments, unless you mark a constructor with a custom attribute. I can see this being an effective way handle the situation. Perhaps utilizing an IoC container in addition to the ORM would provide the sort of solution I'm needing - though it seems to me that this is, while not inherently bad, an unnecessary additional step for using an ORM.

like image 698
Harper Shelby Avatar asked May 29 '09 15:05

Harper Shelby


4 Answers

I think most of ORMs actually support this concept, at least DataObject.Net does. This code works as it expected:

[HierarchyRoot(typeof(KeyGenerator), "Id")]
public class Message : Entity
{
  [Field]
  public int Id { get; private set; }

  [Field(Length = 100)]
  public string Text { get; private set; }

  public Message(string Text)
  {
    Text = text;
  }
}

EDIT: DataObjects store data in internal transactional state, and use special materialization constructor, generated by PostSharp. Of course it is not so trivial if ORM use poco objects.

like image 158
Alex Kofman Avatar answered Sep 20 '22 17:09

Alex Kofman


Unfortunately this is impossible in .NET without you marking up the constructors in some way.

The method signature stored for each constructor in the assembly metadata only contains the type of each parameter to the constructor. There's no way for ANY .NET ORM to really know which constructor to use. All the ORM sees is something like this:

.ctor()
.ctor(string, string)
.ctor(string, string, string)

There's no way for the ORM to know which .ctor parameter corresponds to the FirstName, LastName and MiddleName for your Customer object for instance.

In order to give you this support, a .NET ORM must support reading in custom attributes that you define for each parameter. You'd need to markup your constructors like this:

public Customer([Property("FirstName")] string FirstName, [Property("LastName")] string LastName, [Property("MiddleName")] string MiddleName)

This has 2 disadvantages:

  1. There is no way (that I can think of, someone will probably correct me), that this can go into a mapping file.
  2. You'd still need to write the same mapping as you've always done, because the ORM still needs to be able to get individual values for each property.

So you'd need to do all this extra work marking up the constructors and at the same time, you'd still need to map your classes EXACTLY as you were doing before.

like image 26
Praveen Angyan Avatar answered Sep 21 '22 17:09

Praveen Angyan


Why do you think this feels wrong ? Do you want your OR/M to perform business logic when reconstituting an object ? IMHO, no.

When you load an object from the DB, then the OR/M should be able to reconstitute the object, no matter what. Setting some value while reconsituting, should not lead to trigger some kind of logic that changes another value (that maybe should be given a value by the ORM as well ... )

Even so, I think a constructor should only contain parameters for those fields that are mandatory at object-creation time to have the object in a 'valid' state. Next to that, you might have public read-only properties that have been given a value by some calculation, and that need to be persisted as well.
If you feel reflection is a 'smell' to reconstitute objects, how are you going to deal with this situation ? You'll have to somehow create a public method which would be able to set that 'read only' value, but this breaks encapsulation, and you don't wan't that either.

like image 34
Frederik Gheysels Avatar answered Sep 21 '22 17:09

Frederik Gheysels


I agree with one of the previous posters that constructors should be for creating an object at the beginning of its lifetime. Using a constructor to hydrate an existing object that is currently in an archived state is counter-intuitive at best.

What needs to be considered, and what all major ORMs seem to be missing at this point, is that re-constituting a domain object from its last known state, as stored in a database or any other data store, is NOT inherently a constructing or modifying operation; so neither constructors or property setters should be used. Rather, the framework mechanism that most closely corresponds to this kind of operation is serialization! There are already recognized patterns for storing an object's state and then later reconstituting it through the ISerializable interface, the standard serialization constructor, etc. Persisting an object to a database is fundamentally no different from persisting it to a stream; in fact, one of the values of the StreamingContextStates enumeration that is used during serialization is Persistence!. IMHO, this should be the standard approach when designing any persistence mechanism. Unfortunately, I don't know of any ORM that supports this out of the box.

It should also be noted that an object that has been designed for serialization is still POCO; persistence ignorance has NOT been violated. The key point here is that a domain object should know best what data is needed to persist and restore it, as well as in what order that data should be restored (which often matters). What the object should NOT have to know is the specific mechanism by which it will be stored: relational database, flat file, binary blob, etc.

like image 39
David Nelson Avatar answered Sep 21 '22 17:09

David Nelson