Mapping conflict Entity Framework - c#

I am using EF codeFirst in order to create my database.
I have 2 models Blog and Message with the below fields, I am getting a mapping conflict since Message table has a foreign key to the blog table and Also blog table has a foreign key with the lastMessage posted to the Message table.
public class Blog
{
public int BlogId { get; set; }
public string Topic{ get; set; }
public virtual Message LastMessage{ get; set; }
}
public class Message
{
public int MessageId { get; set; }
public string Text { get; set; }
public virtual Blog Blog { get; set; }
}
public class BlogMap : EntityTypeConfiguration<Blog>
{
public BlogMap()
{
// Primary Key
HasKey(t => t.BlogId);
// Properties
Property(t => t.BlogId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Table & Column Mappings
ToTable("Blog");
Property(t => t.Topic).HasColumnName("Topic").HasMaxLength(100);
// Relationships
HasOptional(t => t.LastMessage)
.WithRequired(t => t.Blog)
.Map(t => t.MapKey("LastMessageId"));
}
}
public class MessageMap : EntityTypeConfiguration<Message>
{
public MessageMap()
{
// Primary Key
HasKey(t => t.MessageId);
// Properties
Property(t => t.MessageId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Table & Column Mappings
ToTable("Message");
Property(t => t.Text).HasColumnName("Text").HasMaxLength(100);
// Relationships
HasRequired(t => t.Blog)
.WithOptional()
.Map(t => t.MapKey("BlogId"));
}
}
Exception:
{"The navigation property 'Blog' declared on type 'MyProject.DAL.Model.Message' has been configured with conflicting mapping information."}

The problem is that you have mapped the "Blog" property of the Message class twice. In the BlogMap class, you define it here:
HasOptional(t => t.LastMessage)
.WithRequired(t => t.Blog)
.Map(t => t.MapKey("LastMessageId"));
This tells EF that the Blog property is the "return" side of the LastMessageId relationship.
In the MessageMap class, you define it here:
HasRequired(t => t.Blog)
.WithOptional()
.Map(t => t.MapKey("BlogId"));
This says that the Blog property represents the BlogId relationship.
I suspect the second one is the one you actually want, and the WithRequired() line of the first relationship should be replaced with .WithOptional() like this:
HasOptional(t => t.LastMessage)
.WithOptional()
.Map(t => t.MapKey("LastMessageId"));

Below is the fix, I removed the navigation property on the other side of the relationship using WithMany()
Add BlogId and MessageId to Message and Blog tables
Blog Mapping:
HasOptional(t => t.LastMessage).WithMany().HasForeignKey(d => d.MessageId);
// Relationships
HasOptional(t => t.LastMessage)
.WithRequired(t => t.Blog)
.Map(t => t.MapKey("LastMessageId"));
Message Mapping:
HasOptional(t => t.Blog).WithMany().HasForeignKey(d => d.BlogId);
// Relationships
HasRequired(t => t.Blog)
.WithOptional()
.Map(t => t.MapKey("BlogId"));

Related

One to Zero Relationship not working?

I have been trying to establish an optional One-to-One relationship between 2 entities unsuccessfully. I can do it by creating a Unique Constraint on the RTUDEVICE tables DeviceId, but I am trying to do it the "right way" through the Fluent API
What am I doing wrong?
Relationship Explanation:
One DEVICE record may have one-and-only-one record in the RTUDEVICE table.
ENTITIES:
Below are simplified versions of the actual classes...
public partial class Device
{
public Int Id { get; set; }
public string DeviceName { get; set; }
public virtual RTUDevice RTUDevice { get; set; }
}
public partial class RTUDevice
{
public Int Id { get; set; }
public int DeviceId { get; set; }
public bool IsCRMAlarmDevice { get; set; }
public bool HasCustomRegisters { get; set; }
public bool HasGasQualityRegisters { get; set; }
public bool HistoryVerified { get; set; }
public virtual Device Device { get; set; }
}
MAPPING:
I did many attempts using various online example without success...the failing code is commented-out.
public DeviceMap(DbModelBuilder modelBuilder)
{
ToTable("Device", "dbo")
.HasKey(m => m.Id);
Property(m => m.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsRequired();
Property(m => m.DeviceName)
.IsUnicode(false)
.HasMaxLength(100)
.IsRequired()
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UX_Device_AlternateKey") { IsUnique = true }));
//modelBuilder.Entity<RTUDevice>()
// .HasOptional(e => e.Device)
// .WithRequired(e => e.RTUDevice)
// .WillCascadeOnDelete(true);
}
public RTUDeviceMap(DbModelBuilder modelBuilder)
{
ToTable("RTUDevice", "dbo")
.HasKey(m => m.Id);
Property(m => m.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
.IsRequired();
Property(e => e.DeviceId)
.IsRequired();
Property(e => e.IsCRMAlarmDevice )
.IsRequired();
Property(e => e.HasCustomRegisters)
.IsRequired();
Property(e => e.HasGasQualityRegisters)
.IsRequired();
Property(e => e.HistoryVerified)
.IsRequired();
// One-to-Zero-or-One relationship
//modelBuilder.Entity<RTUDevice>()
// .HasOptional(e => e.Device)
// .WithRequired(e => e.RTUDevice)
// .WillCascadeOnDelete(true);
// One-to-Zero-or-One relationship
//modelBuilder.Entity<RTUDevice>()
// .HasRequired(e => e.Device)
// .WithMany()
// .HasForeignKey(c => c.DeviceId)
// .WillCascadeOnDelete(true);
}
You should just use Id as the PK (as well as FK) in RTUDevice entity and get rid of DeviceId. Then define the relationship as follows:
modelBuilder.Entity<Device>()
.HasKey(it => it.Id);
modelBuilder.Entity<RTUDevice>()
.HasKey(it => it.Id);
modelBuilder.Entity<Device>()
.HasOptional(it => it.RTUDevice)
.WithRequired(it => it.Device);
If DeviceId needs to be used explicitly for mapping, use the following:
modelBuilder.Entity<Device>()
.HasOptional(it => it.RTUDevice)
.WithOptionalPrincipal(it => it.Device)
.Map(it => it.MapKey("DeviceId"));

Setting up foreign keys in EF Code First to existing SQL Server database

I have a comment class that contains a user object for the comment author / last person to modify the comment.
public class Comment
{
public int CommentId { get; set; }
public int SignOffId { get; set; }
public string CommentText { get; set; }
public int LastModifiedByUserId { get; set; }
public DateTime LastModifiedOnDate { get; set; }
public virtual User LastModifiedByUser { get; set; }
}
I'm trying to setup the relationship in the CommentMap class but I can't figure out how to do it without putting a virtual Comment property in the User class. But I don't want this because it doesn't make sense from the business logic for the User class to have a Comment object.
LastModifiedByUserId is the foreign key in the Comments table pointing to the User table.
Here's the CommentMap ctor.
public CommentMap() {
// Primary Key
this.HasKey(t => t.CommentId);
// Properties
this.Property(t => t.CommentText)
.IsRequired()
.IsMaxLength();
this.Property(t => t.LastModifiedByUserId)
.IsRequired();
this.Property(t => t.LastModifiedOnDate)
.IsRequired();
// Table & Column Mappings
this.ToTable("Comments");
this.Property(t => t.CommentId).HasColumnName("CommentId");
this.Property(t => t.SignOffId).HasColumnName("SignOffId");
this.Property(t => t.CommentText).HasColumnName("CommentText");
this.Property(t => t.LastModifiedByUserId).HasColumnName("LastModifiedByUserId");
this.Property(t => t.LastModifiedOnDate).HasColumnName("LastModifiedOnDate");
// Relationships
this.HasRequired(c => c.LastModifiedByUser)
.WithRequiredDependent(u => u.UserId) //This doesn't work
.HasForeignKey(c => c.LastModifiedByUserId);
}
It wants an entity in the WithRequiredDependent line, not an integer. Is this completely the wrong way to setup this relationship? When I pull a comment from the db I want it to also grab the User object for the person who last modified the comment.
I don't think that a user can only modify one comment. Because that's what your model expresses. The combination HasRequired - WithRequiredDependent expresses a 1:1 relationship. It should be 1:n, as follows:
this.HasRequired(c => c.LastModifiedByUser)
.WithMany()
.HasForeignKey(c => c.LastModifiedByUserId);

Entity Framework code first unique index for key and property

I have two tables, Configuration and Device. Device has a string property called Name. A configuration can have many devices. I want an index on the Name property, and the name must be unique within a configuration. So I want an index on multiple columns in the Device table, namely on Configuration_ID and Name.
class Configuration
{
Guid ID { get; set; }
List<Device> Devices { get; set;
}
class Device
{
Guid ID { get; set; }
string Name { get; set;
}
class ConfigurationMap : EntityTypeConfiguration<Configuration>
{
ConfigurationMap()
{
this.HasKey(t => t.ID);
this.Property(t => t.ID).HasColumnName("ID");
this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.HasMany(t => t.Devices).WithRequired().WillCascadeOnDelete(true);
}
}
class DeviceMap
{
DeviceMap()
{
this.HasKey(t => t.ID);
this.Property(t => t.ID).HasColumnName("ID");
this.Property(t => t.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(t => t.DisplayName)
.HasColumnName("Name")
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("NameIndex")))
.HasMaxLength(450);
// Where to put the other index?
}
}
My two questions are:
Is this a good idea at all? ;)
How can I do that using fluent API?
Add ConfigurationId property to Device class (foreign key). Then, in cofiguration, for both ConfigurationId and Name properties do:
Property(t => t.Name).HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IDX_ConfigurationId_Name", 0) { IsUnique = true }));
Property(t => t.ConfigurationId).HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IDX_ConfigurationId_Name", 1) { IsUnique = true }));

Fluent configuration for a collection and single property of same type

I have 2 classes, Type A has a single instance of Type B as well as a collection of Type B. I have tried various configurations but I cannot seem to get it to work correctly. If you could explain to me what I am doing wrong or give me a resource that would be helpful. I am getting better with these mappings but now and then they reach beyond my understanding apparently. I have narrowed down my classes to just the important properties.
Clarification
There will only ever be one sweepstakes related to an applicant, the applicant though will be located in the collection of applicants, and the winning entry will have the ID populated in the WinnerId field which I was hoping EF would map to the correct applicant.
Error
The navigation property 'Sweepstakes' declared on type 'NPlay.Common.Models.SweepstakesApplicant' has been configured with conflicting mapping information.
Classes
public SweepstakesConfiguration()
{
Property(c => c.Id).HasColumnName("SweepstakesId");
HasMany(c => c.Applicants)
.WithRequired(c => c.Sweepstakes)
.HasForeignKey(c => c.SweepstakesId);
HasOptional(c => c.WinningApplicant)
.WithRequired(c => c.Sweepstakes)
.Map(c => c.MapKey("WinnerId"));
}
public class SweepstakesApplicant
{
public long Id { get; set; }
public int SweepstakesId { get; set; }
public virtual Sweepstakes Sweepstakes { get; set; }
public int BuyerId { get; set; }
public virtual Buyer Buyer { get; set; }
public int AgentId { get; set; }
public virtual Agent Agent { get; set; }
}
Mappings
public SweepstakesConfiguration()
{
Property(c => c.Id).HasColumnName("SweepstakesId");
HasOptional(c => c.WinningApplicant)
.WithRequired(c => c.Sweepstakes)
.Map(c => c.MapKey("WinnerId"));
}
public SweepstakesApplicantConfiguration()
{
Property(a => a.Id).HasColumnName("SweepstakesApplicantId");
HasRequired(a => a.Sweepstakes)
.WithMany(s => s.Applicants)
.HasForeignKey(a => a.SweepstakesId)
.WillCascadeOnDelete();
HasRequired(c => c.Sweepstakes)
.WithOptional(c => c.WinningApplicant)
.Map(c => c.MapKey("SweepstakesId"));
HasRequired(a => a.Buyer)
.WithMany(b => b.SweepstakesApplications)
.HasForeignKey(a => a.BuyerId);
HasRequired(a => a.Agent)
.WithMany()
.HasForeignKey(a => a.AgentId);
}
Edit: Updated mappings config and error.
Edit: Fixed title further, amazing how many edits I have received, I wonder if this site would be better if people spent more time answering questions then just going around editing :D, hmm this will probably be edited as well.
You are using the same foreign key (SweepstakesId) in both the one to zero-or-one and one-to-many relation ships.
Either create a collection on the SweepstakesApplicant called WinnerOfSweepstakes or similar and say hasMany, or remove the navigation property from the mapping.
This is the configuration I ended up with.
public SweepstakesConfiguration()
{
Property(c => c.Id).HasColumnName("SweepstakesId");
HasOptional(c => c.WinningApplicant)
.WithMany()
.HasForeignKey(c => c.WinnerId);
}
public SweepstakesApplicantConfiguration()
{
Property(a => a.Id).HasColumnName("SweepstakesApplicantId");
HasRequired(a => a.Sweepstakes)
.WithMany(s => s.Applicants)
.HasForeignKey(a => a.SweepstakesId)
.WillCascadeOnDelete();
HasRequired(a => a.Buyer)
.WithMany(b => b.SweepstakesApplications)
.HasForeignKey(a => a.BuyerId);
HasRequired(a => a.Agent)
.WithMany()
.HasForeignKey(a => a.AgentId);
}

Add/Insert entity with inherited nested object graph (one to many relationship)

Adding an entity with 1 level of one to many relationship is pretty straight forward.
using (var dbCtx = new DbContext())
{
dbCtx.Stuff.Add(myObject);
dbCtx.SaveChanges();
}
But how do you add an object with 2 levels? Adding it in the same way omits the 2. level. Which means that the Bar objects (in the example below) isn't saved. What am I doing wrong?
Object graph
Inherited objects
public class BaseEntity
{
public int Id;
// Omitted properites...
}
public class MyEntity : BaseEntity
{
// Omitted properites...
// Navigation properties
public virtual ICollection<Foo> Foos { get; set; }
}
Nested objects (1:M)
public class Foo // 1. level
{
public int Id;
public int MyEntityId;
// Omitted properites...
// Navigation properties
public virtual ICollection<Bar> Bars { get; set; }
public virutal MyEntity MyEntity { get; set; }
}
public class Bar // 2. level
{
public int Id;
public int FooId;
// Omitted properites...
// Navigation properties
public virutal Foo Foo { get; set; }
}
Mapping setup using fluent API
Inherited objects
public class BaseEntityMap : EntityTypeConfiguration<BaseEntity>
{
public BaseEntityMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
// Table & Column Mappings
this.ToTable("BaseEntitySet");
this.Property(t => t.Id).HasColumnName("Id");
// ...
}
}
public class MyEntityMap : EntityTypeConfiguration<MyEntity>
{
public MyEntityMap()
{
// Table & Column Mappings
this.ToTable("BaseEntitySet_MyEntities");
}
}
Nested objects (1:M)
public class FooMap : EntityTypeConfiguration<Foo>
{
public FooMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("FooSet");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.MyEntityId).HasColumnName("MyEntity_Id");
// Relationships
this.HasRequired(t => t.MyEntity)
.WithMany(t => t.Foos)
.HasForeignKey(d => d.MyEntityId);
}
}
public class BarMap : EntityTypeConfiguration<Bar>
{
public BarMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("BarSet");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.FooId).HasColumnName("Bar_Id");
// Relationships
this.HasRequired(t => t.Foo)
.WithMany(t => t.Bars)
.HasForeignKey(d => d.FooId);
}
}
Repository
public void Add(BaseEntity item)
{
using (var ctx = new DbContext())
{
ctx.BaseEntities.Add(item);
ctx.SaveChanges();
}
}
\Wanted to post my answer in case it helps others, and so the experts can tear it to pieces and post the real answer. For me it did not start working out of the blue :)
If I understand your object graph correctly, it's MyEntity has Foos which has Bars. I had a similar structure, but when calling "SaveChanges" a DbUpdateException would be thrown with a message that
"multiple entities may have the same primary key."
Here's how I made it work for me:
Step 1: Change the Id properties from int to int? and initialize them to null. To me this is a more accurate model than a plain integer. When an entity is new, the ID is literally "undefined or unknown". 0 is a defined number and for some reason EF has a problem with the ID's being the same, even when the records are being added.
public class BaseEntity
{
public BaseEntity()
{
this.Id = null;
}
public int? Id;
// Omitted properites...
}
public class Foo
{
public Foo()
{
this.Id = null;
}
public int? Id;
}
public class Bar
{
public Bar()
{
this.Id = null;
}
public int? Id;
}
Step 2: Add the "DatabaseGeneratedOption.Identity" flag to the Id properties in the mapping. I believe this prevents it from being "required" in the case the entity is added to the datacontext.
public class BaseEntityMap : EntityTypeConfiguration<BaseEntity>
{
public BaseEntityMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
// Table & Column Mappings
this.ToTable("BaseEntitySet");
this.Property(t => t.Id).HasColumnName("Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// ...
}
}
public class FooMap : EntityTypeConfiguration<Foo>
{
public FooMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("FooSet");
this.Property(t => t.Id).HasColumnName("Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(t => t.MyEntityId).HasColumnName("MyEntity_Id");
// Relationships
this.HasRequired(t => t.MyEntity)
.WithMany(t => t.Foos)
.HasForeignKey(d => d.MyEntityId);
}
}
public class BarMap : EntityTypeConfiguration<Bar>
{
public BarMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Table & Column Mappings
this.ToTable("BarSet");
this.Property(t => t.Id).HasColumnName("Id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(t => t.FooId).HasColumnName("Bar_Id");
// Relationships
this.HasRequired(t => t.Foo)
.WithMany(t => t.Bars)
.HasForeignKey(d => d.FooId);
}
}

Categories

Resources