简体   繁体   中英

Entity Framework not mapping parent id as foreign key in child table

I am using Entity Framework 5.0, I want to insert rows in table which are in a parent-child relationship ie user and user_role .

  • user contains columns user_id, first_names, last_name

  • user_role contains columns role_id, role_name, user_id

I've setup Entity Framework and also can see their association in designer viewer, now when I try to execute my code, constraint exception occurs which says that user_id is not mapped in user_role table

Microsoft Entity Framework - not working:

using (MicrosoftEntities context = new MicrosoftEntities())
{
     USER user = new USER();
     user.FIRST_NAMES = "u";
     user.LAST_NAME = "u";

     user.USER_ROLE.Add(new USER_ROLE() { ROLE_ID = 10001, role_name ="a" });
     user.USER_ROLE.Add(new USER_ROLE() { ROLE_ID = 10002, role_name ="b" });

     context.USERZs.Add(user);
     context.SaveChanges();
}

Telerik Entity Framework - working perfectly fine:

using (TelerikEntities context = new TelerikEntities())
{
    Telerik_USER user1 = new Telerik_USER();
    user1.FIRST_NAMES = "u";
    user1.LAST_NAME = "u";

    user1.Telerik_USER_ROLE.Add(new Telerik_USER_ROLE() { ROLE_ID = 10001 });
    user1.Telerik_USER_ROLE.Add(new Telerik_USER_ROLE() { ROLE_ID = 10002 });

    context.Add(user1);
    context.SaveChanges();
}

What am I doing wrong? Please help me out.

I am compelled to use Microsoft Entity Framework.

Entity Framework classes

public partial class USER
{
    public USER()
    {
        this.USER_ROLE = new HashSet<USER_ROLE>();
    }

    public long USER_ID { get; set; }
    public string FIRST_NAMES { get; set; }
    public string LAST_NAME { get; set; }

    public virtual ICollection<USER_ROLE> USER_ROLE { get; set; }
  }

public partial class USER_ROLE
{
    public long USER_ROLE_ID { get; set; }
    public Nullable<long> USER_ID { get; set; }
    public Nullable<long> ROLE_ID { get; set; }
    public Nullable<string> ROLE_NAME { get; set; }
    public virtual USER USER { get; set; }
}

The user must exist before you add the role. You can't do it at the same time. You should do it in a transaction, but I will post working code for the inserts not the trans.

using (MicrosoftEntities context = new MicrosoftEntities())
{
     USER user = new USER();
     user.FIRST_NAMES = "u";
     user.LAST_NAME = "u";

     context.USERZs.Add(user);
     context.SaveChanges();


     user.USER_ROLE.Add(new USER_ROLE() { ROLE_ID = 10001, role_name ="a", USER_ID = user.USER_ID });
     user.USER_ROLE.Add(new USER_ROLE() { ROLE_ID = 10002, role_name ="b", USER_ID = user.USER_ID });
     context.SaveChanges();

}

The problem is that your new child entities are not being tracked by EF at the time of insertion (because you newed them). If you used proxies and would let EF generate its own ids, it should work.

The common way of doing this is attaching the entities before SaveChanges() , in your case:

using (MicrosoftEntities context = new MicrosoftEntities())
{
     USER user = new USER();
     user.FIRST_NAMES = "u";
     user.LAST_NAME = "u";

     /** Notice we don't specify a ROLE_ID here, we let EF generate it **/
     user.USER_ROLE.Add(new USER_ROLE() { role_name ="a" });
     user.USER_ROLE.Add(new USER_ROLE() { role_name ="b" });

     /** attach all entities to the context **/
     foreach(var role in user.USER_ROLE)
     {
       context.Entry(role).State = role.ROLE_ID == default(long) ? EntityState.Added : EntityState.Modified;
     }

     context.USERZs.Add(user);
     context.SaveChanges();
}

In EF7 you could use AttachGraph (or TrackGraph , not sure how it's called in final versions), in EF6 you need to do it by hand.

This could be further simplified by using proxies, creating the entities with DbContext.Create instead of new 'ing them, and having a navigation property on USER_ROLE which is the USER (in case it's 0:n, which we don't know) and setting the User property on the role: this would take care of attaching and setting the correct ids on saving.

Sounds to me that you are moving your database issues to an ORM, and you should be abstracting from those issues. Generating your own ID's make no sense in an ORM: if things were great (which aren't, in practice), having an ORM should theoretically allow you to not care about id's at all, and use objects for relationships, not ids.

If you plan to make the work of the ORM in your code, forget about EF and ORMs and get some SQL mapper (like Dapper.NET or similar), which does wonders and it's way more performant than EF.

You need to save the new User first so you get a database generated Id (assuming it is auto generated of course):

using (MicrosoftEntities context = new MicrosoftEntities())
using(var transaction = context.DataBase.BeginTransaction())
{
 USER user = new USER();
 user.FIRST_NAMES = "u";
 user.LAST_NAME = "u";
 context.Users.Add(user);
 context.SaveChanges();

 user.USER_ROLE.Add(new USER_ROLE() { ROLE_ID = 10001, role_name ="a", userId = user.Id});
 user.USER_ROLE.Add(new USER_ROLE() { ROLE_ID = 10002, role_name ="b", userId = user.Id});

 context.SaveChanges();
 transaction.Commit();
}

Only works with EF6+

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM