Lazy load an ICompositeUserType - nhibernate

I have a class MoneyCompositeUserType : ICompositeUserType
Which I use like so in a mapping:
public InvoiceMap()
{
Table("Invoices");
Id(x => x.Id);
Map(x => x.Customer);
Map(x => x.Number);
Map(x => x.TotalValue)
.CustomType(typeof(MoneyCompositeUserType))
.Columns.Clear()
.Columns.Add("TotalValue_Amount", "TotalValue_Currency");
}
And here is the class:
public class Invoice
{
public virtual int Id { get; set; }
public virtual int Number { get; set; }
public virtual string Customer { get; set; }
public virtual Money TotalValue { get; set; }
}
I thought that the value would be lazy loaded, that's the point of the virtual right? But the NullSafeGet method of the composite user type is called when the item is loaded. Here is my failing test:
using (var session = NHibernateHelper.OpenSession())
{
var fromDb = session.Get<Invoice>(invoice.Id);
Assert.IsFalse(NHibernate.NHibernateUtil.IsPropertyInitialized(fromDb, "TotalValue"));
}
Why is that property not being lazy loaded?

I thought that the value would be lazy loaded, that's the point of the virtual right?
Not exactly -- NHibernate needs your properties to be virtual so that it can use a proxy class in place of your class to enable lazy loading. Lazy loading is not enabled just because a property is marked virtual.
I believe all you should have to do is mark the individual property with .LazyLoad in your mapping (see lazy properties for more information):
Map(x => x.TotalValue)
.LazyLoad() // <-----
.CustomType(typeof(MoneyCompositeUserType))
.Columns.Clear()
.Columns.Add("TotalValue_Amount", "TotalValue_Currency");

Related

Nhibernate updating an entity when selecting

