Entity Framework code first unique index for key and property - c#

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 }));

Related

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);

One to one relationship Entity Framework foreign key mapping

I am working on assigning a relationship between two objects but I am getting an error on the mapping.
I have the following objects:
public abstract class EntityBase : IEntity
{
public int Id { get; set; }
}
public class ManagerUser : EntityBase
{
public string UserCode { get; set; }
public string Title { get; set; }
public virtual Staff StaffDetails { get; set; }
}
public class Staff : EntityBase
{
public string UserCode { get; set; }
public string DOB { get; set; }
}
public class ManagerUserMap : EntityMapBase<ManagerUser>
{
protected override void SetupMappings()
{
base.SetupMappings();
//this.HasKey(t => t.UserCode);
this.ToTable("ManagerUsers");
this.Property(t => t.Id).HasColumnName("ManagerUsersID")
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
this.Property(t => t.Title).HasColumnName("txtTitle");
this.HasRequired(x => x.StaffDetails)
.WithMany()
.HasForeignKey(x => x.UserCode);
}
}
public class StaffMap : EntityMapBase<Staff>
{
protected override void SetupMappings()
{
base.SetupMappings();
//this.HasKey(t => t.UserCode);
this.ToTable("TblStaff");
this.Property(t => t.Id).HasColumnName("StaffID")
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
this.Property(t => t.UserCode).HasColumnName("User_Code");
this.Property(t => t.DateOfBirth).HasColumnName("dob");
}
}
I am getting the following error:
The type of property 'UserCode' on entity 'ManagerUser' does not match
the type of property 'Id' on entity 'Staff' in the referential
constraint 'ManagerUser_StaffDetails'
I have searched around and failed to find a solution to get it to compare the the foreign key in ManagerUser with the UserCode property in Staff instead of the ID property.
Your keys are a bit messed up in the fluent config, HasKey sets the primary key for the entity. Below I have set the primary key to be Id for both entities. Then use the Usercode as a FK:
public class ManagerUserMap : EntityMapBase<ManagerUser>
{
protected override void SetupMappings()
{
base.SetupMappings();
this.HasKey(t => t.Id);
this.ToTable("ManagerUsers");
this.Property(t => t.Id).HasColumnName("ManagerUsersID")
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
this.Property(t => t.Title).HasColumnName("txtTitle");
this.HasRequired(x => x.StaffDetails)
.WithMany()
.HasForeignKey(x => x.UserCode);
}
}
public class StaffMap : EntityMapBase<Staff>
{
protected override void SetupMappings()
{
base.SetupMappings();
this.HasKey(t => t.Id);
this.ToTable("TblStaff");
this.Property(t => t.Id).HasColumnName("StaffID")
.HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
this.Property(t => t.UserCode).HasColumnName("User_Code");
this.Property(t => t.DateOfBirth).HasColumnName("dob");
}
}

Mapping conflict Entity Framework

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"));

Entity Framework Id column issue

