NHibernate intermediate table mapping by code - nhibernate

I know that there are similar questions to this topic on stackoverflow, but none of them could solve my problem. I'm stuck with it, for at least two days now. So here is my question:
I have two tables with a Many-To-Many relationship, like this:
Department * - * Person
I don't want to use a Many-To-Many relationship, but two Many-To-One and define the link table as entity.
The link table should be called "Lam".
My entities
public class Department
{
public virtual int Id { get; set; }
public virtual IList<Lam> Lams { get; set; }
}
public class Person
{
public virtual int Id { get; set; }
public virtual IList<Lam> Lams { get; set; }
}
public class Lam
{
public virtual Department Department { get; set; }
public virtual Person Person { get; set; }
}
My mappings
public class DepartmentMapping : ClassMapping<Department>
{
public DepartmentMapping()
{
Id(x => x.Id, map => map.Generator(Generators.Native));
Bag(x => x.Lams, col =>
{
col.Key(k => k.Column("DepartmentId"));
col.Inverse(true);
}, r => r.OneToMany());
}
}
public class PersonMapping : ClassMapping<Person>
{
public PersonMapping()
{
Id(x => x.Id, map => map.Generator(Generators.Native));
Bag(x => x.Lams, col =>
{
col.Key(k => k.Column("PersonId"));
col.Inverse(true);
}, r => r.OneToMany());
}
}
Pairing mapping:
public class LamMapping : ClassMapping<Lam>
{
public LamMapping()
{
ManyToOne(x => x.Department, map =>
{
map.Column("DepartmentId");
});
ManyToOne(x => x.Person, map =>
{
map.Column("PersonId");
});
}
}
If I try to run my application, I get the following error message:
Incorrect syntax near 'Index'. If this is intended as a part of a
table hint, A WITH keyword and parenthesis are now required. See SQL
Server Books Online for proper syntax.
Could someone please just tell me, whats wrong with my code?

