I have a solution that I have been creating for a while and I have used EF Code First migrations to create the database. Recently for another solution my supervisor ran some scripts on the database that added a few tables. Now, I need to connect to those tables in my solution. I have this code here:
public class RoleMap : EntityTypeConfiguration<Role>
{
public RoleMap()
{
//Primary key
this.HasKey(t => t.RoleId);
this.Property(t => t.ApplicationId)
.IsRequired();
this.Property(t => t.Description)
.HasMaxLength(256);
this.Property(t => t.RoleName)
.IsRequired()
.HasMaxLength(256);
this.Property(t => t.LoweredRoleName)
.IsRequired()
.HasMaxLength(256);
this.ToTable("aspnet_Roles");
this.Property(t => t.RoleId).HasColumnName("RoleId");
this.Property(t => t.ApplicationId).HasColumnName("ApplicationId");
this.Property(t => t.Description).HasColumnName("Description");
this.Property(t => t.LoweredRoleName).HasColumnName("LoweredRoleName");
this.Property(t => t.RoleName).HasColumnName("RoleName");
}
}
static DataContext()
{
Database.SetInitializer<DataContext>(null);
}
public DataContext(): base("DefaultConnection")
{
}
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new RoleMap());
}
I found this code in this link:
http://www.codeproject.com/Tips/661053/Entity-Framework-Code-First-Map
Unfortunately, when I try to create a new migration - it is trying to create a new table (even though I told it the table already exists).
public partial class addRoles : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.aspnet_Roles",
c => new
{
RoleId = c.Guid(nullable: false),
ApplicationId = c.Guid(nullable: false),
Description = c.String(maxLength: 256),
LoweredRoleName = c.String(nullable: false, maxLength: 256),
RoleName = c.String(nullable: false, maxLength: 256),
})
.PrimaryKey(t => t.RoleId);
}
public override void Down()
{
DropTable("dbo.aspnet_Roles");
}
}
With the help of JamieD77 I found the solution. It is a combination of the code first mapping found in this link:
Code First Mapping
and then here, add -ignorechanges when add-migration
Code First Migrations With Existing Table
Related
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 }));
I use Identity 2.0. I have CustomUserRole
public class CustomUserRole : IdentityUserRole<long>
{
}
When i try generate a migration all is good. But if i add a configuration to CustomUserRole
public class CustomRoleConfiguration : EntityTypeConfiguration<CustomRole>
{
public CustomRoleConfiguration()
{
Property(r => r.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
EF adds a field CustomRole_Id
CreateTable(
"dbo.AspNetUserRoles",
c => new
{
UserId = c.Long(nullable: false),
RoleId = c.Long(nullable: false),
CustomRole_Id = c.Long(),
})
.PrimaryKey(t => new { t.UserId, t.RoleId })
.ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
.ForeignKey("dbo.CustomRoles", t => t.CustomRole_Id)
.Index(t => t.UserId)
.Index(t => t.CustomRole_Id);
Now i have two field for Role. I dont understand why.
I had to write
base.OnModelCreating(modelBuilder);
after
modelBuilder.Configurations.Add(new DocumentConfiguration());
modelBuilder.Configurations.Add(new SubvisionConfiguration());
modelBuilder.Configurations.Add(new DocumentActionConfiguration());
modelBuilder.Configurations.Add(new CustomRoleConfiguration());
while i write it before
I am using EntityFramework and ASP.NET identity. I have derived from IdentityUser and IdentityGroup to store extra fields for my application.
I want to call properties: User.Groups and Group.Users, a many-to-many relationship, and have EntityFramework automatically create the linking table, GroupUsers.
My first attempt had the following:
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
public virtual ICollection<ApplicationGroup> Groups { get; set; }
// ...
}
public class ApplicationGroup : IdentityGroup<ApplicationUser>
{
public virtual ICollection<ApplicationGroupRole> Roles { get; set; }
}
public class IdentityGroup<TUser, TKey> : IGroup<TKey>
where TUser : IdentityUser
where TKey : IEquatable<TKey>
{
public virtual ICollection<TUser> Users { get; set; }
// ...
}
And the DBMigration looked something like
CreateTable(
"UMS.ApplicationGroupApplicationUsers",
c => new
{
ApplicationGroup_Id = c.String(nullable: false, maxLength: 128),
ApplicationUser_Id = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.ApplicationGroup_Id, t.ApplicationUser_Id })
.ForeignKey("UMS.ApplicationGroups", t => t.ApplicationGroup_Id, cascadeDelete: true)
.ForeignKey("UMS.Users", t => t.ApplicationUser_Id, cascadeDelete: true)
.Index(t => t.ApplicationGroup_Id)
.Index(t => t.ApplicationUser_Id);
In particular, note the linking table has two indexes, one for each foreign key.
However, I wanted to name the linking table explicitly, so in my DBContext I added:
modelBuilder.Entity<ApplicationUser>().ToTable("Users");
modelBuilder.Entity<ApplicationGroup>().ToTable("Groups")
.HasMany(x => x.Users)
.WithMany(x => x.Groups)
.Map(x =>
{
x.ToTable("GroupUsers");
x.MapLeftKey("UserId");
x.MapRightKey("GroupId");
});
However, this gives me an automatic migration with only 1 index:
CreateTable(
"UMS.GroupUsers",
c => new
{
UserId = c.String(nullable: false, maxLength: 128),
GroupId = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.UserId, t.GroupId })
.ForeignKey("UMS.Groups", t => t.UserId, cascadeDelete: true)
.ForeignKey("UMS.Users", t => t.GroupId, cascadeDelete: true)
.Index(t => t.UserId);
Is this just a bug in EntityFramework? This appears to only happen when one type has a collection of the other via a derived type. Is it possible to keep an explicitly named linking table whilst automatically creating both indexes?
This may not resolve the problem you are having, however, it will correct your code. In your case, as per definition, the "Left Key" should be "GroupId" and the "Right Key" should be "UserId". Check this link. Notice the code you posted, you have got them mixed:
.ForeignKey("UMS.Groups", t => t.UserId, cascadeDelete: true)
.ForeignKey("UMS.Users", t => t.GroupId, cascadeDelete: true)
Your code should look like this:
modelBuilder.Entity<ApplicationGroup>().ToTable("Groups")
.HasMany(x => x.Users)
.WithMany(x => x.Groups)
.Map(x =>
{
x.ToTable("GroupUsers");
x.MapLeftKey("GroupId");
x.MapRightKey("UserId");
});
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<Company>
{
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<Company>().HasRequired(x => x.CreatedByProfile).WithMany().Map(
x => x.MapKey("CreatedByProfileId"));
UPDATE
Since you already have the property in your class.
modelBuilder.Entity<Company>().HasRequired(
x => x.CreatedByProfile).WithMany().HasForeignKey(x => x.CreatedBy);
I have two tables that are connect N to N:
[Table("Backoffice_Roles")]
public class Role
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid RoleId { get; set; }
public ICollection<User> Users { get; set; }
}
[Table("Backoffice_Users")]
public class User
{
// Primary key
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; }
public ICollection<Role> Roles { get; set; }
}
This all works fine and it creates 3 tables: Backoffice_Roles, Backoffice_Users and RoleUsers.
Is there a way to rename RoleUsers to Backoffice_RoleUsers ?
I tried renaming the table manually in the migration file but it gives this error:
System.Data.Entity.Infrastructure.DbUpdateException: An error occurred
while saving entities that do not expose foreign key properties for
their relationships. The EntityEntries property will return null
because a single entity cannot be identified as the source of the
exception. Handling of exceptions while saving can be made easier by
exposing foreign key properties in your entity types. See the
InnerException for details. --->
System.Data.Entity.Core.UpdateException: An error occurred while
updating the entries. See the inner exception for details. --->
System.Data.SqlClient.SqlException: Invalid object name
'dbo.RoleUsers'.
This the migration without changing the name of the last table manually:
public override void Up()
{
CreateTable(
"dbo.Backoffice_Users",
c => new
{
UserId = c.Guid(nullable: false, identity: true),
})
.PrimaryKey(t => t.UserId);
CreateTable(
"dbo.Backoffice_Roles",
c => new
{
RoleId = c.Guid(nullable: false, identity: true),
})
.PrimaryKey(t => t.RoleId);
CreateTable(
"dbo.RoleUsers",
c => new
{
Role_RoleId = c.Guid(nullable: false),
User_UserId = c.Guid(nullable: false),
})
.PrimaryKey(t => new { t.Role_RoleId, t.User_UserId })
.ForeignKey("dbo.Backoffice_Roles", t => t.Role_RoleId)
.ForeignKey("dbo.Backoffice_Users", t => t.User_UserId)
.Index(t => t.Role_RoleId)
.Index(t => t.User_UserId);
}
Use following mapping to provide name for junction table:
modelBuilder.Entity<Role>()
.HasMany(r => r.Users)
.WithMany(u => u.Roles)
.Map(m => m.ToTable("Backoffice_RoleUsers"));
You can provide mappings by overriding OnModelCreating method of your DbContext class.