Entity Framework 6, Invalid Column Name - Incorrect Mapping - entity-framework

I solved this by adding in the ForeignKey("Route") data annotation like this:
[ForeignKey("Route")]
public Guid rnh_rtefk { get; set; }
this is my first attempt at using EF.
I have created a model using Code First from database in my MVC app using EF 6.0.0.0
In my database I have a table of Routes and a table of RunSheetHeader. A route can have many RunSheetHeader and a RunSheetHeader can have one Route. The Routes primary key is Routes.rte_pk and this maps to the foreign key: RunSheetHeader.rnh_rtefk.
The code generated is this:
public partial class Route
{
public Route()
{
RunSheetHeaders = new HashSet<RunSheetHeader>();
}
[Key]
public Guid rte_pk { get; set; }
[Required]
[StringLength(50)]
public string rte_name { get; set; }
public virtual ICollection<RunSheetHeader> RunSheetHeaders { get; set; }
}
[Table("RunSheetHeader")]
public partial class RunSheetHeader
{
public RunSheetHeader()
{
RunSheetDetails = new HashSet<RunSheetDetail>();
}
[Key]
public Guid rnh_pk { get; set; }
[Column(TypeName = "date")]
public DateTime rnh_date { get; set; }
public Guid rnh_rtefk { get; set; }
public virtual Route Route { get; set; }
public virtual ICollection<RunSheetDetail> RunSheetDetails { get; set; }
}
This is from the Context class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Route>()
.HasMany(e => e.RunSheetHeaders)
.WithRequired(e => e.Route)
.HasForeignKey(e => e.rnh_rtefk)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RunSheetHeader>()
.HasMany(e => e.RunSheetDetails)
.WithRequired(e => e.RunSheetHeader)
.HasForeignKey(e => e.rnd_rnhfk)
.WillCascadeOnDelete(false);
}
The error I get is:
"Invalid column name 'Route_rte_pk'."
and the SQL shows up in SQL Profiler as:
SELECT
1 AS [C1],
[Extent1].[rnh_pk] AS [rnh_pk],
[Extent1].[rnh_date] AS [rnh_date],
[Extent1].[rnh_rtefk] AS [rnh_rtefk],
[Extent1].[Route_rte_pk] AS [Route_rte_pk]
FROM [dbo].[RunSheetHeader] AS [Extent1]
From reading the other answers here regarding similar problems it seems to be a problem with mapping the correct foreign keys, but it looks to me like that has been done correctly. Can anyone spot what I am missing?
Thanks for any help

I found 2 possible solutions to this:
1.
[ForeignKey("Route")]
public Guid rnh_rtefk { get; set; }
or 2.
[ForeignKey("rnh_rtefk")]
public virtual Route Route { get; set; }
Both get rid of the error, but I was advised that the second option is the better one to use.

the following code is running just fine, are you sure your OnModelCreating is well called ?
namespace testef {
public partial class Route {
public Route() {
RunSheetHeaders = new HashSet<RunSheetHeader>();
}
[Key]
public Guid rte_pk { get; set; }
[Required]
[StringLength(50)]
public string rte_name { get; set; }
public virtual ICollection<RunSheetHeader> RunSheetHeaders { get; set; }
}
[Table("RunSheetHeader")]
public partial class RunSheetHeader {
public RunSheetHeader() {
//RunSheetDetails = new HashSet<RunSheetDetail>();
}
[Key]
public Guid rnh_pk { get; set; }
[Column(TypeName = "date")]
public DateTime rnh_date { get; set; }
public Guid rnh_rtefk { get; set; }
public virtual Route Route { get; set; }
//public virtual ICollection<RunSheetDetail> RunSheetDetails { get; set; }
}
// ---------------
public class TestEFContext : DbContext {
public TestEFContext(String cs)
: base(cs) {
Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Route>()
.HasMany(e => e.RunSheetHeaders)
.WithRequired(e => e.Route)
.HasForeignKey(e => e.rnh_rtefk)
.WillCascadeOnDelete(false);
//modelBuilder.Entity<RunSheetHeader>()
// .HasMany(e => e.RunSheetDetails)
// .WithRequired(e => e.RunSheetHeader)
// .HasForeignKey(e => e.rnd_rnhfk)
// .WillCascadeOnDelete(false);
}
public DbSet<Route> Routes { get; set; }
}
class Program {
String cs = #"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
using (TestEFContext ctx = new TestEFContext(cs)) {
Route r = new Route {
rte_pk = Guid.NewGuid(),
rte_name = "test"
};
r.RunSheetHeaders.Add(new RunSheetHeader {
rnh_pk = Guid.NewGuid(),
rnh_date = DateTime.Now
});
ctx.Routes.Add(r);
ctx.SaveChanges();
Console.WriteLine(ctx.Routes.Count());
}
using (TestEFContext ctx = new TestEFContext(cs)) {
foreach (Route r in ctx.Routes) {
Console.WriteLine(r.rte_name);
foreach (RunSheetHeader rsh in r.RunSheetHeaders) {
Console.WriteLine(" {0}", rsh.rnh_date);
}
}
}
}
}
}

