Problem
I'm using NHibernate mapping by code to map relationships. In this case I map users to roles to privileges. Users and roles are in a n:m relationship, same for roles and privileges. The SQL DB is SQL Server.
If I remove the n:m relationshop from my code between roles and privileges, my code works. If it is there, I get the following MappingException:
Could not determine type for:
Dtp.Entities.AppPrivilege, Dtp.Entities, for columns: NHibernate.Mapping.Column(id)
I can't find the source of the difference, since the same relationshop between users and roles works without a hitch. Can anyone shed a light on this problem please?
The precise part which produces the error (it goes away if commented out) is the Bag for AppRole.AppPrivileges.
MyCode
AppUser
~~~ Table ~~~
AppUserId, uniqueidentifier, not null
//some omitted properties
~~~ Entity ~~~
public class AppUser {
public virtual Guid AppUserId { get; set; }
//some omitted properties
public virtual IList<AppRole> AppRoles { get; set; }
}
~~~ Mapping ~~~
public class AppUserMap : ClassMapping<AppUser>
{
public AppUserMap()
{
Table("AppUser");
Schema("dbo");
Lazy(true);
Id(x => x.AppUserId, map => map.Generator(Generators.GuidComb));
//some omitted properties
Bag(x => x.AppRoles,
colmap => {
colmap.Cascade(Cascade.None);
colmap.Table("AppUser_AppRole");
colmap.Key(x => x.Column("AppUserId"));
},
map => map.ManyToMany(many => many.Column("AppRoleId")));
}
}
AppUser_AppRole
~~~ Table ~~~
AppUserId, uniqueidentifier, not null
AppRoleId, uniqueidentifier, not null
AppRole
~~~ Table ~~~
AppRoleId, uniqueidentifier, not null
//some omitted properties
~~~ Entity ~~~
public class AppRole{
public virtual Guid AppRoleId { get; set; }
//some omitted properties
public virtual IList<AppUser> AppUsers { get; set; }
public virtual IList<AppPrivilege> AppPrivileges { get; set; }
}
~~~ Mapping ~~~
public class AppRoleMap : ClassMapping<AppRole> {
public AppRoleMap()
{
Table("AppRole");
Schema("dbo");
Lazy(true);
Id(x => x.AppRoleId, map => map.Generator(Generators.GuidComb));
//some omitted properties
Bag(x => x.AppUsers,
colmap => {
colmap.Cascade(Cascade.None);
colmap.Table("AppUser_AppRole");
colmap.Key(x => x.Column("AppRoleId"));
},
map => map.ManyToMany(many => many.Column("AppUserId")));
//The following definition produces the bug.
Bag(x => x.AppPrivileges,
colmap => {
colmap.Cascade(Cascade.None);
colmap.Table("AppRole_AppPrivilege");
colmap.Key(x => x.Column("AppRoleId"));
},
map => map.ManyToMany(many => many.Column("AppPrivilegeId")));
}
}
AppRole_AppPrivilege
~~~ Table ~~~
AppRoleId, uniqueidentifier, not null
AppPrivilegeId, uniqueidentifier, not null
AppPrivilege
~~~ Table ~~~
AppPrivilegeId, uniqueidentifier, not null
//some omitted properties
~~~ Entity ~~~
public class AppPrivilege {
public virtual Guid AppPrivilegeId { get; set; }
//some omitted properties
public virtual IList<AppRole> AppRoles { get; set; }
}
~~~ Mapping ~~~
public class AppPrivilegeMap: ClassMapping<AppPrivilege> {
public AppPrivilegeMap()
{
Table("AppPrivilege");
Schema("dbo");
Lazy(true);
Id(x => x.AppPrivilegeId, map => map.Generator(Generators.GuidComb));
//some omitted properties
Bag(x => x.AppRoles,
colmap => {
colmap.Cascade(Cascade.None);
colmap.Table("AppRole_AppPrivilege");
colmap.Key(x => x.Column("AppPrivilegeId"));
},
map => map.ManyToMany(many => many.Column("AppRoleId")));
}
}
The answer to this problem cannot be found in the files above. I just forgot to add the AppPrivilegeMap to the ModelMapper during NHibernateHelper.OnConfigure().
Now the last task is to find out where to put the .Inverse(true), so serializing an object won't result in an exception because of circular reference.
Related
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")));
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
HI all, my scenario
public class Permission
{
public virtual Function Function { get; set; }
public virtual Profile Profile { get; set; }
}
public class MapPermission : ClassMap<Permission>
{
public MapPermission()
{
Table("Permissions".ToUpper());
CompositeId().KeyProperty(x => x.Function, "FunctionID").KeyProperty(x => x.Profile, "ProfileID");
}
}
Where Function AND Profile are two easy mapped entities. When i Run i have this error:
Could not determine type for: Data.Model.Entities.Function, Data.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(FunctionID)"}
Is there a way to avoid this? ultimately i need to create a class with CompositeID made by two custom mapped classes. If i uses compositeID with int fields it works like a charm
Thanks in Advance
Function (Like Profile) Mapping
public class Function
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class MapFunction : ClassMap<Function>
{
public MapFunction()
{
Table("FUNCTIONS");
Id(x => x.ID);
Map(x => x.Name);
}
}
Use KeyReference instead of KeyProperty
public class MapPermission : ClassMap<Permission>
{
public MapPermission()
{
Table("Permissions".ToUpper());
CompositeId()
.KeyReference(x => x.Function, "FunctionID")
.KeyReference(x => x.Profile, "ProfileID");
}
}
My model object Reading has a Location but it's not a direct relationship in the database. In the DB, this "has-a" relationship or "reference" spans 3 tables, as shown in this snip:
My Reading maps to the ComponentReading table and i want my Location to map to the Location table. My ClassMap<Reading> class looks like this for now:
public class ReadingMap : ClassMap<Reading>
{
public ReadingMap()
{
Table("ComponentReading");
Id(x => x.ID).Column("ComponentReadingId");
//References(x => x.Location).Formula(
Join("VehicleReading", vr =>
{
Join("TrainReading", tr =>
{
tr.References(x => x.Location, "LocationId");
});
});
Map(x => x.TemperatureValue).Column("Temperature");
}
}
And here is my simple Location mapping:
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.ID).Column("LocationId");
Map(x => x.Name);
}
}
The commented References( method sort of shows what i want to achieve with the relationship between Reading and Location but obviously i can't express it to FNH as simply as the commented line suggests.
I don't think the Join( code is even nearly correct either, but it also tries to communicate the relationship that i'm after.
I hope someone can see what i'm trying to do here. Can you help me?
This question is related.
I think you cant nest joins that way. An ugly but pragmatic solution would be (untested):
class Reading
{
public virtual int ID { get; set; }
protected virtual Hidden.TrainReading m_trainReading;
public virtual Location Location
{ get { return m_trainReading.Location; } set { m_trainReading.Location = value; } }
public virtual int TemperatureValue { get; set; }
}
namespace Hidden
{
class TrainReading
{
public virtual int ID { get; set; }
public virtual int VehicleReadingId { get; set; }
public virtual Location Location { get; set; }
}
}
public class ReadingMap : ClassMap<Reading>
{
public ReadingMap()
{
Table("ComponentReading");
Id(x => x.ID).Column("ComponentReadingId");
References(Reveal.Member<Reading, Hidden.TrainReading>("m_trainReading"), "");
Map(x => x.TemperatureValue).Column("Temperature");
}
}
public class TrainReadingMap : ClassMap<Hidden.TrainReading>
{
public TrainReadingMap()
{
Table("TrainReading");
Id(x => x.ID).Column("TrainReadingId");
References(x => x.Location, "LocationId");
Join("VehicleReading", vr =>
{
vr.KeyColumn("TrainReadingId");
vr.Map(x => x.VehicleReadingId, "VehicleReadingId");
});
}
}
I have couple of classes and want to map them correctly to database:
public class A
{
public virtual Guid Id { get; private set; }
public virtual ComponentClass Component { get; set; }
}
public class ComponentClass
{
public virtual IList<B> Elements { get;set; }
}
public class B
{
public virtual Guid Id { get; private set; }
public virtual DateTime Time { get; set; }
}
I map them using fluent mappings like that:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Inverse().Cascade.All());
}
}
public class BMap : ClassMap<B>
{
public B() {
Id(x => x.Id);
Map(x => x.Time);
}
}
When I save my entity, I have class A mapped to one table and class B to another as expected.
But I have nulls in Component_id column.
Can you tell me what am I missing here?
I believe Components are supposed to be in the same table , as clearly stated in Ayende's blog post, as they serve only to make the data better represented as an object model. Be sure to read through his blog, it's probably one of the best nHibernate resources out there.
Ok, I've resolved my problem - I can use Id of my "parent" class. So the component mapping will become:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Cascade.All().Column("Id"));
}
}
So obvious as I look at it now ... but It took me an hour.
If you have a one-to-many association direct to a collection of components (ie. without the ComponentClass wrapper as per the question) then you can map it directly:
HasMany(x => x.Elements)
.AsSet()
.Table("ElementTable")
.KeyColumn("KeyColumn")
.Cascade.All()
.Component(x =>
{
x.Map(c => c.Id);
x.Map(c => c.Time);
})
.LazyLoad();