如何将两个不同的对象插入数据库,但检查它们是否已经存在抛出“InvalidOperationException”?

时间:2017-06-27 12:37:43

标签: c# entity-framework invalidoperationexception

情况草图

我开发了一个系统,其中可以将许多材料(代码Matetiaal添加到广告(代码Zoeker中。这种关系很多很多。在这里你得到了我的课程结构。

+-------------------------------------+   many to many   +-------------------------------+
|                Zoeker               | <--------------> |           Materiaal           |
+------------+------------------------+                  +---------+---------------------+
| ID         | int                    |                  | ID      | int                 |
| Materialen | ICollection<Materiaal> |                  | Zoekers | ICollection<Zoeker> |
| Titel      | string                 |                  | Naam    | string              |
+------------+------------------------+                  +---------+---------------------+

我将使用下一个伪代码添加新材料和广告:

void Insert(Zoeker zoeker, string[] materialen) 
{
    foreach (string m in materialen)
    {
        Materiaal mat = materiaalRepo.GetByName(m);

        if (mat == null)
        {
            mat = materiaalRepo.Insert(new Materiaal(m));
        }

        zoeker.Materialen.Add(mat);
    }

    zoekerRepo.Insert(zoeker);
}

如果必须添加新广告Zoeker,我将检查数据库中是否已存在该材料。如果材料不存在,方法materialenRepo.GetByName(string)将返回null。如果是这样,请将新材料插入数据库并返回Materiaal - 对象。之后或materialenRepo.GetByName(string)未返回null,请将该材料添加到广告的Materialen - 属性中,然后将其插入数据库。

要使用我的应用程序连接数据库,我使用Entity Framework。

我的问题

当我在下面的标题中使用代码时,我有这个例外:

  

System.InvalidOperationException多个IEntityChangeTracker实例无法引用实体对象。

public virtual TEntity Insert(TEntity entity)
{
    return dbSet.Add(entity); // <-- on this line
}

调用zoekerRepo.Insert(zoeker);时,标记的行会抛出异常。

我的代码

在这里你可以找到我的代码。

服务

public class ZoekService
{
    public int InsertZoeker(Zoeker zoeker, ApplicationUser user, string materialenString)
    {
        return InsertZoeker(zoeker, user, materialenString.Split(','));
    }

    public int InsertZoeker(Zoeker zoeker, ApplicationUser user, string[] materialen)
    {
        zoeker.Gebruiker = user;
        zoeker.Materialen = new List<Materiaal>();

        using (ApplicationDbContext ctx = new ApplicationDbContext())
        {
            ZoekerRepository zoekerRepo = new ZoekerRepository(ctx);
            MateriaalRepository materiaalRepo = new MateriaalRepository(ctx); 
            // ^^^^ See update one

            foreach (string m in materialen)
            {
                Materiaal materiaal = materiaalRepo.GetByNaam(m);

                if (materiaal == null)
                {
                    materiaal = materiaalRepo.Insert(new Materiaal(m)); 
                    // ^ See update three
                }

                zoeker.Materialen.Add(materiaal);
            }

            zoekerRepo.Insert(zoeker); 
            zoekerRepo.SaveChanges(); // <-- Pointer didn't come on this line.

            return zoeker.ID;
        }
    }
}

存储库

ZoekerRepository延伸GenericRepository<Zoeker>

public class ZoekerRepository : GenericRepository<Zoeker>
{
    public ZoekerRepository()
    { }

    public ZoekerRepository(ApplicationDbContext ctx): base(ctx)
    { }

    // Some other methods I've add or will override.
}

MateriaalRepository延伸GenericRepository<Materiaal>

public class MateriaalRepository : GenericRepository<Materiaal>
{
    public MateriaalRepository()
    { }

    public MateriaalRepository(ApplicationDbContext ctx): base(ctx)
    { }

    public Materiaal GetByNaam(string naam)
    {
        return (from m in dbSet
                where m.Naam.Equals(naam)
                select m).SingleOrDefault<Materiaal>();
    }

    // Some other methods I've add or will override.
}

GenericRepository<TEntity>

public class GenericRepo<TEntity> where TEntity : class
{ 
    internal ApplicationDbContext context;
    internal DbSet<TEntity> dbSet;

    public GenericRepo()
    {
        context = new ApplicationDbContext();
        dbSet = context.Set<TEntity>();
    }

    public GenericRepo(ApplicationDbContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

    public virtual TEntity Insert(TEntity entity)
    {
        return dbSet.Add(entity); // <-- after `zoekerRepo.Insert(Zoeker)` the exception
                                  //       will be thrown on this line.
    }

    public void SaveChanges()
    {
        try
        {
            context.SaveChanges();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

我做了什么

当然我已尝试过其他代码,但所有代码都提供相同的异常。我将创建我的代码,以便材料的存储库使用名为GenericRepo<TEntity> 的相同通用类与广告存储库中的存储库分隔(除了我要添加或覆盖的东西)。我将尽可能多地重用它。一次为GenericRepo<Zoeker>,另一次为GenericRepo<Materiaal>

我的问题

我怎么能解决这个错误?

更新一个

此问题与entity object cannot be referenced by multiple instances of IEntityChangeTracker. while adding related objects to entity in Entity Framework 4.1不重复,因为我使用了与接受的答案相同的上下文:

  

您可以通过在服务类之外创建上下文并在两个服务中注入和使用它来解决此问题:

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance 

请参阅上面代码的评论。

更新两个

在这里,我已经完成了我的代码的新部分。

控制器

public class DeelController : Controller
{
    private UserManager<ApplicationUser> UserManager { get; set; }
    private ZoekService _zoekservice = new ZoekService();

    public DeelController()
    {
        this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(ApplicationDbContext));
    }

    [HttpGet]
    [Authorize]
    public ActionResult Nieuw()
    {
        return View(new Zoeker());
    }

    [HttpPost]
    [Authorize]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Nieuw(Zoeker zoeker)
    {
        _zoekservice.InsertZoeker(zoeker, await UserManager.FindByNameAsync(User.Identity.Name), zoeker.MaterialenString);

        return RedirectToAction(nameof(Index));
    }
}

Zoeker延伸Post

public class Zoeker: Post
{
    public virtual ICollection<Materiaal> Materialen { get; set; }
    public virtual ICollection<ApplicationUser> Delers { get; set; }

    [NotMapped]
    public string MaterialenString { get; set; } // <-- contains a comma separated
                                                 //       list of all materials
}

Post

public class Post
{
    [Key]
    public int ID { get; set; }
    public string Titel { get; set; }
    public string Omschrijving { get; set; }
    public virtual ApplicationUser Gebruiker { get; set; }
    public string GebruikerID { get; set; }
    public DateTime StartPeriode { get; set; }
    public DateTime? Verwijderd { get; private set; }
    public DateTime? Opgelost { get; private set; }
    public DateTime? Belangerijk { get; private set; }
    public virtual ICollection<Antwoord> Antwoorden { get; set; }
}

更新三

我也尝试用这段代码替换上面代码中的注释行,但没有解决这个错误:

materiaal = /*materiaalRepo.Insert(*/ new Materiaal(m); //);

如果我使用不存在的资料,使用更新三的代码,我又有了错误。

更新四个

如果我使用观察者,我会看到:

Watchers shows a field _entityWrapper for the existing material

Material[0]获得_entityWrapper字段EntityWrapperWithoutRelationships<System.Data.Entity.DynamicProxies.Materiaal_E0DDEFDA63D33C75C64324662B40FDC414EC19CA4AD6BF18443B63FC60015374>注意:此资料已存在于数据库中。在这里我也有同样的错误。

更新五个

我也取代了`InsertZoeker(Zoeker

public int InsertZoeker(Zoeker zoeker, ApplicationUser user, string[] materialen)
{
    Zoeker newZoeker = new Zoeker()
    {
        Gebruiker = user,
        StartPeriode = DateTime.Now,
        Materialen = new List<Materiaal>(),
        Titel = zoeker.Titel,
        Omschrijving = zoeker.Omschrijving
    };

    using (ApplicationDbContext ctx = new ApplicationDbContext())
    {
        ZoekerRepository zoekerRepo = new ZoekerRepository(ctx);
        MateriaalRepository materiaalRepo = new MateriaalRepository(ctx);

        foreach (string m in materialen)
        {
            Materiaal materiaal = materiaalRepo.GetMateriaalByNaam(m);

            if (materiaal == null)
            {
                materiaal = /*materiaalRepo.Insert(*/ new Materiaal(m); //);
            }

            newZoeker.Materialen.Add(materiaal);
        }

        zoekerRepo.Insert(newZoeker);
        zoekerRepo.SaveChanges();

        return zoeker.ID;
    }
}

Can you do anything right?

1 个答案:

答案 0 :(得分:1)

我很愚蠢,我正在使用其他上下文让用户进入我的DeelController。我在我的问题中删除了该代码。

@mwwills:这确实是一个重复的问题。

@everyone_else:对不起,我已经做到了。