I have this entity:
public class Permission
{
public virtual int Id{get;set;}
public virtual string Entity { get; set; }
public virtual bool ReadAction { get; set; }
public virtual bool UpdateAction { get; set; }
public virtual bool CreateAction { get; set; }
public virtual bool DeleteAction { get; set; }
public virtual Role Role { get; set; }
public virtual string RoleName
{
get { return Enum.GetName(typeof(Role),this.Role}
}
}
public class PermissionMap : ClassMap<Permission>
{
public PermissionMap()
{
Id(x => x.Id, "id").GeneratedBy.Identity().UnsavedValue(0);
Map(x => x.Entity);
Map(x => x.DeleteAction).CustomType<BooleanType>();
Map(x => x.ReadAction).CustomType<BooleanType>();
Map(x => x.CreateAction).CustomType<BooleanType>();
Map(x => x.UpdateAction).CustomType<BooleanType>();
Map(x => x.Role).CustomType<int>();
}
}
public enum Role
{
Administrator = 0,
SalesPerson = 1,
Marketing = 2
}
Every time I query the database it gets Updated.
I thought it is because of RoleName, but event when I have removed it its still get update.
The Permission get update for each row it has in the database.
Thanks
As discussed in comments, the point is, that we are all the time working with a session. It is so smart that even during read operation, it does keep track of all items by their ID.
If, any object is changed somehow (e.g. for a rendering or transfering reasons) and is still referenced by open session, it is managed as dirty one. When the session.Fulsh() is called (usually by default on transaction Commit() or even auto, depends on the FlushMode) any dirty object is persisted.
So, to avoid issuing the UPDATE statement the Entity mapping and its content must match. As at the end Shazam founded out, in this case the culprit was this mapping
// Map(x => x.Role).CustomType<int>();
Map(x => CustomType<Role>()

Trying to run query for entities when I have Inheritance with fluent Nhibernate

I attempted to extract some common properties to a base class and map with Fluent Nhibernate. In addition, I also attempted to add a second level of inheritance.
//Base entity class
public class EntityBase : IEntityBase
{
public EntityBase()
{
CreatedDate = DateTime.Now;
}
public virtual DateTime? CreatedDate { get; set; }
public virtual int Id { get; set; }
public virtual int Version { get; set; }
}
//Base Entity Mapping
public class EntityBaseMap: ClassMap<EntityBase>
{
public EntityBaseMap()
{
UseUnionSubclassForInheritanceMapping();
Id(x => x.Id);
Version(x => x.Id);
Map(x => x.CreatedDate);
}
}
//first sub class of EntityBase
public class Actuate : EntityBase, IActuate
{
public virtual DateTime? ActivatedOn { get; set; }
}
//Actuate Mapping class
public class ActuateMap : SubclassMap<Actuate>
{
public ActuateMap()
{
Map(x => x.ActivatedOn);
}
}
//Sub class entity
public class Item : Actuate
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual decimal UnitPrice { get; set; }
public virtual ItemStatus Status { get; set; }
public virtual Store Store { get; set; }
}
//Item Mapping class
public class ItemMap : SubclassMap<Item>
{
public ItemMap()
{
Abstract();
Map(x => x.Name);
Map(x => x.Description);
Map(x => x.UnitPrice);
Map(x => x.Status);
References(x => x.Store);
}
}
The entity I have discovered has a problem (other relationship issues might exists)
//Store entity Does not inherit from EntityBase or Actuate
public class Store
{
public virtual int Id { get; set; }
public virtual int Version { get; set; }
public virtual string Name { get; set; }
public virtual IEnumerable<Item> Items { get; set; }
}
//Store mapping class
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id).GeneratedBy.Assigned();
Version(x => x.Version);
Map(x => x.Name);
HasMany(x => x.Items);
}
}
Problem
If I try to run the following query:
//store = is the Store entity I have retrieved from the database and I am trying
//trying to return the items that are associated with the store and are active
store.Items != null && store.Items.Any(item => item.Status == ItemStatus.Active);
I get the following error:
ERROR
Nhibernate.Exceptions.GenericADOException: could not initialize a collection: [SomeDomain.Store.Items#0][SQL: SELECT items0_.StoreId as StoreId1_, items0_.Id as Id1_, items0_.Id as Id10_0_, items0_.CreatedDate as CreatedD2_10_0_, items0_.ActivatedOn as Activate1_11_0_, items0_.Name as Name12_0_, items0_.Description as Descript2_12_0_, items0_.UnitPrice as UnitPrice12_0_, items0_.Status as Status12_0_, items0_.StoreId as StoreId12_0_ FROM [Item] items0_ WHERE items0_.StoreId=?]"}
Inner Exception
"Invalid object name 'Item'."
Now, if I take out the base classes and Item doesn't inherit, and the
Id, Version
columns are part of the Item entity and are mapped in the ItemMap mapping class (with the ItemMap class inheriting from ClassMap<Item> instead, everything works without issue.
NOTE
I have also attempted to add on the StoreMap class unsuccessful.
HasMany(x => x.Items).KeyColumn("Id");
Any thoughts?
if entityBase is just for inheriting common properties then you do not need to map it at all
public class EntityBaseMap<TEntity> : ClassMap<TEntity> where TEntity : EntityBase
{
public EntityBaseMap()
{
Id(x => x.Id);
Version(x => x.Version);
Map(x => x.CreatedDate);
}
}
public class ActuateMap : EntityBaseMap<Actuate> { ... }
Notes:
Versionmapping should map Version property not Id
Version should be readonly in code so nobody accidently alters it
.KeyColumn("Id") is wrong because the column is from the Items table and then it's both the autogenerated id and foreign key. That's not possible nor usefull
usually only classes which are abstract should containt Abstract() in the mapping

Fluent-Nhibernate HasMany property reference on an object

A referral has many leads. The entities are however, related by an agent identifier. Within my referral entity I ended up having to add an integer property mapped to the agent_id column in order to make the mappings work correctly.
If I remove the AgentID property from the entity and perform the mapping on the "Agent" object like so:
HasMany(x => x.Leads)
.AsBag()
.KeyColumn("Agent_Id")
.PropertyRef("Agent");
I run into an error:
Object does not match target type.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.Reflection.TargetException: Object does not
match target type.
I guess, I'm asking if this is an acceptable solution? The additional AgentID property won't be used anywhere other than within the property reference. Is there another way to perform this mapping within having to change the domain model as it cannot be changed at this time.
The working mappings:
public class Referral
{
public virtual int Id { get; set; }
public virtual int AgentID { get; set; }
public virtual Agent Agent { get; set; }
public virtual int? PositionNumber { get; set; }
public virtual DateTime? LastReferralDate { get; set; }
public virtual Account Account { get; set; }
public virtual IEnumerable<Lead> Leads { get; set; }
}
public ReferralMap()
{
Table("Referral");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
Map(x => x.PositionNumber).Column("PositionNumber");
Map(x => x.LastReferralDate).Column("LastReferralDate");
Map(x => x.AgentID).Column("Agent_ID");
References(x => x.Agent).Column("Agent_ID");
References(x => x.Account).Column("Account_id");
HasMany(x => x.Leads)
.AsBag()
.KeyColumn("Agent_Id")
.PropertyRef("AgentID");
}
i guess it is because you have
public LeadMap()
{
Id(l => l.AgentId).Column("Agent_Id");
}
instead of
public LeadMap()
{
CompositeId().KeyReference(l => l.Agent, "Agent_Id");
}

NHibernate uni-directional associations

Playing around with Fluent NHibernate's Getting Started project. I tried to customize the example a bit, for a few reasons, among them elimination of circular reference for json serialization.
What I have done is to strip the Store and StoreMap classes of it's references back to Employee and Product classes. It now looks like this:
Store/StoreMap
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
}
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
}
Employee/EmployeeMap
public class Employee
{
public virtual int Id { get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store).Cascade.All();
}
}
Product/ProductMap
public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; private set; }
public Product()
{
StoresStockedIn = new List<Store>();
}
public virtual void StockAt(Store store)
{
StoresStockedIn.Add(store);
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All()
.Table("StoreProduct");
}
}
I've moved "Cascade" operations into the Product and Employee instead of Store. From the testing I've done, both HasMany and HasManyToMany associations seem to be working okay.
My question is if it's the right approach. Whether or not it will lead to something that I have not anticipated.
as far as I know, it's standard practice in nHibernate to have both ends of a relationship in your model classes (actually, it usually makes sense from the business point of view).
Although you don't have to do it (your example, I think, should work fine).
If you don't want to expose one (or both) ends you can always define them as private or protected.
p.s concerning json serialization: it's not recommended to serialize your model entities. this is for a few reasons, the top ones being:
1. the data you display to the user may be different from what you have in your entities (for example- you might want to present an employee with their name, Id, their store name and number of products they own. it would be hard to do that using your model classes).
2. it's quite possible that you'd have uninitialized collections in your objects (if you use lazy-loading). These do not get serialized.
For serialization, DTOs is the recommended approach.

