I'm trying to map the following tables/entities in FNH and seem to be getting nowhere fast!
**Tables**
Contacts
ID (PK - int - generated)
...
PhoneTypes
ID (PK - varchar - assigned) (e.g. MOBILE, FAX)
ContactPhones
ContactRefId (PK - FK to Contacts)
PhoneTypeRefId (PK - FK to PhoneTypes)
...
(I should note that I am also using the S#arp Architecture framework)
**Entities**
public class Contact : Entity
{
(The ID property is defined in the Entity base class and is type int)
public virtual ICollection<ContactPhone> PhoneNumbers { get; set; }
}
public class PhoneType : EntityWithTypedId<string>, IHasAssignedId<string>
{
(The ID property is defined in the base class and is type string)
....
}
public class ContactPhone : EntityWithTypedId<ContactPhoneId>, IHasAssignedId<ContactPhoneId>
{
public virtual Contact Contact { get; set; }
public virtual PhoneType PhoneType { get; set; }
....
}
I read that it is advisable when working with composite ids, to separate the composite id into a different class.
hibernate composite key
public class ContactPhoneId : EntityWithTypedId<ContactPhoneId>, IHasAssignedId<ContactPhoneId>
{
public virtual Contact Contact { get; set; }
public virtual PhoneType PhoneType { get; set; }
}
...I could just make this class serializable and override
Equals and GetHashCode myself instead of using the S#arp Arch base class.
I've tried so many combinations of mappings that I'm now completely confused.
This is my latest shot:
public class ContactMap : IAutoMappingOverride<Contact>
{
public void Override(AutoMapping<Contact> mapping)
{
mapping.HasMany<ContactPhone>(x => x.PhoneNumbers)
.KeyColumns.Add("ContactRefId")
.KeyColumns.Add("PhoneTypeRefId")
.AsSet()
.Inverse()
.Cascade.All();
}
}
public class PhoneTypeMap : IAutoMappingOverride<PhoneType>
{
public void Override(AutoMapping<PhoneType> mapping)
{
mapping.Id(x => x.Id).Column("Id").GeneratedBy.Assigned();
}
}
public class ContactPhoneMap : IAutoMappingOverride<ContactPhone>
{
public void Override(AutoMapping<ContactPhone> mapping)
{
mapping.Table("ContactPhones");
mapping.CompositeId<ContactPhoneId>(x => x.Id)
.KeyReference(y => y.Contact, "ContactRefId")
.KeyReference(y => y.PhoneType, "PhoneTypeRefId");
}
}
I've had many exceptions thrown when trying to generate the mappings, the latest of which is:
Foreign key (FK672D91AE7F050F12:ContactPhones [ContactRefId, PhoneTypeRefId]))
must have same number of columns as the referenced primary key (Contacts [Id])
Does anyone see anything obvious that I'm doing wrong? I'm new to NH and FNH, which may be obvious from this post. :-) Also, has anyone used Composite Ids like this while using S#arp Architecture? What are the best practices (other than to use surrogate keys :-) ) ?
Many thanks...and sorry about the long post.
I have a many to many relationship too. I've got mine setup like this:
mapping.HasManyToMany(x => x.Artists).Cascade.All().Inverse().Table("ArtistImages");
The ArtistImages table has primary keys for tables Artists and Images.
Related
Edit 1: So I wasn't getting anywhere and tried something different. I renamed my FriendRequest class to FriendRequestBase and made it abstract and then I made a FriendRequest class that inherits from FriendRequestBase without any extra fields, so now the inheritance looks like this:
FriendRequest : FriendRequestBase
GroupModRequest : FriendRequestBase
That should work, right? Well now, I added a migration to see if it will add any changes and yes, for some reason it now starts dropping foreign keys.
DropForeignKey("dbo.GroupModRequests", "SenderID", "dbo.AspNetUsers");
DropForeignKey("dbo.GroupModRequests", "ReceiverID", "dbo.AspNetUsers");
What could be the problem there? I've correctly set up the foreign keys with the Fluent API:
modelBuilder.Entity<GroupModRequest>()
.HasRequired(r => r.Receiver)
.WithMany()
.HasForeignKey(k => k.ReceiverID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<GroupModRequest>()
.HasRequired(r => r.Sender)
.WithMany()
.HasForeignKey(k => k.SenderID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<GroupModRequest>()
.HasRequired(r => r.Group)
.WithMany(g => g.GroupModRequests)
.HasForeignKey(k => k.GroupID)
.WillCascadeOnDelete(true);
I'm trying to seed my database after running 'update-database' and this is what I'm getting:
All objects in the EntitySet 'ApplicationDbContext.FriendRequests'
must have unique primary keys. However, an instance of type
'AW.Models.GroupModRequest' and an instance of type
'AW.Models.FriendRequest' both have the same primary key value,
'EntitySet=FriendRequests;SenderID=a9540bd6-8532-4c7a-9f68-19d2aeecffcb;ReceiverID=8f50eccf-8ccf-432e-a033-82d933b5e3f5'.
As I mentioned previously, FriendRequests are in one table and GroupModRequests are in a different table so this error doesn't make sense. I can kind of guess why this happens, though.
GroupModRequests inherits from FriendRequests and I'm using the Table-Per-Conrete-Class approach, meaning that FriendRequests has a table and GroupModRequests has a table and it has all of the properties FriendRequests have plus some more. Note, that FriendRequests isn't an abstract class.
FriendRequests has a composite primary key consisting of: SenderID, ReceiverID.
GroupModRequests has a composite primary key consisting of: SenderID, ReceiverID and GroupID.
And so I bet that for some reason it is ignoring the 'GroupID' part of the key and ignoring the fact that they are in different tables and throws this error when a FriendRequest has the same Sender/Receiver ID as a GroupModRequest.
So how can I fix this in EF6? I already set this with fluent, but apparently that isn't enough:
modelBuilder.Entity<GroupModRequest>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("GroupModRequests");
});
The composite keys look like this:
public class FriendRequest
{
[Key, Column(Order = 1)]
public string SenderID { get; set; }
[ForeignKey("SenderID")]
public virtual ApplicationUser Sender { get; set; }
[Key, Column(Order = 2)]
public string ReceiverID { get; set; }
[ForeignKey("ReceiverID")]
public virtual ApplicationUser Receiver { get; set; }
}
public class GroupModRequest : FriendRequest
{
[Key, Column(Order = 3)]
public Guid GroupID { get; set; }
public virtual Group Group { get; set; }
}
Note: I also have 'LocationModRequests' which inherits from FriendRequests as well and is almost the same as GroupModRequests, if it matters.
I'm having issues with Nhibernate persisting a HasOne Relationship for one of my entities with Cascade.None() in effect. My domain model involves 4 classes listed below.
public class Project
{
public virtual int Id {get;set;}
public virtual IList<ProjectRole> Team { get; protected set; }
}
public class ProjectRole
{
public virtual int Id { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
public class Role
{
public virtual int Id { get; set; }
public virtual string Value { get; set; }
}
public class User
{
public virtual int Id { get; protected set; }
public virtual string LoginName { get; set; }
}
So basically we have projects, which have a list of ProjectRoles available from the Team property. Each ProjectRole links a User to the specific Role they play on that project.
I'm trying to setup the following cascade relationships for these entities.
project.HasMany<ProjectRoles>(p=> p.Team).Cascade.All()
projectRole.HasOne<Role>(r => r.Role).Cascade.None()
projectRole.HasOne<User>(r => r.User).Cascade.SaveUpdate()
I've used fluent nhibernate overrides to setup the cascades as above, but I'm finding that the line
projectRole.HasOne<Role>(r => r.Role).Cascade.None()
is resulting in the ProjectRole.Role property not being saved to the database. I've diagnosed this be looking at the SQL Generated by Nhibernate and I can see that the "Role_id" column in the ProjectRoles table is never set on update or insert.
I've also tried using
projectRole.HasOne<Role>(r => r.Role).Cascade.SaveUpdate()
but that fails as well. Unfortunately leaving it Cascade.All() is not an option as that results in the system deleting the Role objects when I try to delete a project role.
Any idea how to setup Cascade.None() for the ProjectRole-> Role relationship with out breaking persistence.
HasOne is for a one-to-one relationship which are rare. You want to use References to declare the one side of a one-to-many relationship. Making some assumptions about your domain model, the mapping should look like:
project.HasMany<ProjectRoles>(p=> p.Team).Inverse().Cascade.AllDeleteOrphan()
projectRole.References<Role>(r => r.Role);
projectRole.References<User>(r => r.User);
See also this question about the difference between HasOne and References.
I am attempting to use NHibernate to generate a model for a very odd database. The tables themselves have primary keys for show only, all the actual relationships are on unique columns. For example, a product table with a product id primary key and a unique product name column. Another table, demand, has a product name column and that defines the relationship. I know this situation isn't ideal but it's out of my control.
At any rate, I was able to use Fluent NHibrenate to map product to demand, but I cannot seem to get the entity to lazy-load.
public class Demand
{
public virtual DemandId { get; set; }
public virtual Product { get; set; }
}
public class DemandMap : ClassMap<Demand>
{
public DemandMap()
{
this.Table("Demand");
this.LazyLoad();
this.Id(x => x.DemandId);
this.References(x => x.Product).PropertyRef(x => x.ProductName).LazyLoad();
}
}
Does anyone have any insight into why lazy loading is not working? I know it is not because I can see the product being fetched along with the demand in the SQL profiler.
My idea (Maybe you can try use "HasMany" there is example but you can read something about this):
First class
public class Demand
{
public virtual int DemandId { get; set; }
public virtual int Product { get; set; }
public virtual IEnumerable<NewClass> Name {get; set;}
}
this.HasMany(x=> x.Product).Column("Product_id").not.nullable;
Second class
public class NewClass
{
public virtual Demand Product_id {get; set;}
}
this.References(x => x.Product).Column("product_id).not.nullable
We have a multi-tenant database - i.e. common database used by multiple clients, hence each table will have a "ClientID" column indicating the Tenant.
We are using Fluent NHibernate for ORM and looking for the best approach to tackle multi-tenancy: each Mapping class needs to map ClientID, however, the value would come from the User Account object - i.e. from User Session of some sort.
Is there a nice and easy way to achieve this using Fluent NHibernate? If so, could you provide an example?
I'm not entirely sure if this is what your asking but if you need each class to map to the ClientId, here's one example.
Basically, each class would have a UserAccount property or whatever class is going to store the user's account information that has the ClientId property on it. Then in your Fluent NHibernate mapping, you can map the classes together using the References() method. See example below:
public class UserAccount
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Bill> Bills { get; set; }
}
public class Bill
{
public virtual int BillId { get; set; }
public virtual UserAccount User { get; set; }
}
public class UserAccount : ClassMap<UserAccount>
{
public UserAccount()
{
Id( x => x.Id ).Column( "ClientId" );
Map( x => x.Name );
HasMany( x => x.Bills );
}
}
public class BillMap : ClassMap<Bill>
{
public BillMap()
{
Id( x => x.Id ).Column( "BillId" );
References( x => x.User ).Column( "ClientId" );
}
}
So in your Bill table, you would have a ClientId column which in database terms is really a foreign key referencing the UserAccount table's primary key column which would also be named ClientId.
If you are truly going to have a large amount of tables that are all going to have a ClientId column, you also have the option of abstracting that out to a base class that your entities inherit from that would already have the UserAccount property on it. You could do the same base class approach for your Fluent NHibernate mapping files as well.
I'm trying to use the table-per-subclass (which fluent-nhibernate automaps by default) with a class structure like the following:
public class Product
{
public virtual int Id{ get; set; }
public virtual string Title{ get; set; }
}
public class ProductPackage : Product
{
public ProductPackage(){ Includes = new List<Product>(); }
public virtual IList<Prodcut> Includes{ get; private set; }
[EditorBrowsable( EditorBrowsableState.Never )]
public class ProductPackageAutoOverride : IAutoMappingOverride<ProductPackage>
{
public void Override( AutoMap<ProductPackage> mapping )
{
mapping.HasManyToMany( x => x.Includes )
.WithTableName( "IncludesXProduct" )
.WithParentKeyColumn( "ProductId" )
.WithChildKeyColumn( "IncludesProductId" )
.Cascade.SaveUpdate();
}
}
}
Instead of adding a new table "IncludesXProduct" to represent the many-to-many mapping, it adds a property "ProductPackageId" to the Product table. Of course persisting to this schema doesn't work.
Have I missed something simple or is this type of thing not really supported by NHibernate?
It is possible to do this with NHibernate. Unfortunately my fluent syntax isn't very good, but it looks like FNH is somehow regarding the relationship as a many-to-one rather than a many-to-many.
If you tag your question with "fluent-nhibernate" then you may get more knowledgeable people answering.