实体框架使用 Codefirst、通用存储库、工作单元模式保存多对多关系

Posted

技术标签:

【中文标题】实体框架使用 Codefirst、通用存储库、工作单元模式保存多对多关系【英文标题】:Entity Framework Saving Many-to-Many Relationship with Codefirst, Generic Repository, Unit of Work Pattern 【发布时间】:2014-02-11 17:57:29 【问题描述】:

我在获取多对多关系以使用 EF Codefirst 正确保存时遇到问题。我已经正确地为我的类建模并使用 Fluent-API 正确地为连接表建模。我认为这个问题与使用断开连接的 DTO 有关。当我保存对父实体 (Condo) 的更改时,父实体(例如 Title 和 UserId)上的标量属性会正确保存,但对子实体 (Amenities) 的更改不会保存到多对多表中。

这是有助于澄清事情的代码流:

public ICommandResult Execute(CreateOrUpdateCondoCommand command)
    
        ICollection<Amenity> amenities = new List<Amenity>();

        foreach (var item in command.Amenities)
        
            Amenity amenity = new Amenity  AmenityId = item.AmenityId ;
            amenities.Add(amenity);
        

        var condo = new Condo
        
            [...other properties]
            Title = command.Title,               
            Amenities = amenities             
        ;                  

        if (condo.CondoId == 0)
        
            condoRepository.Add(condo);
        
        else
        
            condoRepository.Update(condo);
        

        unitOfWork.Commit();

        return new CommandResult(true);
    

    /// <summary>
    /// Updates the entity.
    /// </summary>
    /// <param name="entity">The entity</param>
    public virtual void Update(T entity)
    
        dbset.Attach(entity);
        dataContext.Entry(entity).State = EntityState.Modified;
    

我能够通过创建 condoRepository.UpdateCondo(condo) 方法来让事情正常进行,如下所示:

/// <summary>
    /// Method for updating a condo
    /// </summary>   
    /// <param name="condo">The condo to update</param>
    public void UpdateCondo(Condo condo)
     
        var updatedCondo = this.DataContext.Set<Condo>().Include("Amenities")
            .Single(x => x.CondoId == condo.CondoId);

        // Set the attributes
        [other properties here...]
        updatedCondo.Title = condo.Title;           

        updatedCondo.Amenities.Clear();

        foreach (var amenity in condo.Amenities)
        
            var amenityToAttach = this.DataContext.Amenities.Single(x => x.AmenityId == amenity.AmenityId);
            updatedCondo.Amenities.Add(amenityToAttach);                
        

        this.Update(updatedCondo);
    

但是,是否有更好的通用方法来执行此操作,并且不需要我每次需要保存多对多关系时都创建自定义“更新”方法?这个https://***.com/a/11169307/3221076 的答案有助于澄清我认为的问题所在,但我不确定如何实施更通用的方法。

谢谢, 杰森

【问题讨论】:

【参考方案1】:

试试这个教程,希望对你有帮助。

http://www.asp.net/mvc/tutorials/getting-started-with-ef-5-using-mvc-4/creating-a-more-complex-data-model-for-an-asp-net-mvc-application.

【讨论】:

【参考方案2】:

我不知道如何让它完全通用,但我做过类似的事情,它允许您添加、删除和修改主从关系。 (我认为最初的想法来自 Steven Sanderson,但我找不到。)这段代码在我的 DbContext 中。希望对您有所帮助。

public Condo UpdateSheet(Condo condo) 
    Guard.IsNotNull(condo);

    List<long> retainedAmenityIds = (from amenity in condo.Amenities
                                      where amenity.Id != 0
                                      select amenity.Id).ToList();
    List<Amenity> retainedAmenities = (from amenity in condo.Amenities
                                        where amenity.Id != 0
                                        select amenity).ToList();
    string sql = String.Format("SELECT Id FROM 0 WHERE CondoId = 1", GetTableName<Amenity>(), condo.Id);
    List<long> deletedAmenityIds = Database.SqlQuery<long>(sql).Except(retainedAmenityIds).ToList();
    if (0 < deletedAmenityIds.Count) 
        foreach (var id in deletedAmenityIds) 
            Amenity amenity = Amenities.Single(a => a.Id == id);
            Amenities.Remove(amenity);
        
    
    List<Amenity> addedAmenities = condo.Amenities.Except(retainedAmenities).ToList();
    foreach (var amenity in addedAmenities) 
        amenity.SheetId = condo.Id;
        Entry(amenity).State = EntityState.Added;
    
    foreach (var amenity in retainedAmenities)  Entry(amenity).State = EntityState.Modified; 
    Entry(condo).State = EntityState.Modified;
    SaveChanges();
    return condo;

【讨论】:

以上是关于实体框架使用 Codefirst、通用存储库、工作单元模式保存多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

使用带有实体框架 6 的存储库模式更新记录

具有实体框架 4.1 和父/子关系的存储库模式

关于EF实体框架中的 dbContext

在实体框架中使用存储过程(代码优先)

EF实体框架之CodeFirst八

EF实体框架之CodeFirst六