The mapping
Again the same mapping:- <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
- <class entity-name="ProductLine">
- <id name="Id" type="int">
- <generator class="hilo"/>
- </id>
- <property name="Description" not-null="true" length="200" type="string"/>
- <bag name="Models" cascade="all" inverse="true">
- <key column="productId"/>
- <one-to-many class="Model"/>
- </bag>
- </class>
- <class entity-name="Model">
- <id name="Id" type="int">
- <generator class="hilo"/>
- </id>
- <property name="Name" not-null="true" length="25" type="string"/>
- <property name="Description" not-null="true" length="200" type="string"/>
- <many-to-one name="ProductLine"
- column="productId"
- not-null="true"
- class="ProductLine"/>
- </class>
- </hibernate-mapping>
Saving Ducks
Instead anonymous objects this time I’ll use a DynamicEntity.- using (ISession s = sessions.OpenSession())
- using (ITransaction t = s.BeginTransaction())
- {
- dynamic line = new DynamicEntity();
- line.Description = "High quality cars";
- line.Models = new ArrayList();
- line.Pizza = "calda";
- dynamic ferrari = new DynamicEntity();
- ferrari.ProductLine = line;
- ferrari.Name = "Dino";
- ferrari.Description = "Ferrari Dino";
- ferrari.Fuel = "Gasoline";
- dynamic lamborghini = new DynamicEntity();
- lamborghini.ProductLine = line;
- lamborghini.Name = "Countach";
- lamborghini.Description = "Lamborghini Countach";
- lamborghini.Origin = "Italy";
- line.Models.Add(ferrari);
- line.Models.Add(lamborghini);
- savedId = s.Save("ProductLine", (object)line);
- t.Commit();
- }
Updating Ducks
- dynamic productLine;
- using (ISession s = sessions.OpenSession())
- using (ITransaction t = s.BeginTransaction())
- {
- var entity = s.Get("ProductLine", savedId);
- productLine = new DynamicEntity(entity);
- productLine.Description = "Quality cars";
- dynamic newModel = new DynamicEntity();
- newModel.ProductLine = entity;
- newModel.Name = "Locus";
- newModel.Description = "Audi Locus";
- productLine.Models.Add(newModel);
- t.Commit(); // Persist modification
- }
- // Test over modifications
- using (ISession s = sessions.OpenSession())
- {
- var entity = s.Get("ProductLine", savedId);
- productLine = new DynamicEntity(entity);
- Assert.AreEqual("Quality cars",productLine.Description);
- Assert.AreEqual(3, productLine.Models.Count);
- }
Deleting Ducks
- using (ISession s = sessions.OpenSession())
- using (ITransaction t = s.BeginTransaction())
- {
- // Delete the detached dynamic entity
- s.Delete("ProductLine", productLine);
- t.Commit();
- }
- using (ISession s = sessions.OpenSession())
- {
- // check entity deletation with cascade
- s.Get("ProductLine", savedId).Should().Be.Null();
- s.CreateQuery("from Model").List().Count.Should().Be.EqualTo(0);
- }
The “Magic”
This time the magic is all in few lines of one class…- public class DynamicEntity : DynamicObject, IDictionary
- {
- private readonly Hashtable dictionary = new Hashtable();
- public DynamicEntity()
- {
- }
- public DynamicEntity(object fromNh)
- {
- dictionary = fromNh as Hashtable;
- }
- public override bool TryGetMember(GetMemberBinder binder, out object result)
- {
- string name = binder.Name;
- result = dictionary[name];
- var nestedEntity = result as Hashtable;
- if (nestedEntity != null)
- {
- result = new DynamicEntity(nestedEntity);
- }
- return dictionary.Contains(name);
- }
- public override bool TrySetMember(SetMemberBinder binder, object value)
- {
- dictionary[binder.Name] = value;
- return true;
- }
- #region IDictionary Members
- ...
- #endregion
- }
Conclusion
If you are working with anemic entities, and especially if you then transforms it to DTO, ask yourself why you should write those classes when you can do the same work with just one implementation.re-NHibernating… quack quack ;-)
"ask yourself why you should write those classes"
ReplyDeleteWell, one of the reasons to write classes is just for the Intellisense. This is BIG reason for me.
so, we can define it as : IDD = IDE Driven Design
ReplyDeleteLol IDD. What would you call you example? NDD - Notepad Driven Design.
ReplyDeleteStatic Typing. You can't just assume that duck typing is better. The intellisense is just a hint that you're not just putting random strings in your code that will blow when you try to execute them.
ReplyDeletewhere lamborghini.Origin is saved?
ReplyDeleteIt will be ignored because not present in the schema.
ReplyDelete