Fluent-Nhibernate HasMany property reference on an object - nhibernate

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");
}

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>()

NHibernate exception: could not initialize a collection, Invalid column name. Fluent mapping. Maybe a many-to-one issue?

I am puzzled and frustrated by an exception I'm getting via NHibernate. I apologize for the length of this post, but I've tried to include an appropriate level of detail to explain the issue well enough to get some help!
Here's the facts:
I have a Person class which contains a property BillingManager, which is also a Person type. I map this as an FNH "Reference".
I have an ExpenseReport class which contains a property SubmittedBy, which is a Person type. I map this as an FNH "Reference".
I have a BillableTime class which contains a property Person, which is a Person type. I map this as an FNH "Reference".
Person contains a collection (IList) of ExpenseReport types (property ExpenseReports)
Person contains a collection (IList) of BilledTime types (property Time)
(See classes and mappings at bottom of post.)
All was cool until I added the IList<BilledTime> Time collection to Person. Now, when I try to access _person.Time, I get an exception:
The code:
// Get billable hours
if (_person.Time == null ||
_person.Time.Count(x => x.Project.ProjectId == project.ProjectId) == 0)
{
// No billable time for this project
billableHours = Enumerable.Repeat(0F, 14).ToArray();
}
The exception:
could not initialize a collection:
[MyApp.Business.Person.Time#211d3567-6e20-4220-a15c-74f8784fe47a]
[SQL: SELECT
time0_.BillingManager_id as BillingM8_1_,
time0_.Id as Id1_,
time0_.Id as Id1_0_,
time0_.ReadOnly as ReadOnly1_0_,
time0_.DailyHours as DailyHours1_0_,
time0_.Week_id as Week4_1_0_,
time0_.Person_id as Person5_1_0_,
time0_.Project_id as Project6_1_0_,
time0_.Invoice_id as Invoice7_1_0_
FROM [BillableTime] time0_
WHERE time0_.BillingManager_id=?]
It's true that BillingManager_id is an invalid column name, it doesn't exist in the BillableTime table. However, I don't understand why NHB has created this SQL... doesn't make sense to me. I have seen this "Invalid column name" exception a lot when searching for a solution, but nothing seems to work. Even more confusing: like BilledTime, the ExpenseReport type also contains a reference to Person and it works perfectly.
One thing I was able to figure out is that if I remove the BillingManager reference from the Person mapping (References(p => p.BillingManager)), the exception goes away and things seem to work (with respect to BillableTime; it of course breaks the BillingManager persistence). Now it seems like there is some "self-reference" problem, since the Person.BillingManager property is itself a reference to a Person.
Any idea what is going on here? I'm at a loss...
Thanks.
=== Classes & Mappings ===
public class Person
{
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual Person BillingManager { get; set; }
public virtual IList<ExpenseReport> ExpenseReports { get; set; }
public virtual IList<BillableTime> Time { get; set; }
}
public class PersonMapping : ClassMap<Person>
{
public PersonMapping()
{
Id(p => p.UserId).GeneratedBy.Assigned();
Map(p => p.LastName).Not.Nullable();
Map(p => p.FirstName).Not.Nullable();
References(p => p.BillingManager);
HasMany(p => p.ExpenseReports).Cascade.AllDeleteOrphan();
HasMany(p => p.Time).Cascade.AllDeleteOrphan();
}
}
public class BillableTime
{
public virtual int Id { get; private set; }
public virtual Week Week { get; set; }
public virtual Person Person { get; set; }
public virtual Project Project { get; set; }
public virtual float[] DailyHours { get; set; }
public virtual Invoice Invoice { get; set; }
public virtual bool ReadOnly { get; set; }
}
public class BillableTimeMapping : ClassMap<BillableTime>
{
public BillableTimeMapping()
{
Id(x => x.Id);
References(x => x.Week);
References(x => x.Person);
References(x => x.Project);
References(x => x.Invoice);
Map(x => x.ReadOnly).Not.Nullable().Default("0");
Map(x => x.DailyHours).Length(28);
}
}
public class ExpenseReport
{
public virtual long Id { get; set; }
public virtual Person SubmittedBy { get; set; }
}
the following line should solve the issue, but i' dont know exactly why it is happening. if i have the spare time i will investigate.
HasMany(p => p.Time).Cascade.AllDeleteOrphan().KeyColumn("Person_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 - One to Many Mapping Issue

I am getting NHibernate.MappingException while trying to do one-to-many mapping in fluent nhibernate.
Below are the snippets from my entity and their mapping classes:
public class ReportRequest : IReportRequestToBeFullyLoaded
{
public virtual Int32? Id { get; set; }
public virtual string Description { get; set; }
public virtual ISet<ReportOutputEmail> ReportOutputEmails { get; set; }
}
public class ReportOutputEmail
{
public virtual string RecipientAddress { get; set; }
public virtual string Message { get; set; }
public virtual ReportRequest ReportRequest { get; set; }
}
public class ReportRequestMap : ClassMap<ReportRequest>
{
public ReportRequestMap()
{
Table("ReportRequest");
Id(x => x.Id).UnsavedValue(null).GeneratedBy.Native();
Map(x => x.Description);
HasMany(x => x.ReportOutputEmails).Table("ReportOutputEmail")
.ForeignKeyConstraintName("FK_ReportOutputEmail_ReportRequest")
.KeyColumn("ReportRequestId")
.AsSet()
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class ReportOutputEmailMap: ClassMap<ReportOutputEmail>
{
public ReportOutputEmailMap()
{
References(x => x.ReportRequest)
.ForeignKey("FK_ReportOutputEmail_ReportRequest")
.Column("ReportRequestId");
Map(x => x.RecipientAddress);
Map(x => x.Message);
}
}
There is some issue with one-to-many mapping between ReportRequest->ReportOutputEmail,
Getting error:
Error: NHibernate.MappingException: (XmlDocument)(3,6): XML validation error: The
element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element
'property' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'meta, subselect, cache, synchronize, comment, tuplizer, id, composite-id' in namespace 'urn:nhibernate-mapping-2.2'.
Can anyone help figuring out.
Thank you!
Your ReportOutputEmail doesn't have an identity, it'll need one if its going to be an entity.
Also, I recommend you upgrade your copy of Fluent NHibernate, as this is reported in a much more helpful manner since 1.1 (you'd get an identity missing message).

Fluent NHibernate Relationship Mapping and Save Exception

I'm new to NHibernate and am attempting to use Fluent's AutoMapping capability so that I do not need to maintain separate XML files by hand. Unfortunately I'm running into a problem with referenced entities, specifically 'Exception occurred getter of Fluent_NHibernate_Demo.Domain.Name.Id' - System.Reflection.TargetException: Object does not match target type.
I appear to have an error in at least one of my mapping classes although they do generate the correct SQL (i.e. the created tables have the correct indexes).
The implementations for my domain models and mappings are:
Name.cs
public class Name
{
public virtual int Id { get; protected set; }
public virtual string First { get; set; }
public virtual string Middle { get; set; }
public virtual string Last { get; set; }
}
Person.cs
public class Person
{
public virtual int Id { get; protected set; }
public virtual Name Name { get; set; }
public virtual short Age { get; set; }
}
NameMap.cs
public NameMap()
{
Table("`Name`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
Map(x => x.First).Column("`First`").Not.Nullable().Length(20);
Map(x => x.Middle).Column("`Middle`").Nullable().Length(20);
Map(x => x.Last).Column("`Last`").Not.Nullable().Length(20);
}
PersonMap.cs
public PersonMap()
{
Table("`Person`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
References<Name>(x => x.Name.Id, "`NameId`").Not.Nullable();
// There's no exception if the following line is used instead of References
// although no constraint is created
// Map(x => x.Name.Id).Column("`NameId`").Not.Nullable();
Map(x => x.Age).Column("`Age`").Nullable();
}
Finally, the following code will produce the exception:
Name name = new Name { First = "John", Last = "Doe" };
session.Save(name);
Person person = new Person { Name = name, Age = 22 };
session.Save(person); // this line throws the exception
As mentioned, the created schema is correct but I'm unable to save using the above code. What is the correct way to create a foreign key constraint using Fluent NHibernate?
If you want to reference the name, by ID, then that's what you should do. NHibernate is smart enough to figure out what the actual FK field on Person should be and where it should point; that is, after all, the job an ORM is designed to perform.
Try this mapping:
public PersonMap()
{
Table("`Person`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
References(x => x.Name, "`NameId`").Not.Nullable();
Map(x => x.Age).Column("`Age`").Nullable();
}
You've mapped the person and the name; as a result, NHibernate knows which property is the ID property of Name, and can create and traverse the foreign key on Person.