The mapping as is is correct (at least the same was working for me). So, the question is from where is coming your exception?.
Let's have a SQL SELECT query over your simplified table/entity Person:
SELECT id FROM Person
That would work.
But if - in a not shown part of your mapping - exists Person's some property, let's say Index like this:
// entity
public class Person
{
...
// C# property named Index
public virtual int Index { get; set; }
...
// mapping
public class PersonMapping : ClassMapping<Person>
{
public PersonMapping()
{
...
// that by default would be column Index
Property(x => x.Index)
That would lead to SELECT like this
SELECT id, Index FROM Person
And in SQL Server worlds, that will cause error:
Msg 1018, Level 15, State 1, Line 1
Incorrect syntax near 'Index'. If this is intended as a part of a table hint, A WITH keyword and parenthesis are now required.
So, because your mapping shown above is correct, I would suspect some part like this
If this is the case, we can use
...
Property(x => x.Index, x => { x.Column("[Index]"); });

Related

NHibernate mapping - self referencing: parent and children

I am trying to have this kind of Model:
public class Activity
{
public virtual int ID { get; set; }
public virtual int? ParentID { get; set; }
public virtual int? RootID { get; set; }
public virtual Activity Parent { get; set; }
public virtual Activity Root { get; set; }
public virtual IList<Activity> Children { get; set; }
}
If you are looking at it from a structure point of view, it is a tree.
The root element does not have a parent or a root, but may have children.
Any of its children must have a parent and a root (for the first level children root = parent)
The mapper is like this:
public class ActivityMap : ClassMapping<Activity>
{
public ActivityMap()
{
Table("activity");
Lazy(true);
Id(x => x.ID, map => map.Generator(Generators.Identity));
ManyToOne(x => x.Root, map => { map.Column("RootID"); map.Cascade(Cascade.All); });
Bag(x => x.Children,
mapping =>
{
mapping.Inverse(false);
mapping.Lazy(CollectionLazy.Lazy);
mapping.Key(k => k.Column("ParentID"));
mapping.Cascade(Cascade.All);
},
mapping => mapping.ManyToMany(map=>map.Class(typeof(Activity)))
);
}
}
The problem is when I try to fetch the children, the sql statement looks like:
SELECT children0_.ParentID as ParentID1_,
children0_.elt as elt1_,
activity1_.ID as ID55_0_,
activity1_.TaskID as TaskID55_0_,
activity1_.ActivityTypeID as Activity3_55_0_,
activity1_.StateID as StateID55_0_,
activity1_.Continueforward as Continue5_55_0_,
activity1_.Ordernumber as Ordernum6_55_0_,
activity1_.IsDeleted as IsDeleted55_0_,
activity1_.Created as Created55_0_,
activity1_.Modified as Modified55_0_,
activity1_.StartTime as StartTime55_0_,
activity1_.EndTime as EndTime55_0_,
activity1_.Progress as Progress55_0_,
activity1_.RootID as RootID55_0_
FROM Children children0_ left outer join activity activity1_ on children0_.elt=activity1_.ID WHERE children0_.ParentID=?
First of all it seems that it is looking for the Children table which does not exist. Should be Activity table
Second: I am not sure what is with that "elt" column... it does not exist anywhere
Anyone has an idea how to make this mapping?
Later Edit:
found in answer to the second question:
NHibernate elt field
In my later edit I have the answer to my second question.
And for the second question, the solution that I found is to give up to the relation with the Root entity
ManyToOne(x => x.Root, map => { map.Column("RootID"); map.Cascade(Cascade.All); });
and replace it with
Property(x => x.RootID);
because I have no need to have the entire entity for the Root.
Also I have changed the bag for Children like this:
Bag(x => x.Children,
mapping =>
{
mapping.Inverse(false);
mapping.Lazy(CollectionLazy.Lazy);
mapping.Key(k => k.Column("ParentID"));
mapping.Cascade(Cascade.All);
},
mapping => mapping.OneToMany()
);

How do I return only one specific item from hasmany relationship

Though I have benefited from the collective wisdom of this site many times, this is my first question here.
I have, say, three classes like so:
public class ClassA
{
public ClassA() {}
public virtual IList<ClassB> ClassBs { get; set; }
}
public class ClassB
{
public ClassB() {}
public virtual DateTime StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual ClassC SomeObject { get; set; }
}
public class ClassC
{
public ClassC() {}
public virtual string Name { get; set; }
}
I am using NHibernate (3.3.1.4) and FluentNHibernate (1.3.0.733), and the mapping files are:
public Class ClassAMap : ClassMap<ClassA>
{
HasMany(x => x.ClassBs);
}
public Class ClassBMap : ClassMap<ClassB>
{
Map(x => x.StartDate).Not.Nullable();
Map(x => x.EndDate).Nullable();
References(x => x.SomeData).Not.Nullable();
}
public Class ClassCMap : ClassMap<ClassC>
{
Map(x => x.Name).Not.Nullable();
}
There are also IDs and versioning but I somehow think they are irrelevant.
What I want to do is:
select all "SomeObject"s from ClassBs of all "ClassA"s which have their "EndDate"s null and which have the most current StartDate in their group.
I tried some juggling with QueryOver but the most I could get was an IList<IList<ClassB>> which is really far from what I want to accomplish.
Edit:
(I think) Following code accomplishes the task with Linq. But this code requires all records from the DB. This may not be a problem for ClassBs with up to 3 records or so per ClassA but for ClassBs with hundreds of records this means getting all those records from DB to use just one record from ClassBs of each ClassA in the DB.
IList<ClassC> classCLs = new List<ClassC>();
ClassB latest = null;
foreach (
IList<ClassB> classBLs in
Session.QueryOver<ClassA>()
.Select(c => c.ClassBs).List<IList<ClassB>>()
) {
latest = classBLs.Where(cB => cB.EndDate == null).Aggregate((curr, next) => next.StartDate > curr.StartDate ? next : curr);
if (latest != null && !classCLs.Contains(latest.SomeObject)) {
classCLs.Add(latest.SomeObject);
}
}
Assuming your ClassA has an Id property, maybe this, involving a subquery, can be of some help :
ClassA aAlias = null;
ClassB bAliasMax = null, bAlias = null;
var subQuery = QueryOver.Of<ClassA>().JoinAlias(a => a.ClassBs, () => bAliasMax)
.Where(Restrictions.On(() => bAliasMax.EndDate).IsNotNull)
.Where(a=>a.Id==aAlias.Id)
.Select(Projections.Max(() => bAliasMax.StartDate));
var result =
_laSession.QueryOver(() => aAlias)
.JoinAlias(a => a.ClassBs, () => bAlias)
.WithSubquery.WhereProperty(() => bAlias.StartDate).Eq(subQuery)
.Select(Projections.Property(() => bAlias.SomeObject)) // suggested adding from question's author
.List<ClassC>(); // see above
I guess there are more effective answers, which would involve grouping.

NHibernate mapping by code: How to map IDictionary?

How can I map these Entities using mapping-by-code:
public class Foo
{
public virtual IDictionary<Bar, string> Bars { get; set; }
}
public class Bar
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
I found this thread, but it does not map an entity, only simple types. I tried many mappings, including automapping:
Map(x => x.Bars,
m =>
{
m.Key(k => k.NotNullable(true));
m.Cascade(Cascade.All);
},
But most of them throw these two errors:
Foreign key (Bars [idx])) must have same number of columns as the referenced primary key (Bars [FooId, idx]).
An association from the table FoosToStrings refers to an unmapped class: System.String.
Any help will be highly appreciated. Thanks. :)
i think this should work
Map(x => x.Bars,
entryMap => entryMap.Key(k => k.Column("foo_id")),
keymap => keymap.ManyToMany(m => m.Column("bar_Id")),
elementMap => elementMap.Element(m => m.Column("value")));

Why doesn't this class hierarchy fluent mapping work?

What's wrong with my mapping shown below? Is this a problem with GeneratedBy.Foreign()? How should I use it cause my PK in UserTable(UID) is also the FK which refers to PersonTable PK(PID). I get the Duplicate class/entity mapping consoleMappingTest.SystemUser error. what do you suggest(be sure to look at database structure- no way to change it). thanks.
Inheritance structure:
public class Person
{
public virtual int ID { get; set; }
}
public class User:Person
{
public override int ID
{
get
{
return base.ID;
}
set
{
base.ID = value;
}
}
public virtual string Name { get; set; }
public virtual int Salary { get; set; }
}
public class SystemUser:User
{
public virtual int Password { get; set; }
}
Database structure:
for saving some info about person(some fields not shown here):
PersonTable(PID)
for saving User and all it's subclasses like system user:
UserTable(UID,Name,Salary,Type)
and here is my mapping:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonTable");
Id(x => x.ID, "PID").GeneratedBy.Assigned();//or HiLo-not important
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("UserTable");
DiscriminateSubClassesOnColumn("Type").Default("U");
Id(x => x.ID, "UID").GeneratedBy.Foreign("Person");//how should use this?
Map(x => x.Salary);
Join("PTable", j =>
{
j.KeyColumn("UID");
j.Map(x => x.Name);
});
}
}
public class SystemUserMap : SubclassMap<SystemUser>
{
public SystemUserMap()
{
DiscriminatorValue("SU");
Map(x => x.Password);
}
}
Foreign("") is meant to point to a Reference (Property with another mapped entity) from which the Id should be retrieved. You don't have a Reference to class Person named Person so you can't use it like this.
you already asked the same question with an answer. I know i didn't do it right first shot but would be nice if you told me what doesnt work with the latest edit or you dont like the solution befor asking the same question again

