0

I searched on site for solving my problem, but I still haven't solved it. I have 2 entities:

public class Article
{
    [Key]
    public Guid ID { get; set; }
    public Guid? ApprovedBy_ID { get; set; }
    public Guid CreatedBy_ID { get; set; }
    public virtual Profile ApprovedBy { get; set; }
    public virtual Profile CreatedBy { get; set; }
    //New guid for new article
    public Article()
    {
        ID = Guid.NewGuid();
    }
}
public class Profile
{
    [Key]
    public Guid ID { get; set; }
    [Required]
    [StringLength(100)]
    public string FullName { get; set; }
    public Profile()
    {
        ID = Guid.NewGuid();
    }
}

And here is my insert-logic:

    private readonly iContext context;
    public ArticleLogic()
    {
        context = new iContext();
    }
    public IEnumerable<Article> GetAllArticle()
    {
        return context.Articles.Include("Categories").Include("Pictures").Include("ApprovedBy").Include("CreatedBy").Include("Template");
    }

    public Article AddArticle(Article article)
    {
            try
            {
                Profile pf = context.Profiles.First();
                context.Profiles.Attach(pf);

                Article art = new Article();
                art.Title = article.Title;
                art.Description = article.Description;
                art.Content = article.Content;
                art.Tag = article.Tag;
                art.Template = article.Template;
                //pf has ID = '0816f19c-31c1-4103-8f51-ba422beab1c0' (first row in database)
                art.CreatedBy = pf;
                art.CreatedBy_ID = pf.ID;
                context.Articles.Add(art);
                context.SaveChanges();

                return article;
            }
            catch (Exception ex)
            {
                //But ex throw error duplicate ID = '7aa1d064-54ff-47b9-807d-db422fa71f8c' (second row in database)
                Debug.WriteLine(ex.StackTrace);

                throw ex;
            }

    }

DB Context:

public myContext()
    : base("name=abc")
{
    this.Configuration.LazyLoadingEnabled = true;
    this.Configuration.ProxyCreationEnabled = false;
}
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Role> Roles { get; set; }
public virtual DbSet<Template> Templates { get; set; }
public virtual DbSet<Article> Articles { get; set; }
public virtual DbSet<Picture> Pictures { get; set; }
public virtual DbSet<Profile> Profiles { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    //One-to-many: Article - Picture (nullable)
    modelBuilder.Entity<Picture>()
        .HasOptional<Article>(c => c.Article)
        .WithMany(p => p.Pictures)
        .HasForeignKey(f => f.ArticleId);

    ////One-to-many: Role - Profile
    modelBuilder.Entity<Profile>()
        .HasRequired<Role>(i => i.Role)
        .WithMany(i => i.Profiles)
        .HasForeignKey(f => f.RoleID);

    ///Many-to-many: Article - Category
    modelBuilder.Entity<Article>()
        .HasMany(t => t.Categories)
        .WithMany(t => t.Articles)
        .Map(m => {
                m.ToTable("ArticleCategories");
                m.MapLeftKey("ArticleId");
                m.MapRightKey("CategoryId");
         });
    //
        modelBuilder.Entity<Article>()
            .HasOptional(a => a.ApprovedBy)
            .WithMany(a => a.ArticleApprovedBy)
            .HasForeignKey(f=>f.ApprovedBy_ID)
            .WillCascadeOnDelete(false);

        modelBuilder.Entity<Article>()
            .HasRequired(a => a.CreatedBy)
            .WithMany(a => a.ArticleCreatedBy)
            .HasForeignKey(f => f.CreatedBy_ID)
            .WillCascadeOnDelete(false);

        base.OnModelCreating(modelBuilder);
}

I am using CodeFirst EF 6.1.3 and I am getting an error: Violation of PRIMARY KEY constraint 'PK_Profiles'. Cannot insert duplicate key in object 'dbo.Profiles' like this link Entity Framework Code First - Cannot insert duplicate key in object 'dbo.T_CRProviders'? Can anyone help me? Thanks.

Community
  • 1
  • 1

3 Answers3

0

context thinks pf is new and should be inserted into the DB. Show the instantiation and configuration of context - do you have change tracking disabled?

Moho
  • 15,457
  • 1
  • 30
  • 31
  • but how do I do that? I try `context.Profiles.Attach(pf); context.Entry(pf).State = EntityState.Unchanged; context.Entry(article.CreatedBy).State = EntityState.Unchanged; context.Entry(article.ApprovedBy).State = EntityState.Unchanged;`, but it's not correct. – Nhật Quang Oct 28 '15 at 20:25
  • is your entity/insert code you posted exactly as it appears in your code? The `Profile` entity doesn't match with your model configuration and clearly EF doesn't think the `Profile` object exists in the DB, which it should based on your code. Also, lazy loading doesn't work when proxy generation is disabled – Moho Oct 28 '15 at 20:48
  • Following [this](http://stackoverflow.com/questions/5559043/entity-framework-code-first-two-foreign-keys-from-same-table), but still error, hichic, any idea for this problem? :( – Nhật Quang Oct 29 '15 at 03:05
  • I added more insert-logic code on question, I realized that error throw difference ID with inserted ID, i don't know why that's happened. :D – Nhật Quang Oct 29 '15 at 04:47
0

You need to tell EF that a Profile can be referenced multiple times by Article:

modelBuilder.Entity<Article>()
    .HasOptional(a=>a.ApprovedBy)
    .WithMany()
    .WillCascadeOnDelete(false);

modelBuilder.Entity<Article>()
    .HasOptional(a=>a.CreatedBy)
    .WithMany()
    .WillCascadeOnDelete(false);

The HasOptional could also be HasRequired if the property is mandatory (not null in the DB). Adding WithMany is a key here if you want (as you do in your sample) be able to associate a profile to multiple properties of Article (and multiple Articles too).

PS: I wrote this code by memory, so something could need adjustment.

Francesc Castells
  • 2,692
  • 21
  • 25
  • I added this code, but nothing changes, I think the problem is EF doesn't know about the existence entity. I dont know how to make `context` know Profile is existed. – Nhật Quang Oct 28 '15 at 20:36
  • I think you need it anyway. There must be something else also. With this mapping context knows already about the profile and when you do First() you get a valid profile from the DB right? Try adding Guid properties ApprovedById and CreatedById and map them as FK in the mapping in my answer (on the phone now) – Francesc Castells Oct 28 '15 at 21:12
  • Yes, that is your same case. It should work. I only see that you are reattaching the Profile. When you get an entity from the context, like you do with the Profile, you don't have to attach it (it is already attached). – Francesc Castells Oct 29 '15 at 08:54
0

Your line:

 context.Profiles.Attach(pf);

will attach the pf, however it is already tracked by EF. You just got it from the database. The pf will be reinserted by EF, and this fails. Just remove the line and you will be fine.

Add/Attach and Entity States

Peter
  • 27,590
  • 8
  • 64
  • 84