Fluent NHibernate mapping IDictionary<string, class> in a smart way

Given these classes:
using System.Collections.Generic;
namespace FluentMappingsQuestion
{
public class Entity
{
public virtual int Id { get; set; }
public virtual IDictionary<string, Property> Properties { get; set; }
}
public class Property
{
public virtual Entity OwningEntity { get; set; }
public virtual string Name { get; set; }
public virtual int Value { get; set; }
public virtual decimal OtherValue { get; set; }
}
}
How can I map them using NHibernate (preferably fluent flavor) so that doing this is possible:
[Test]
public void EntityPropertyMappingTest()
{
using (var session = _factory.OpenSession())
{
var entity = new Entity();
// (#1) I would *really* like to use this
entity.Properties["foo"] = new Property { Value = 42, OtherValue = 42.0M };
session.Save(entity);
session.Flush();
// (#2) I would like the entity below to "update itself"
// on .Save or .Flush. I got it to work with .Load()
Assert.AreEqual(42, entity.Properties["foo"].Value);
Assert.AreEqual(42.0M, entity.Properties["foo"].OtherValue);
Assert.AreEqual("foo", entity.Properties["foo"].Name);
Assert.AreEqual(entity, entity.Properties["foo"].Owner);
}
}
I have almost managed to do this with these mappings:
// class EntityMap : ClassMap<Entity>
public EntityMap()
{
Id(x => x.Id);
HasMany(x => x.Properties)
.Cascade.AllDeleteOrphan()
.KeyColumn("EntityId")
.AsMap(x => x.Name);
}
// class PropertyMap : ClassMap<Property>
public PropertyMap()
{
Id(x => x.Id);
References(x => x.OwningEntity).Column("EntityId");
Map(x => x.Name).Length(32);
Map(x => x.Value);
{
The problems I have:
If I make Entity.Properties .Inverse(), it starts breaking
If I don't make it .Inverse() then NHibernate does: INSERT(Entity), INSERT(Property), UPDATE(Property) instead of just INSERT(Entity), INSERT(Property)
If I make Property.Name .Not.Nullable(), it starts breaking
If I don't make it .Not.Nullable(), I have a hole in my db schema
How should I change my mappings?
I worked around this by specifying this mapping:
HasMany<Property>(Reveal.Member<Entity>("InternalProperties"))
.AsMap(p => p.Name)
.Cascade.AllDeleteOrphan()
.Inverse();
and creating two properties of type IDictionary<string, Property>: Properties and InternalProperties. The first one is a proxy dictionary over the second one, and deals with setting the OwningEntity and Name properties for the Property entries.