Entity Framework 6 says two entries have the same primary key, while they are in completely different tables - sql

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.

Related

EF Core composite foreign key and constraint

In my project I have noticed that I will be have a lot of dictionaries with the same structure:
shortcut
full name for tooltip
which will be used on many different business forms.
I started to thing that there is no sense to keep all of them in separate tables.
It is better to keep all of them in one table and provide an additional column (DictionaryType) which will separate them in the case of asking the database for data?
So one repository with such method
public class DictionaryEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public string FullName { get; set; }
public DictionaryType Type { get; set; }
}
public async Task<IEnumerable<DictionaryEntity> GetDictionaries(DictionaryType type)
{
return await _dbContext.Dictionaries.Where(d => d.DictionaryType == type).ToArrayAsync();
}
If new dictionaries appear, I need to only extend DictionaryType and I don't need to worry about database changes or repo/service/controller changes.
For now it is nice and easy, but... I would like to configure foreign key in business entities in that way:
public class CarEntity
{
public Guid Id { get; set; }
public Guid ModelTypeId { get; set;}
public DictionaryEntity ModelType { get; set;}
public Guid PetrolTypeId { get; set;}
public DictionaryEntity PetrolType { get; set;}
}
How to configure in EF Core, foreign key in that way where:
CarEntity.ModelTypeId points to DictionaryEntity.Id and DictionaryEntity.Type = DctionaryType.ModelType ?
CarEntity.PetrolTypeId points to DictionaryEntity.Id and DictionaryEntity.Type = DctionaryType.PetrolType ?
I read, that there is something like a composite foreign key, so I could do FK on { dict.Name, dict.Type } but it demands from me to keep in CarEntity as many properties as composite foreign key have.
Is there a chance to do unique constraint across multiple tables ?
Something like this:
modelBuilder.Entity<CarEntity>()
.HasCheckConstraint("CK_ModelType", "[ModelTypeId] IS NOT NULL AND [Document].[Type] = 'ModelType'", c => c.HasName("CK_ModelType_Dictionary"));

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 Code First Class with parent and children of same type as it's own class

I have a class of Content which should be able to have a parentId for inheritance but also I want it to have a list of child content which is nothing to do with this inheritance tree.
I basically wanted a link table as ChildContentRelationship with Id's for parentContent and childContent in it and the Content class would have a list of ChildContentRelationship.
This has caused a lot of errors.
Here's waht I sort of want to do
public class Content
{
public int Id { get; set; }
public int? ParentContentId { get; set; }
public virtual Content ParentContent { get; set; }
public string Name { get; set; }
public int ContentTypeId { get; set; }
public virtual ContentType ContentType { get; set; }
public virtual ICollection<Property> Properties { get; set; }
public virtual ICollection<ChildContentRelationship> ChildContent { get; set; }
}
How would I set this up in EF?
I am not sure if I understand your model correctly. Let's discuss the options.
For a moment I omit this additional entity ChildContentRelationship and I assume the ChildContent collection is of type ICollection<Content>.
Option 1:
I assume that ParentContent is the inverse property of ChildContent. It would mean that if you have a Content with Id = x and this Content has a ChildContent with Id = y then the ChildContents ParentContentId must always be x. This would only be a single association and ParentContent and ChildContent are the endpoints of this same association.
The mapping for this relationship can be created either with data annotations ...
[InverseProperty("ParentContent")]
public virtual ICollection<Content> ChildContent { get; set; }
... or with Fluent API:
modelBuilder.Entity<Content>()
.HasOptional(c => c.ParentContent)
.WithMany(c => c.ChildContent)
.HasForeignKey(c => c.ParentContentId);
I think this is not what you want ("...has nothing to do with..."). Consider renaming your navigation properties though. If someone reads Parent... and Child... he will very likely assume they build a pair of navigation properties for the same relationship.
Option 2:
ParentContent is not the inverse property of ChildContent which would mean that you actually have two independent relationships and the second endpoint of both relationships is not exposed in your model class.
The mapping for ParentContent would look like this:
modelBuilder.Entity<Content>()
.HasOptional(c => c.ParentContent)
.WithMany()
.HasForeignKey(c => c.ParentContentId);
WithMany() without parameters indicates that the second endpoint is not a property in your model class, especially it is not ChildContent.
Now, the question remains: What kind of relationship does ChildContent belong to? Is it a one-to-many or is it a many-to-many relationship?
Option 2a
If a Content refers to other ChildContents and there can't be a second Content which would refer to the same ChildContents (the children of a Content are unique, so to speak) then you have a one-to-many relationship. (This is similar to a relationship between an order and order items: An order item can only belong to one specific order.)
The mapping for ChildContent would look like this:
modelBuilder.Entity<Content>()
.HasMany(c => c.ChildContent)
.WithOptional(); // or WithRequired()
You will have an additional foreign key column in the Content table in your database which belongs to this association but doesn't have a corresponding FK property in the entity class.
Option 2b
If many Contents can refer to the same ChildContents then you have a many-to-many relationship. (This is similar to a relationship between a user and roles: There can be many users within the same role and a user can have many roles.)
The mapping for ChildContent would look like this:
modelBuilder.Entity<Content>()
.HasMany(c => c.ChildContent)
.WithMany()
.Map(x =>
{
x.MapLeftKey("ParentId");
x.MapRightKey("ChildId");
x.ToTable("ChildContentRelationships");
});
This mapping will create a join table ChildContentRelationships in the database but you don't need a corresponding entity for this table.
Option 2c
Only in the case that the many-to-many relationship has more properties in addition to the two keys (ParentId and ChildId) (for example something like CreationDate or RelationshipType or...) you would have to introduce a new entity ChildContentRelationship into your model:
public class ChildContentRelationship
{
[Key, Column(Order = 0)]
public int ParentId { get; set; }
[Key, Column(Order = 1)]
public int ChildId { get; set; }
public Content Parent { get; set; }
public Content Child { get; set; }
public DateTime CreationDate { get; set; }
public string RelationshipType { get; set; }
}
Now your Content class would have a collection of ChildContentRelationships:
public virtual ICollection<ChildContentRelationship> ChildContent
{ get; set; }
And you have two one-to-many relationships:
modelBuilder.Entity<ChildContentRelationship>()
.HasRequired(ccr => ccr.Parent)
.WithMany(c => c.ChildContent)
.HasForeignKey(ccr => ccr.ParentId);
modelBuilder.Entity<ChildContentRelationship>()
.HasRequired(ccr => ccr.Child)
.WithMany()
.HasForeignKey(ccr => ccr.ChildId);
I believe that you want either option 2a or 2b, but I am not sure.