Related

Entity Framework Core: How to solve Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths

I'm using Entity Framework Core with Code First approach but recieve following error when updating the database:
Introducing FOREIGN KEY constraint 'FK_AnEventUsers_Users_UserId' on table 'AnEventUsers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
My entities are these:
public class AnEvent
{
public int AnEventId { get; set; }
public DateTime Time { get; set; }
public Gender Gender { get; set; }
public int Duration { get; set; }
public Category Category { get; set; }
public int MinParticipants { get; set; }
public int MaxParticipants { get; set; }
public string Description { get; set; }
public Status EventStatus { get; set; }
public int MinAge { get; set; }
public int MaxAge { get; set; }
public double Longitude { get; set; }
public double Latitude { get; set; }
public ICollection<AnEventUser> AnEventUsers { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class User
{
public int UserId { get; set; }
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Gender Gender { get; set; }
public double Rating { get; set; }
public ICollection<AnEventUser> AnEventUsers { get; set; }
}
public class AnEventUser
{
public int AnEventId { get; set; }
public AnEvent AnEvent { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
public class ApplicationDbContext:DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options):base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AnEventUser>()
.HasOne(u => u.User).WithMany(u => u.AnEventUsers).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<AnEventUser>()
.HasKey(t => new { t.AnEventId, t.UserId });
modelBuilder.Entity<AnEventUser>()
.HasOne(pt => pt.AnEvent)
.WithMany(p => p.AnEventUsers)
.HasForeignKey(pt => pt.AnEventId);
modelBuilder.Entity<AnEventUser>()
.HasOne(eu => eu.User)
.WithMany(e => e.AnEventUsers)
.HasForeignKey(eu => eu.UserId);
}
public DbSet<AnEvent> Events { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<AnEventUser> AnEventUsers { get; set; }
}
The issue I thought was that if we delete a User the reference to the AnEvent will be deleted and also the reference to AnEventUser will also be deleted, since there is a reference to AnEventUser from AnEvent as well we get cascading paths. But I remove the delete cascade from User to AnEventUser with:
modelBuilder.Entity<AnEventUser>()
.HasOne(u => u.User).WithMany(u => u.AnEventUsers).IsRequired().OnDelete(DeleteBehavior.Restrict);
But the error doesn't get resolved, does anyone see what is wrong? Thanks!
In your sample code in OnModelCreating you have declared modelBuilder.Entity<AnEventUser>().HasOne(e => e.User)... twice: at start of method and at end.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AnEventUser>() // THIS IS FIRST
.HasOne(u => u.User).WithMany(u => u.AnEventUsers).IsRequired().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<AnEventUser>()
.HasKey(t => new { t.AnEventId, t.UserId });
modelBuilder.Entity<AnEventUser>()
.HasOne(pt => pt.AnEvent)
.WithMany(p => p.AnEventUsers)
.HasForeignKey(pt => pt.AnEventId);
modelBuilder.Entity<AnEventUser>() // THIS IS SECOND.
.HasOne(eu => eu.User) // THIS LINES
.WithMany(e => e.AnEventUsers) // SHOULD BE
.HasForeignKey(eu => eu.UserId); // REMOVED
}
Second call overrides first. Remove it.
This is what I did from the answer of Dmitry,
and It worked for me.
Class:
public class EnviornmentControls
{
public int Id { get; set; }
...
public virtual Environment Environment { get; set; }
}
And it's Mapping
public EnviornmentControlsMap(EntityTypeBuilder<EnviornmentControls> entity)
{
entity.HasKey(m => m.Id);
entity.HasOne(m => m.Environment)
.WithMany(m => m.EnviornmentControls)
.HasForeignKey(m => m.EnvironmentID)
.OnDelete(DeleteBehavior.Restrict); // added OnDelete to avoid sercular reference
}
These solutions didn't work for my case, but I found a way. I am not quite sure yet if it is safe but there's just something that's happening with deleting. So I modified the generated Migration File instead of putting an override.
onDelete: ReferentialAction.Cascade
The reason I did this because all the overriding mentioned above is not working for me so I manually removed the code which relates to Cascading of Delete.
Just check which specific relation being mentioned at the error so you can go straightly.
Hope this will be able to help for some people who's having the same issue as mine.
public Guid? UsuarioId { get; set; }
builder.Entity<Orcamentacao>()
.HasOne(x => x.Usuario)
.WithMany(x => x.Orcamentacaos)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(false)
.HasForeignKey(x => x.UsuarioId);

Entity Framework many to many fluent API

I'm working with an existing database whose schema is fixed.
There are 3 many-to-many relations, Contact-Group, Contact-Department and Contact-Team.
There is a common table, ContactRelation that acts as the middle table for all 3 relations.
public class Contact
{
[Column("CtcID")]
public int Id { get; set; }
[Column("CtcFirstName")]
public string FirstName { get; set; }
[Column("CtcFamilyName")]
public string LastName { get; set; }
public virtual ICollection<ContactRelation> GroupRelations { get; set; }
public virtual ICollection<ContactRelation> DepartmentRelations { get; set; }
public virtual ICollection<ContactRelation> TeamRelations { get; set; }
}
public class Group
{
[Key, Column("GRID")]
public int Id { get; set; }
[Column("GRName")]
public string Name { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class Department
{
[Key, Column("DEPID")]
public int Id { get; set; }
[Column("DEPName")]
public string Name { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class Team
{
[Key, Column("TMID")]
public int Id { get; set; }
[Column("TransCode")]
public string TransCode { get; set; }
public virtual ICollection<ContactRelation> ContactRelations { get; set; }
}
public class ContactRelation
{
[Key]
[Column("CtcRelnID")]
public int Id { get; set; }
[Column("GRID")]
public int GroupId { get; set; }
[Column("DEPID")]
public int DepartmentId { get; set; }
[Column("TMID")]
public int TeamId { get; set; }
[Column("CUCtcID")]
public int ContactId { get; set; }
[Column("RCode")]
public string RoleCode { get; set; }
}
In my mapping, I have the following code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Group>()
.HasMany(g => g.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.GroupId);
modelBuilder.Entity<Department>()
.HasMany(c => c.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.DepartmentId);
modelBuilder.Entity<Team>()
.HasMany(s => s.ContactRelations)
.WithRequired()
.HasForeignKey(r => r.TeamId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.GroupRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.DepartmentRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
modelBuilder.Entity<Contact>()
.HasMany(c => c.TeamRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
}
I then try to execute the following query:
var ctc = repo.Contacts
.Include("GroupRelations")
.Include("DepartmentRelations")
.FirstOrDefault(c => c.FirstName.ToLower() == "jason");
and I keep getting error:
System.Data.SqlClient.SqlException:
Invalid column name 'Contact_Id1'.
Invalid column name 'Contact_Id'.
Invalid column name 'Contact_Id1'.
Invalid column name 'Contact_Id2'.
I read somewhere that a table cannot participate in more than one many-to-many relations. Is that true? Is it because the ContactRelation table is used more than once that I'm getting this error?
If so, what's the correct way to map these relations, without modifying the database schema.?
PS: I'm working with EF6.1
Thanks for your help.
Remove this mapping
modelBuilder.Entity<Contact>()
.HasMany(c => c.TeamRelations)
.WithRequired()
.HasForeignKey(r => r.ContactId);
and then try executing your query.

Why is it that Entity Framework gives the error "Enumeration yielded no results" for typical many-to-many functionality?

Our ASP.NET C# web application is used in the following environment .NET Framework 4
ASP.NET Web Forms.
IIS 7
Windows 2008
Visual Studio 2010
.NET IDE C#
HTTPS ( SSL )
-Entity Framework 5
In our application, we have the following tables.
1) LearningCart
2) EnrollmentEntriesInLearningCarts
3) EnrollmentEntry
4) Tutorial
5) aspnet_Users
There is a many-to-many relationship between the LearningCart and the EnrollmentEntry tables ( which have the EnrollmentEntriesInLearningCarts bridge table)
The many-to-many relationship between the LearningCart and the EnrollmentEntry tables are depicted in the following Database diagram picture:
Also, I also ensured that the EnrollmentEntriesInLearningCarts bridge table already had data inside it:
namespace PerlsData.Domain
{
using System;
using System.Collections.Generic;
public partial class LearningCart
{
public LearningCart()
{
}
public virtual System.Guid LearningCartsDatabaseId { get; set; }
public virtual short Type { get; set; }
public virtual short Status { get; set; }
public virtual string LearningCartsName { get; set; }
public virtual Nullable<System.Guid> Creator { get; set; }
public virtual Nullable<System.Guid> TeamLead { get; set; }
public virtual Nullable<short> isSubmitted { get; set; }
public virtual Nullable<short> isRemoved { get; set; }
public virtual Nullable<System.DateTime> DateSharedWithInstructor { get; set; }
public virtual ICollection<EnrollmentEntry> associatedEnrollmentEntry { get; set; }
}
}
namespace PerlsData.Domain
{
using System;
using System.Collections.Generic;
public partial class EnrollmentEntry
{
public EnrollmentEntry()
{
}
public virtual System.Guid EnrollmentEntryDatabaseID { get; set; }
public virtual System.Guid UserId { get; set; }
public virtual System.Guid TutorialDatabaseID { get; set; }
public virtual aspnet_Users aspnet_Users { get; set; }
public virtual Tutorial Tutorial { get; set; }
public virtual ICollection<LearningCart> associatedLearningCart { get; set; }
}
}
namespace PerlsData.Domain
{
using System;
using System.Collections.Generic;
public partial class aspnet_Users
{
public aspnet_Users()
{
}
public System.Guid ApplicationId { get; set; }
public System.Guid UserId { get; set; }
public string UserName { get; set; }
public string LoweredUserName { get; set; }
public string MobileAlias { get; set; }
public bool IsAnonymous { get; set; }
public System.DateTime LastActivityDate { get; set; }
}
}
namespace PerlsData.Domain
{
using System;
using System.Collections.Generic;
public partial class Tutorial
{
public Tutorial()
{
}
public virtual System.Guid TutorialDatabaseID { get; set; }
public virtual string TutorialCode { get; set; }
public virtual System.Guid SectionDatabaseID { get; set; }
public virtual System.Guid UserId { get; set; }
public virtual aspnet_Users aspnet_Users { get; set; }
public virtual ICollection<EnrollmentEntry> EnrollmentEntries { get; set; }
public virtual Section Section { get; set; }
}
}
namespace PerlsData
{
public class Context : System.Data.Entity.DbContext
{
public DbSet<PerlsData.Domain.EnrollmentEntry> EnrollmentEntries { get; set; }
public DbSet<PerlsData.Domain.LearningCart> LearningCarts { get; set; }
public Context()
: base("Name=LocalSqlServer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PerlsData.Domain.LearningCart>().ToTable("LearningCarts", schemaName: "dbo");
modelBuilder.Entity<PerlsData.Domain.LearningCart>().HasKey(t => t.LearningCartsDatabaseId);
modelBuilder.Entity<PerlsData.Domain.LearningCart>().Property(t => t.LearningCartsDatabaseId)
.HasColumnName("LearningCartsDatabaseId")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<PerlsData.Domain.LearningCart>().HasMany(learnC => learnC.associatedEnrollmentEntry)
.WithMany(enEnt => enEnt.associatedLearningCart)
.Map(
m =>
{
m.ToTable("EnrollmentEntriesInLearningCarts", schemaName: "dbo");
m.MapLeftKey("LearningCartsDatabaseId");
m.MapRightKey("EnrollmentEntryDatabaseID");
}
);
modelBuilder.Entity<PerlsData.Domain.EnrollmentEntry> ().ToTable("EnrollmentEntry", schemaName: "dbo");
modelBuilder.Entity<PerlsData.Domain.EnrollmentEntry>().HasKey(e => e.EnrollmentEntryDatabaseID);
modelBuilder.Entity<PerlsData.Domain.EnrollmentEntry>().Property(t => t.EnrollmentEntryDatabaseID)
.HasColumnName("EnrollmentEntryDatabaseID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<PerlsData.Domain.EnrollmentEntry>().Property(t => t.TutorialDatabaseID)
.HasColumnName("TutorialDatabaseID");
modelBuilder.Entity<PerlsData.Domain.EnrollmentEntry>().HasMany(enEnt => enEnt.associatedLearningCart)
.WithMany(learnC => learnC.associatedEnrollmentEntry)
.Map(
m =>
{
m.ToTable("EnrollmentEntriesInLearningCarts", schemaName: "dbo");
m.MapLeftKey("EnrollmentEntryDatabaseID");
m.MapRightKey("LearningCartsDatabaseId");
}
);
modelBuilder.Entity<PerlsData.Domain.aspnet_Users>().ToTable("aspnet_Users", schemaName: "dbo");
modelBuilder.Entity<PerlsData.Domain.aspnet_Users>()
.Property(au => au.UserId)
.HasColumnName("UserId");
modelBuilder.Entity<PerlsData.Domain.aspnet_Users>()
.HasKey(au => au.UserId);
modelBuilder.Entity<PerlsData.Domain.aspnet_Users>()
.Property(au => au.UserName)
.HasColumnName("UserName");
modelBuilder.Entity<PerlsData.Domain.Tutorial>().ToTable("Tutorial", schemaName: "dbo");
modelBuilder.Entity<PerlsData.Domain.Tutorial>().Property(t => t.TutorialDatabaseID)
.HasColumnName("TutorialDatabaseID")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<PerlsData.Domain.Tutorial>()..HasKey(t => t.TutorialDatabaseID);
modelBuilder.Entity<PerlsData.Domain.Tutorial>().HasRequired(x => x.Section)
.WithMany(x => x.Tutorials)
.HasForeignKey(x => x.SectionDatabaseID);
}
}
}
Here's is where the code fails to retrieve information:
public LearningCart getLearningCartAssociatedWithLoggedInStudentsTutorial(Guid userOfInterestGuid, Guid tutorialOfInterestGuid){
LearningCart learningCartsAssociatedWithLoggedInStudentTutorial = new LearningCart();
EnrollmentEntry enrollmentEntryAssociatedWithLoggedInStudentTutorial = new EnrollmentEntry();
LearningCart learnCartOfInterest = null;
IQueryable<LearningCart> learnCartIQueryable = null;
EnrollmentEntry enrEntOfInterest = null;
using (PerlsData.Context context = new PerlsData.Context())
{
context.Configuration.LazyLoadingEnabled = true;
context.Configuration.ProxyCreationEnabled = true;
IQueryable<EnrollmentEntry> enrollmentEntryOfInterestIQueryable =
context.EnrollmentEntries.Where(ee =>
ee.aspnet_Users.UserId == userOfInterestGuid
&& ee.Tutorial.TutorialDatabaseID == tutorialOfInterestGuid);
foreach (EnrollmentEntry enrEnt in enrollmentEntryOfInterestIQueryable)
{
enrEntOfInterest = enrEnt;
}
learnCartIQueryable = context.LearningCarts.Where(lc =>
lc.associatedEnr ollmentEntry.Any(ee =>
ee.EnrollmentEntryDatabaseID == enrEntOfInterest.EnrollmentEntryDatabaseID));
foreach (LearningCart lc in learnCartIQueryable)
{
learnCartOfInterest = lc;
}
context.Entry(learnCartOfInterest).State = System.Data.EntityState.Detached;
}
return learnCartOfInterest;
}
The problem comes about with trying to figure out a specific LearningCart module.
I try to retrieve a LearningCart, and place it in the IQueryable object:
IQueryable<LearningCart> learnCartIQueryable = context.LearningCarts.Where(lc =>
lc.associatedEnrollmentEntry.Any(ee =>
ee.EnrollmentEntryDatabaseID == enrEntOfInterest.EnrollmentEntryDatabaseID));
Using Visual Studio 2010, I put learnCartIQueryable under "Add Watch", and I got "Enumeration yielded no results"
I already do have data inside in the EnrollmentEntriesInLearningCarts bridge table.
Why is it that Entity Framework fails to retrieve the data in the EnrollmentEntriesInLearningCarts bridge table, and only gives the error "Enumeration yielded no results" ?

Integrating SimpleMembership with Entity Framework

I'm trying to integrate the SimpleMembership tables with the rest of my Object Model - to manage all the entities from a single database and context.
Up to now the best recipe I've found for manually spinning up the SM tables (the entry point to combine SimpleMember with the rest of my Object Model) is found here. But, as cited in the comments section there are a couple mistakes in the code sample provided. The comments attempt to provide corrections but, due to formatted, really hard to follow.
I'm 80% the way there but getting stuck with the Foreign Key generation for the Membership table. Does the code within OnModelCreating block belong in the MyDbContext class? I'm getting a compile error on the .WithMany(u => u.Members) line.
Membership.cs
[Table("webpages_Membership")]
public class Membership
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public DateTime? CreateDate { get; set; }
[StringLength(128)]
public string ConfirmationToken { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
[Required, StringLength(128)]
public string Password { get; set; }
public DateTime? PasswordChangedDate { get; set; }
[Required, StringLength(128)]
public string PasswordSalt { get; set; }
[StringLength(128)]
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
<strike>public virtual ICollection<Role> Roles { get; set; }</strike>
EDIT: Originally I added the line above to remove a compiler complaint in the extraneous code block below. Removing this attempt to create a FK to Roles will align the rest of this code so that these model classes create a Migration that generates tables for SM.
OAuthMembership.cs
[Table("webpages_OAuthMembership")]
public class OAuthMembership
{
[Key, Column(Order = 0), StringLength(30)]
public string Provider { get; set; }
[Key, Column(Order = 1), StringLength(100)]
public string ProviderUserId { get; set; }
public int UserId { get; set; }
}
Role.cs
[Table("webpages_Roles")]
public class Role
{
[Key]
public int RoleId { get; set; }
[StringLength(256)]
public string RoleName { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
UserProfile.cs
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
MyDbContext.cs
public MyDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Membership> Membership { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<OAuthMembership> OAuthMembership { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProfile>()
.HasMany<Role>(r => r.Roles)
.WithMany(u => u.UserProfiles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
EDIT: The block below was included in one of the article's comments but seems not to be needed.
//modelBuilder.Entity<Membership>()
// .HasMany<Role>(r => r.Roles)
// .WithMany(u => u.Members)
// .Map(m =>
// {
// m.ToTable("webpages_UsersInRoles");
// m.MapLeftKey("UserId");
// m.MapRightKey("RoleId");
// });
}
}
I followed the instructions in the article, and I also took into account the the comments that suggested the article was wrong in a few ways.
I ended up with the following classes:
UserProfile.cs
[Table("UserProfile")]
public class UserProfile
{
[Key, ForeignKey("Membership")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<WebSecurity.Role> Roles { get; set; }
public WebSecurity.Membership Membership { get; set; }
}
You should notice right away the "ForeignKey" attribute I use on the UserId column. Since the user is first created in the Membership table, my UserProfile table is the dependent table.
Membership.cs
[Table("webpages_Membership")]
public class Membership
{
//public Membership()
//{
// Roles = new List<Role>();
// OAuthMemberships = new List<OAuthMembership>();
//}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int UserId { get; set; }
public DateTime? CreateDate { get; set; }
[StringLength(128)]
public string ConfirmationToken { get; set; }
public bool? IsConfirmed { get; set; }
public DateTime? LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
[Required, StringLength(128)]
public string Password { get; set; }
public DateTime? PasswordChangedDate { get; set; }
[Required, StringLength(128)]
public string PasswordSalt { get; set; }
[StringLength(128)]
public string PasswordVerificationToken { get; set; }
public DateTime? PasswordVerificationTokenExpirationDate { get; set; }
public UserProfile UserProfile { get; set; }
}
Per Richard's comments in the article, I commented out the constructor. I also created a reference back to the UserProfile, but not to roles.
OAuthMembership.cs
[Table("webpages_OAuthMembership")]
public class OAuthMembership
{
[Key, Column(Order = 0), StringLength(30)]
public string Provider { get; set; }
[Key, Column(Order = 1), StringLength(100)]
public string ProviderUserId { get; set; }
public int UserId { get; set; }
//[Column("UserId"), InverseProperty("OAuthMemberships")]
//public Membership User { get; set; }
}
My OAuthMembership class remained basically the same; I commented out only the User attribute, per Richard's comment in the article.
AccountModel.cs+UsersContext
Finally, the UserContext class, where I create the association for the UsersInRoles table.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<InternetApplication.Models.WebSecurity.Role>()
.HasMany<InternetApplication.Models.UserProfile>(r => r.UserProfiles)
.WithMany(u => u.Roles)
.Map(m =>
{
m.ToTable("webpages_UsersInRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
}
public DbSet<WebSecurity.Membership> Membership { get; set; }
public DbSet<WebSecurity.OAuthMembership> OAuthMembership { get; set; }
public DbSet<WebSecurity.Role> Roles { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
}
In addition to adding the UsersInRoles mapping, I added DbSet entries for each table.
Now that everything has been created, I can use my Add-Migration and Update-Database commands and use the following code snippet that combines the Membership, UserProfile, and Roles tables:
using (var db = new UsersContext())
{
var memberships = db.Membership
.Include("UserProfile")
.Include("UserProfile.Roles")
.ToList();
foreach (var member in memberships)
{
member.IsConfirmed = true;
}
db.SaveChanges();
}
This was a long post, but I hope that helps.
I used the answer to this question to automatically generate the models from the existing 'webpage_' tables in my database. This ensures that the models are created in the exact same way that SimpleMembership creates them. This resulted in the following code:
Models:
public partial class webpages_Membership
{
public int UserId { get; set; }
public Nullable<System.DateTime> CreateDate { get; set; }
public string ConfirmationToken { get; set; }
public Nullable<bool> IsConfirmed { get; set; }
public Nullable<System.DateTime> LastPasswordFailureDate { get; set; }
public int PasswordFailuresSinceLastSuccess { get; set; }
public string Password { get; set; }
public Nullable<System.DateTime> PasswordChangedDate { get; set; }
public string PasswordSalt { get; set; }
public string PasswordVerificationToken { get; set; }
public Nullable<System.DateTime> PasswordVerificationTokenExpirationDate { get; set; }
}
public partial class webpages_Roles
{
public webpages_Roles()
{
this.webpages_UsersInRoles = new HashSet<webpages_UsersInRoles>();
}
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<webpages_UsersInRoles> webpages_UsersInRoles { get; set; }
}
public partial class webpages_UsersInRoles
{
public int UserId { get; set; }
public int RoleId { get; set; }
public virtual webpages_Roles webpages_Roles { get; set; }
}
Fluent Mappings:
internal partial class MembershipMapping : EntityTypeConfiguration<webpages_Membership>
{
public MembershipMapping()
{
this.HasKey(t => t.UserId);
this.ToTable("webpages_Membership");
this.Property(t => t.UserId).HasColumnName("UserId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.Property(t => t.CreateDate).HasColumnName("CreateDate");
this.Property(t => t.ConfirmationToken).HasColumnName("ConfirmationToken").HasMaxLength(128);
this.Property(t => t.IsConfirmed).HasColumnName("IsConfirmed");
this.Property(t => t.LastPasswordFailureDate).HasColumnName("LastPasswordFailureDate");
this.Property(t => t.PasswordFailuresSinceLastSuccess).HasColumnName("PasswordFailuresSinceLastSuccess");
this.Property(t => t.Password).HasColumnName("Password").IsRequired().HasMaxLength(128);
this.Property(t => t.PasswordChangedDate).HasColumnName("PasswordChangedDate");
this.Property(t => t.PasswordSalt).HasColumnName("PasswordSalt").IsRequired().HasMaxLength(128);
this.Property(t => t.PasswordVerificationToken).HasColumnName("PasswordVerificationToken").HasMaxLength(128);
this.Property(t => t.PasswordVerificationTokenExpirationDate).HasColumnName("PasswordVerificationTokenExpirationDate");
}
}
internal partial class RolesMapping : EntityTypeConfiguration<webpages_Roles>
{
public RolesMapping()
{
this.HasKey(t => t.RoleId);
this.ToTable("webpages_Roles");
this.Property(t => t.RoleId).HasColumnName("RoleId");
this.Property(t => t.RoleName).HasColumnName("RoleName").IsRequired().HasMaxLength(256);
}
}
internal partial class UsersInRolesMapping : EntityTypeConfiguration<webpages_UsersInRoles>
{
public UsersInRolesMapping()
{
this.HasKey(t => new { t.UserId, t.RoleId });
this.ToTable("webpages_UsersInRoles");
this.Property(t => t.UserId).HasColumnName("UserId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.Property(t => t.RoleId).HasColumnName("RoleId").HasDatabaseGeneratedOption(new Nullable<DatabaseGeneratedOption>(DatabaseGeneratedOption.None));
this.HasRequired(t => t.webpages_Roles).WithMany(t => t.webpages_UsersInRoles).HasForeignKey(d => d.RoleId);
}
}
Database Context:
public class MembershipContext : DbContext, IDisposable
{
public DbSet<webpages_Membership> Membership { get; set; }
public DbSet<webpages_Roles> Roles { get; set; }
public DbSet<webpages_UsersInRoles> UsersInRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MembershipMapping());
modelBuilder.Configurations.Add(new RolesMapping());
modelBuilder.Configurations.Add(new UsersInRolesMapping());
base.OnModelCreating(modelBuilder);
}
}
Note that I have excluded the OAuthMembership table, because I didn't need it for my solution. But if you follow the steps in the link I provided above you can easily include that table as well.
Starting from a blank MVC4 Internet Template I ran the project so as to create the SimpleMembership tables in a fresh db - then used EF's Reverse Engineer tool to create POCOs from those tables. Stepped thru it line by line to find the error and edited the code block in the OP.
With that code in place I used Package Manager to 'Add-Migration' and 'Update-Database'. Initial tests confirm everything works - I suppose I'll have to revisit if I find edge-cases that expose any deeper problems.

Entity Framework 5 using multiple relationships between two POCOs

I'm having issues applying multiple relationships (or possibly foreignkey) on two POCO objects. I've got the first relationship many-to-many working and when the database is created it creates the three tables (Projects, Users and ProjectsUsers) needed for the relationship.
Code so far:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? CompletionDate { get; set; }
public bool Deleted { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public User()
{
Name = new Name();
}
public int UserId { get; set; }
public string LoginId { get; set; }
public string Password { get; set; }
public Name Name { get; set; }
public ICollection<Project> ManagedProjects { get; set; }
}
public class ProjectConfiguration : EntityTypeConfiguration<Project>
{
public ProjectConfiguration()
{
HasMany(x => x.Users)
.WithMany(x => x.ManagedProjects);
}
}
public UserConfiguration()
{
HasMany(x => x.ManagedProjects)
.WithMany(x => x.Users);
}
Now I want to add an optional one-to-one relationship of Project.ManagingUser -> User. However, I can't seem to figure out how to indicate this in the configuration.
Code for what I think is needed:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? CompletionDate { get; set; }
public bool Deleted { get; set; }
public int? ManagingUserId { get; set; }
public User ManagingUser { get; set; }
public ICollection<User> Users { get; set; }
}
I don't think the User object needs to change.
This shows my last attempt on mapping the new relationship:
public ProjectConfiguration()
{
HasMany(p => p.Users)
.WithMany(u => u.Projects);
this.HasOptional(p => p.ManagingUser)
.WithOptionalDependent()
.Map(m=>m.MapKey("ManagingUserId"))
.WillCascadeOnDelete(false);
}
What is happening when the database is created, I now end up with only two tables (Projects and Users). And it looks like it is only trying to setup the one-to-one relationship.
Can someone tell me what I'm missing?
Richard I've not changed the UserConfiguration and below is the DbContext:
public class MyDbContext : DbContext
{
public MyDbContext() : base(Properties.Settings.Default.ConnectionString)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Project> Projects { get; set; }
}
You probably want WithMany instead of WithOptionalDependent - it's a one:many relationship, not a one:one.
HasOptional(p => p.ManagingUser)
.WithMany()
.HasForeignKey(m => m.ManagingUserId)
.WillCascadeOnDelete(false);
EDIT
I think you're missing the OnModelCreating override from the DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ProjectConfiguration());
modelBuilder.Configurations.Add(new UserConfiguration());
}