SubClass mapping with multipe "roles"

a tricky problem - please bear with me. Any help greatly appreciated.
I have a table/class Contact (PK Id) and two derived Client and Debtor (PK and FK ContactId). The 4th table Case has foreign keys to Debtor and Client (mappings below).
Everything worked fine at first. But then I hit some data where the same Contact is a Client in one Case but a Debtor in another. If those are read in one nhibernate query like Session.Query<Case>().Fetch(c => c.Debtor).Fetch(c => c.Client)
there is a
NHibernate.WrongClassException
"Object with id: {someGuid...} was not of the specified subclass: Client
(loading object was of wrong class [Debtor])
Seems like the session first level cache is recognizing the record by it's Id and tries to avoid reading the data from the sql result set. Of course the cast NH thinks is necessary for the reuse fails.
Unfortunately changing the DB schema is not an option. It's a legacy system. (an the schema is ok and clean IMO)
Don't know if it is important: The class Contact is not abstract. There are Contacts used who are neither Client nor Debtor.
Is there any chance of getting this to work with these multi-role-contacts? Thanks in advance.
public partial class ContactMap : ClassMap<Contact>
{
public ContactMap()
{
Id(x=>x.Id).GeneratedBy.Guid();
Map(x=>x.FirstName);
Map(x=>x.Name1).Not.Nullable();
...
}
}
public class DebtorMap : SubclassMap<Debtor>
{
public DebtorMap()
{
KeyColumn("ContactID");
Table("[dbo].[Debtor]");
Map(x => x.MaritalStatus);
...
}
}
public partial class ClientMap : SubclassMap<Client>
{
public ClientMap()
{
KeyColumn("ContactID");
Map(x => x.ClientNo).Not.Nullable();
...
}
}
public partial class CaseMap : ClassMap<Case>
public CaseMap()
{
...
References<Client>(x=>x.Client)
References<Debtor>(x=>x.Debtor)
...
}
If you can add a view to the schema, you can create a view called Roles which unions both Client and Debtor records. You can then change your object model to represent roles:
class Contact
{
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual ICollection<Role> Roles { get; private set; }
}
class Role
{
public virtual Guid Id { get; set; }
}
class Client : Role
{
public virtual string ClientNo { get; set; }
}
class Debtor : Role
{
public virtual string MaritalStatus { get; set; }
}
class ContactMap : FluentNHibernate.Mapping.ClassMap<Contact>
{
public ContactMap()
{
Table("dbo.Contacts");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.FirstName);
HasMany(x => x.Roles)
.KeyColumn("ContactId")
.Not.LazyLoad()
.Fetch.Join();
}
}
class RoleMap : FluentNHibernate.Mapping.ClassMap<Role>
{
public RoleMap()
{
Table("dbo.Roles");
Id(x => x.Id).GeneratedBy.GuidComb();
this.Polymorphism.Implicit();
}
}
class ClientMap : FluentNHibernate.Mapping.SubclassMap<Client>
{
public ClientMap()
{
Table("dbo.Clients");
KeyColumn("Id");
Map(x => x.ClientNo);
}
}
class DebtorMap : FluentNHibernate.Mapping.SubclassMap<Debtor>
{
public DebtorMap()
{
Table("dbo.Debtors");
KeyColumn("Id");
Map(x => x.MaritalStatus);
}
}
Since the Contact table does now share a PK with Client and Debtor tables this should work. The Roles view would look something like this:
create view dbo.Roles as
select
Id,
ContactId
from dbo.Clients
union all
select
Id,
ContactId
from dbo.Debtors