Fluent NHibernate mapping foreign key - nhibernate

I have a db that I'm trying to model using Fluent NHibernate.
the tables in questions are:
User:
PK Id, Name, FK accessType, FK authorization
AccessType:
PK Id, Name
Authorization:
PK Id, Name
Permission:
PK Id, FK menuId, FK accessId, FK authId
User Entity:
public Users()
{
Permissions = new List<Permissions>();
}
public virtual AccessTypes AccessType { get; set; }
public virtual Authorization Authorization { get; set; }
public virtual string Name { get; set; }
public virtual IList<Permissions> Permissions { get; set; }
Permission Entity:
public class Permissions : EntityWithTypedId<long>
{
public virtual Menus Menu { get; set; }
public virtual AccessTypes AccessType { get; set; }
public virtual Authorization Authorization { get; set; }
}
User Map:
public UsersMap()
{
Table("USERS");
Map(x => x.Name, "NAME");
References<AccessTypes>(x => x.AccessType, "ACCESS_TYPE_ID");
References<Authorization>(x => x.Authorization, "AUTHORIZATION_ID");
Id(x => x.Id, "ID")
.Column("ID")
.GeneratedBy.Assigned();
HasMany<Permissions>(x => x.Permissions)
.KeyColumns.Add("ACCESS_TYPE_ID", "AUTHORIZATION_ID")
.Inverse()
.Cascade.None();
}
Permission Map:
public PermissionsMap()
{
ReadOnly();
Table("PERMISSIONS");
References<Menus>(x => x.Menu, "MENU_ID");
References<AccessTypes>(x => x.AccessType, "ACCESS_TYPE_ID");
References<Authorization>(x => x.Authorization, "AUTHORIZATION_ID");
Id(x => x.Id, "ID")
.Column("ID")
.GeneratedBy.Assigned();
}
I got this error: Foreign key (FK79B2A3E83BA4D9E3:PERMISSIONS [ACCESS_TYPE_ID, AUTHORIZATION_ID])) must have same number of columns as the referenced primary key (USERS [ID])
I need to get a list of permission by checking the user accessType and user authorization.
My question is: How can I map the Permission list in User mapping? Should I Use the ternary association?
Does anyone have any insight on how to do this?

This scenario is not supported. NHibernate has a feature called property-ref which can be used (but should be avoided) on old databases that were designed poorly. However, property-ref only supports referencing one non-primary-key column. Since you are trying to reference two such columns, this will not work.
However, since the permissions are obviously not tied to the user per se, you should not even map them.
You could still have the property for the list in the Users class and fill that with an extra method that simply reads the Permissions using a Where-condition on both columns. Still, I would advise against this. I would write a method like this (code not tested):
public IList<Permissions> GetPermissionsForUser(Users user)
{
return session.QueryOver<Permissions>()
.Where(p => p.Authorization.Equals(user.Authorization))
.And(p => p.AccessType.Equals(user.AccessType)).List();
}

Related

How to implement Asp.net IdentityUser with breezejs NHibernate

I am working with a project that leverage Breezejs and NHibernate. I implemented Asp.Net IdentityUser in my entity model.
Anytime i tried to generate metedata, insert or update my model, using breeze NHContext. a foreign key not match exception is always thrown. Please, how do i use Fluent mapping in my code in order get over this NorthBreeze limitation
When using NHibernate with Breeze, the foreign keys must be mapped to properties of your entity class. That is so the foreign keys can will be available on the client. For the IdentityUserClaim entity, you would need something like this:
public class IdentityUserClaim : EntityWithTypedId<int>
{
public virtual string ClaimType { get; set; }
public virtual string ClaimValue { get; set; }
public virtual IdentityUser User { get; set; }
// foreign key property
public virtual int UserId { get; set; }
}
public class IdentityUserClaimMap : ClassMapping<IdentityUserClaim>
{
public IdentityUserClaimMap()
{
Table("AspNetUserClaims");
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.ClaimType);
Property(x => x.ClaimValue);
ManyToOne(x => x.User, m => m.Column("User_Id"));
// foreign key mapping
Property(x = x.UserId).Column("User_Id").Not.Insert().Not.Update();
}
}
Note the foreign key is mapped with insert=false and update=false. Updates to the User_Id column go through the normal NHiberate flow (i.e. they are controlled by the related User entity). The UserId property is used only to expose the foreign key value to Breeze.

Entity Framework 5 One to One relationship (e.g. User -> Profile) - ModelBuilder ASP.NET MVC4

I am trying to do a similar thing to what this previous answer had here:
How to declare one to one relationship using Entity Framework 4 Code First (POCO)
The problem is, im very new to this and am using Entity Framework 5 code first and the HasConstraint doesnt exist anymore, not to mention Im not good at lamda. I was wondering if anyone could help expand on this so I can map a User class to a Profile class effectively and easily? I need to know how to do this for the configuration files and model builder
Each user has one profile
Also, another quick question, say the profile model had Lists in this, how would I put these effectively in the model builder and configuration files?
Thank you
e.g.
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public Profile Profile { get; set; }
// public int ProfileId { get; set; }
}
public class Profile
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PostalCode { get; set; }
}
// ...
modelBuilder.Entity<User>()
.HasOptional(x => x.Profile)
.WithRequired();
ProfileId is useless, FK is on the 'other side of the fence' (in Profile).
(this makes most sense IMO)
If you do need an Id in User (e.g. to be able to fill in Profile just by its ID when adding User - which if one-to-one is not really used - as you create both profile and user), then you can reverse...
modelBuilder.Entity<User>()
.HasRequired(x => x.Profile)
.WithOptional();
...and your ProfileId is actually in the Id (pk -> pk).
That solution worked for me
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasMaxLength(450);
entity.HasOne(d => d.Profile).WithOne(p => p.User);
});
modelBuilder.Entity<UserProfile>(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasMaxLength(450);
entity.Property(e => e.Type)
.HasMaxLength(10)
.HasColumnType("nchar");
entity.HasOne(d => d.User).WithOne(p => p.Profile);
});
}

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

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.

How to Map a ValueObject Collection with Foreign Keys in FluentNHibernate

I've been looking all over for an example of this, but it seems pretty uncommon. Hopefully some NHibernate guru will know.
I have the following class which, by my understanding of Value Objects, is a Value Object. Assume every user has the ability to assign one or more tags to any Question (think Stack Overflow). The Tags don't need a primary key, but they do hold references to the User and Question, unlike most of the examples of ValueObjects I see out there.
public class Tag : ValueObject
{
public virtual User User { get; set; }
public virtual Question Question { get; set; }
public virtual string TagName { get; set; }
}
public class User
{
public virtual IList<Tag> Tags { get; set; }
}
public class Question
{
public virtual IList<Tag> Tags { get; set; }
}
Anyway, I am getting the following error:
{"The entity 'Tag' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)."}
I have the following Fluent NHibernate mapping for User and Question:
public void Override(AutoMapping<XXX> mapping)
{
mapping.HasMany(x => x.Tags).Component(c =>
{
c.Map(x => x.TagName);
c.Map(x => x.Question);
c.Map(x => x.User);
});
}
As always, any thought greatly appreciated.
Late Update: Okay, so, maybe this isn't a value object. It doesn't need an identity, but I guess it's not something that could be used in multiple places, either. Any way to handle this without forcing a useless Id field on my object?
Try this:
public void Override(AutoMapping<XXX> mapping)
{
mapping.HasMany(x => x.Tags).AsBag().Component(c =>
{
c.Map(x => x.TagName);
c.References(x => x.Question);
c.References(x => x.User);
});
}
but you cant query (list all) tags then because its a value object.