Work/Repository 模式单元和使用 EF 更新实体

Unit of Work/Repository pattern and updating entities using EF

我正在尝试更好地了解 UoW 模式,因为我喜欢它的概念,但目前我很难保存实体。

问题是,当我的控制器中的编辑 (POST) 操作触发时,只有父实体被保存而子实体完好无损,即我收到以下错误:

Attaching an entity of type 'Access.Models.ApplicationParameter' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

注意: 父实体 Application 可以有多个子 ApplicationParameter 实体。

控制器

    [HttpPost]
    public ActionResult Edit(ApplicationVM applicationVM)
    {
        Application application = ViewModelToModel(applicationVM);
        uow.ApplicationRepository.Update(application);

        foreach (var applicationParameterVM in applicationVM.ApplicationParameters)
        {
            ApplicationParameter applicationParameter = ViewModelToModel(applicationParameterVM);

            bool exists = uow.ApplicationParameterRepository.Exists(applicationParameter.idApplicationParameter);

            if (exists)
            {
                uow.ApplicationParameterRepository.Update(applicationParameter);
            }
            else
            {
                uow.ApplicationParameterRepository.Insert(applicationParameter);
            }
        }

        uow.Save();
        return View(applicationVM);
    }

ApplicationParameterRepository.cs(仅更新片段)

    public void Update(ApplicationParameter entity)
    {
        db.Entry(entity).State = EntityState.Modified;
    }

UOW.cs

public class UnitOfWork : IDisposable
{
    private AMEntities db = null;

    public UnitOfWork()
    {
        db = new AMEntities();
    }

    //Add all the repository handles here
    IApplicationRepository applicationRepository = null;
    IApplicationParameterRepository applicationParameterRepository = null;
    IApplicationUserRepository applicationUserRepository = null;

    //Add all the repository getters here
    public IApplicationRepository ApplicationRepository
    {
        get
        {
            if (applicationRepository == null)
            {
                applicationRepository = new ApplicationRepository(db);
            }
            return applicationRepository;
        }
    }
    public IApplicationParameterRepository ApplicationParameterRepository
    {
        get
        {
            if (applicationParameterRepository == null)
            {
                applicationParameterRepository = new ApplicationParameterRepository(db);
            }
            return applicationParameterRepository;
        }
    }
    public IApplicationUserRepository ApplicationUserRepository
    {
        get
        {
            if (applicationUserRepository == null)
            {
                applicationUserRepository = new ApplicationUserRepository(db);
            }
            return applicationUserRepository;
        }
    }

    public void Save()
    {
        db.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

问题:我应该怎么做才能保存这些子实体?

确保建立 applicationParameterapplication 之间的关系。使用导航属性或 ID 至少有三种方法可以做到这一点,但我在您的代码中没有看到这样一行:

1.

// if application have unique Id, for example it already has been saved in db
applicationParameter.ApplicationId = application.Id; 

2.

applicationParameter.Application = application;

3.

//if application is a EF-generated proxy 
application.ApplicationParameters.Add(applicationParameter); 

此外,在简单的情况下,如果实体已正确添加到上下文中,则无需设置 EntityState

据我所知,您有 2 个选择。

  1. 最简单的方法是在编辑操作开始时获取实体。如果结果为空,则添加一个新值,否则使用新值更新实体并保存。这将确保父实体和所有子实体处于相同的上下文中。

  2. 另一种方法是使用自跟踪实体。看看Self Tracking Entities。这样您就可以处理实体的状态,而不是 EF。

问题是您只将父实体的状态设置为已更新,每个子实体的状态仍会添加,因此它会尝试添加子实体而不是更新它们。