Fluent NHibernate Mapping - Composite Key

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.

Add multi-column unique constraint on foreign Key using fluent-nhibernate automapping

I'm an NHibernate and fluent-nhibernate newbie. And I've got some problem with unique constraint and nhibernate mapping.
I've got the following part of domain model.
public class Batch
{
public virtual int Id {get; set;}
public virtual string Name {get; set;}
public virtual IList<BatchParameter> BatchParameters {get; set;}
}
public class BatchParameter
{
public virtual int Id {get; set;}
public virtual string Name {get; set;}
public virtual Batch Batch {get; set;}
}
I'm trying to use fluent-nhibernate to map it on the db (SQLServer) using automapping.
I want to be set up my db in order to have :
Primary Keys on the "Id"s properties
a Foreign Key on the BatchParamets table
a Unique Constraint on the Batch table on column Name
a Unique Constraint on the BatchParameters table on columns Name and Batch_Id
So I've written down this code:
public class BatchMapping : IAutoMappingOverride<Batch>
{
public void Override(FluentNHibernate.Automapping.AutoMapping<Batch> mapping)
{
mapping.Id( b => b.Id);
mapping.HasMany<BatchParameter>(p => p.BatchParameters).Cascade.All().Inverse();
}
}
public class BatchParameterMapping : IAutoMappingOverride<BatchParameter>
{
public void Override(FluentNHibernate.Automapping.AutoMapping<BatchParameter> mapping)
{
mapping.Id( b => b.Id);
mapping.Map(b => b.Name).Unique();
//mapping.Map(p => p.Name).UniqueKey("Batch_Parameter");
//mapping.Map(p => p.Batch.Id).UniqueKey("Batch_Parameter");
}
}
No problems for the primary keys, the foreign key and the first Unique Constraint. A little bit of headache for the Unique Constraint.
Can someone show me the straight way???
Thanks!
First, it looks like you have a copy-and-paste error: ...Map(b => b.Name)... should go in BatchMapping, not BatchParameterMapping.
public class BatchMapping : IAutoMappingOverride<Batch>
{
public void Override(AutoMapping<Batch> mapping)
{
mapping.Map(b => b.Name).Unique();
}
}
Next, BatchParameter.Batch is a many-to-one relationship from BatchParameter to Batch, so it should be mapped with References(...) instead of Map(...). You use References for foreign keys to another entity and use Map for simple properties.
public class BatchParameterMapping : IAutoMappingOverride<BatchParameter>
{
public void Override(AutoMapping<BatchParameter> mapping)
{
mapping.Map(p => p.Name).UniqueKey("Batch_Parameter");
mapping.References(p => p.Batch).UniqueKey("Batch_Parameter");
}
}
Finally, you should remove the unnecessary mappings for the Id properties and Batch.BatchParameters. Fluent NHibernate's auto-mapping will map them as desired by default. In your Override methods you only need to specify the properties where you want to do something differently than the auto-mapping default, such as specifying unique keys.
If Id and Name are primary keys in your BatchParameter table you would need a composite id. Also if you want to have a reference back to Batch from your BatchParameter class you will need to use Reference. The following should be close to what you need:
public class BatchParameterMapping : IAutoMappingOverride<BatchParameter>
{
public void Override(FluentNHibernate.Automapping.AutoMapping<BatchParameter> mapping)
{
mapping.CompositeId()
.KeyProperty(x => x.Id)
.KeyProperty(x => x.Name);
mapping.References(x => x.Batch);
}
}