【问题标题】:Mapping DTO to Entity while TrackingChanges with AutoMapper and EFCore在使用 AutoMapper 和 EF Core 跟踪更改时将 DTO 映射到实体
【发布时间】:2017-06-07 14:49:58
【问题描述】:

对 AutoMapper 和 EFCore 来说非常新,所以可能比我在这里咀嚼的更多,但我想我会试一试,但失败了,也许不可能,但如果是的话,也许有人可以指出我正确的方向。

我正在使用 AutoMapper 将我的实体转换为 DTO,这很好。

现在我想将我的 DTO 转换为实体并让 EntityFramework 跟踪所有更改(属性更新、从列表中删除子对象等),但这可能吗?

所以如果我有一个简单的 PUT 方法,我想做这样的事情,然后让 automapper 整理剩下的

public async Task<IActionResult> PutAsync([FromBody] MyDTO model)
{
    // GET OBJECT FROM DATABASE HERE
    var dbValue = _repos.GetMyObjectUsingId(999);

    // apply updates
    _mapper.Map<MyDTO, MyObject>(dbValue, model);

}

【问题讨论】:

    标签: c# asp.net .net entity-framework automapper


    【解决方案1】:

    您可以创建一个适配器类,以便您的更改集保持不变: 你有没有从接口继承 DTO

    假设你有一个 DTO:

    public interface IFooDTO
    {
       int Id { get; set;}
       string Name { get; set;}
    }
    
    public class FooDTO : IFooDTO
    {
       public int Id { get; set;}
       public string Name { get; set; }
    }
    

    那么你就有了你的实体

    public class FooEntity
    {
       public int Id;
       public string name;
    }
    

    然后创建继承自 foo 接口的适配器:

    public class FooAdapter : IFooDTO
    {
       FooEntity entity;
       FooAdapter(FooEntity entity)
       {
          this.entity = entity;
       }
    
       public int Id 
       {
          get {return this.entity.Id;}
          set {/*Do nothing set by EF*/}
       }
    
       public string Name
       {
           get {return this.entity.Name;}
           set {this.entity.Name = value; }
       }
    
       public void Apply(FooDTO foo)
       {
            //I don't remember if this is the correct order but you get the gyst
            this._mapper.Map<IFooDTO, IFooDTO>(this, foo);
       }
    }
    

    然后你只需要在映射器中从你的 Idto 映射到你的 dto。

    用法:

     public ActionResult PutFoo(int id, FooDTO foo)
     {
         var entity = context.Foos.FirstOrDefault(x => x.Id == id);
         var adapter = new FooAdapter(entity);
         adapter.Apply(foo);
         //Entity has been updated and has original changes
     }
    

    编辑

    Children 工作正常,只需使用相同的适配器模式,setter 的代码很多,但是

    public BarDTO childBar
    {
       get { return new BarAdapter(this.entity.Bar).ToDTO(); }
       set { new BarAdapter(this.entity.Bar).Apply(value) }
    }
    

    同步实体:

    public static void Sync<TEntity, TEntityKey, TDTO>(this ICollection<TEntity> entityCollection, ICollection<TDTO> dtoCollection,
        Func<TEntity> entityConstructor, Action<TDTO, TEntity> copyAction,
        Action<TEntity> onDeleteAction,
        Func<TEntity, TEntityKey> entityKeySelector, 
        Func<TDTO, TEntityKey> dtoKeySelector)
        where TEntity : class
        where TEntityKey : struct
    {
        dtoCollection = dtoCollection ?? new TDTO[] { };
        except = except ?? new TEntityKey[] { };
    
        var dtoIds = dtoCollection.Select(dto => dtoKeySelector(dto)).ToHashSet();
        foreach (var entity in entityCollection.Where(x => false == dtoIds.Contains(entityKeySelector(x))).ToArray())
        {
            onDeleteAction(entity);
            entityCollection.Remove(entity);
        }
    
        var entityCollectionMap = entityCollection.ToDictionary(x => entityKeySelector(x));
    
        foreach (var dtoItem in dtoCollection)
        {
            TEntity entity = null;
            if (dtoKeySelector(dtoItem).HasValue)
            {
                entity = entityCollectionMap.ContainsKey(dtoKeySelector(dtoItem)) ? entityCollectionMap[dtoKeySelector(dtoItem)] : default(TEntity);
    
            }
    
            if (null == entity)
            {
                entity = entityConstructor();
                entityCollection.Add(entity);
            }
            copyAction(dtoItem, entity);
        }
    }
    

    【讨论】:

    • 这很有趣...谢谢。你也可以对对象中的子对象和列表进行这项工作吗?在列表中,如果项目不在 DTO 中(匹配 ID),自动映射器会从列表中删除它吗??
    • @user2736022 我很高兴这个答案有用,我已经更新了子对象的答案,希望这能满足您的原始帖子。你可以有一个全局同步方法来做你上面所说的只是联合并更新 ICollection 只是很多额外的代码要弄清楚,如果我有时间晚了,我会再进行一次更新
    • 虽然get { return this.entity.Bars.Select(b =&gt; new BarAdapter(b).ToDTO()).ToList(); }getter 很简单
    • 如果您能告诉我有关收藏的信息,我们将不胜感激......再次感谢!
    • @user2736022 这应该是伪同步代码给或带你必须玩它。如果这应该满足您的原始问题,请标记为答案并投票
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多