I have a problem with EF at the moment.
I have an existing database and in there is a custom User table called Profiles.
The user is below (I have stripped out most of the properties for easy reading).
public partial class Profile : IdentityUser
{
public Profile()
{
this.Assets = new List<Asset>();
// ...
}
public string CompanyId { get; set; }
// ...
public virtual Company Company { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
// ...
}
and my DbContext looks like this (simplified):
public partial class SkipstoneContext : IdentityDbContext<Profile>
{
static SkipstoneContext()
{
Database.SetInitializer<SkipstoneContext>(null);
}
public SkipstoneContext()
: base("DefaultConnection")
{
}
public DbSet<Asset> Assets { get; set; }
// ...
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ...
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
modelBuilder.Entity<UserSecret>().HasKey<string>(r => r.UserName);
}
}
And I have a class that looks like this:
public Company()
{
this.Assets = new List<Asset>();
}
public string Id { get; set; }
public string Name { get; set; }
public string CreatedBy { get; set; }
public string ModifiedBy { get; set; }
public System.DateTime DateCreated { get; set; }
public Nullable<System.DateTime> DateModified { get; set; }
public bool Deleted { get; set; }
public virtual Profile CreatedByProfile { get; set; }
public virtual ICollection<Asset> Assets { get; set; }
}
The problem is that when I run my code, I get an error stating:
Invalid column name 'CreatedByProfile_Id'.
I need to tell the system that the Id column for my custom user is just Id.
I had a mapping file:
public class ProfileMap : EntityTypeConfiguration<Profile>
{
public ProfileMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Id)
.IsRequired()
.HasMaxLength(128);
this.Property(t => t.CompanyId)
.IsRequired()
.HasMaxLength(128);
this.Property(t => t.CreatedBy)
.IsRequired();
this.Property(t => t.Title)
.IsRequired();
this.Property(t => t.Forename)
.IsRequired();
this.Property(t => t.Surname)
.IsRequired();
this.Property(t => t.Email)
.IsRequired();
this.Property(t => t.CredentialId)
.IsRequired();
// Table & Column Mappings
this.ToTable("Profiles");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.CompanyId).HasColumnName("CompanyId");
this.Property(t => t.CreatedBy).HasColumnName("CreatedBy");
this.Property(t => t.ModifiedBy).HasColumnName("ModifiedBy");
this.Property(t => t.DateCreated).HasColumnName("DateCreated");
this.Property(t => t.DateModified).HasColumnName("DateModified");
this.Property(t => t.LastLoginDate).HasColumnName("LastLoginDate");
this.Property(t => t.Title).HasColumnName("Title");
this.Property(t => t.Forename).HasColumnName("Forename");
this.Property(t => t.Surname).HasColumnName("Surname");
this.Property(t => t.Email).HasColumnName("Email");
this.Property(t => t.JobTitle).HasColumnName("JobTitle");
this.Property(t => t.Telephone).HasColumnName("Telephone");
this.Property(t => t.Mobile).HasColumnName("Mobile");
this.Property(t => t.Photo).HasColumnName("Photo");
this.Property(t => t.LinkedIn).HasColumnName("LinkedIn");
this.Property(t => t.Twitter).HasColumnName("Twitter");
this.Property(t => t.Facebook).HasColumnName("Facebook");
this.Property(t => t.Google).HasColumnName("Google");
this.Property(t => t.Bio).HasColumnName("Bio");
this.Property(t => t.CompanyName).HasColumnName("CompanyName");
this.Property(t => t.CredentialId).HasColumnName("CredentialId");
this.Property(t => t.IsLockedOut).HasColumnName("IsLockedOut");
this.Property(t => t.IsApproved).HasColumnName("IsApproved");
this.Property(t => t.CanEditOwn).HasColumnName("CanEditOwn");
this.Property(t => t.CanEdit).HasColumnName("CanEdit");
this.Property(t => t.CanDownload).HasColumnName("CanDownload");
this.Property(t => t.RequiresApproval).HasColumnName("RequiresApproval");
this.Property(t => t.CanApprove).HasColumnName("CanApprove");
this.Property(t => t.CanSync).HasColumnName("CanSync");
this.Property(t => t.AgreedTerms).HasColumnName("AgreedTerms");
this.Property(t => t.Deleted).HasColumnName("Deleted");
this.Property(t => t.UserName).HasColumnName("UserName");
// Relationships
this.HasRequired(t => t.Company)
.WithMany(t => t.Users)
.HasForeignKey(d => d.CompanyId);
}
}
but if I add that to my DbContext class I get an error stating:
A configuration for type 'Models.Profile' has already been added. To
reference the existing configuration use the Entity() or
ComplexType() methods.
I assume this is simple to fix, so could someone point me in the right direction please?
Cheers,
/r3plica
The problem is at your Company class configuration.
Try:
public class CompanyMap : EntityTypeConfiguration&ltCompany>
{
public CompanyMap()
{
// Add this
this.HasRequired(x => x.CreatedByProfile).WithMany().Map(
x => x.MapKey("CreatedByProfileId"));
// CreatedByProfileId is the FK column in the Company table that points
// to the Profile table. This is my made up name for the column since
// I don't know the real name in your database.
}
}
If you don't have a configuration class for Company then in your OnModelCreating method you need:
modelBuilder.Entity&ltCompany>().HasRequired(x => x.CreatedByProfile).WithMany().Map(
x => x.MapKey("CreatedByProfileId"));
UPDATE
Since you already have the property in your class.
modelBuilder.Entity&ltCompany>().HasRequired(
x => x.CreatedByProfile).WithMany().HasForeignKey(x => x.CreatedBy);

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