如何让AutoMapper使用接口映射和具体映射

How to get AutoMapper to use interface mappings and concrete mappings

我试图让 AutoMapper 从接口级别(DRY 方法)更新我的对象的所有属性,但只有接口属性正在更新:

我已经尝试研究和弄乱 AutoMapper 文档中的包含路径,但它没有按照我预期的方式工作,并且不确定它是否相关:https://github.com/AutoMapper/AutoMapper/wiki/Mapping-inheritance

这是我的模型:

public interface IEntity {
    int Id { get; set; }
}

public interface IDatedEntity : IEntity {
    DateTime DateCreated { get; set; }
    DateTime DateModified { get; set; }
}

public interface IBMSEntity : IDatedEntity {
    string Notes { get; set; }
    bool Active { get; set; }

}

public class ProjectStatus : IBMSEntity {
    public string Name { get; set; }

    public StatusType Type { get; set; }

    public bool IsDefault { get; set; }

    #region Interface Properties
    public int Id { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime DateModified { get; set; }
    public string Notes { get; set; }
    public bool Active { get; set; }
    #endregion
}

这里是接受 IDatedEntity 并将其映射以便使用 EntityFramework 进行更新的代码部分:

public class BaseDatedEntityUpdate<T> : BaseEntityUpdate<T>, IUpdate<T> where T : class, IDatedEntity {

    public BaseDatedEntityUpdate(BMSContext context, IValidationDictionary validation) : base(context, validation) { }

    public override T Update(T entity) {
        //setting - I'd like to put this on the base mappings also
        var now = DateTime.Now;
        entity.DateModified = now;

        //mapping
        var original = dbSet.Find(entity.Id);
        Mapper.Map(entity, original);

        return original;
    }
}

我的地图配置有 2 个选项:

选项 1: 我将我的忽略属性放在基本类型上:

        //testing base/interface maps
        Mapper.CreateMap<IDatedEntity, IDatedEntity>()
            .ForMember(x => x.DateCreated, y=> {
                y.UseDestinationValue();
                y.Ignore();
            });

        Mapper.CreateMap<ProjectStatus, ProjectStatus>();

这不会忽略 DateCreated,所以我必须将 Mapper.Map(entity, original); 更改为 Mapper.Map<IDatedEntity, IDatedEntity>(entity, original); 但是我遇到了问题,我的项目状态属性(例如名称、类型和 IsDefault)没有被映射跨越。

选项 2: 我将映射放在我的具体类型上:

        //testing base/interface maps
        Mapper.CreateMap<IDatedEntity, IDatedEntity>();

        Mapper.CreateMap<ProjectStatus, ProjectStatus>()
            .ForMember(x => x.DateCreated, y=> {
                y.UseDestinationValue();
                y.Ignore();
            });

这在技术上按照我想要的方式工作,但它不是很干。

问题:

有没有办法将映射应用到基类型并保持我的代码干燥? IE。我想使用选项 1,从而映射所有 ProjectStatus(以及 IDatedEntity 的任何其他派生)属性 - 无需创建详细的具体类型映射?

也许它不像你喜欢的那么枯燥,也许有点矫枉过正;p 但你可以用一点反射和一两个循环来映射每种类型。

您也可以只指定一个程序集 - 我只是采用了一种蛮力方法,以防您需要几个程序集。

public interface ITest
{
    DateTime CreatedAt { get; set; }
}

public class Test : ITest
{
    public string Guid { get; set; }
    public DateTime CreatedAt { get; set; }

    public Test()
    {
        CreatedAt = DateTime.Now;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var mapMethod = typeof (Program).GetMethod("Map", BindingFlags.Static | BindingFlags.Public);
        foreach (var genericMapMethod in from assembly in AppDomain.CurrentDomain.GetAssemblies()
                                         from type in assembly.DefinedTypes
                                         where typeof (ITest).IsAssignableFrom(type)
                                         select mapMethod.MakeGenericMethod(type))
        {
            genericMapMethod.Invoke(null, new object[0]);
        }

        var test = new Test {Guid = Guid.NewGuid().ToString()};
        Thread.Sleep(1);
        var test2 = new Test();

        Mapper.Map(test, test2);

        Console.WriteLine(test2.Guid);
        Console.WriteLine(test.Guid == test2.Guid);
        Console.WriteLine(test.CreatedAt == test2.CreatedAt);

        Console.WriteLine("Done");
        Console.ReadLine();
    }

    public static void Map<T>() where T : ITest
    {
        Mapper.CreateMap<T, T>()
        .ForMember(x => x.CreatedAt, y => {
            y.UseDestinationValue();
            y.Ignore();
        });
    }
}

让我知道这是否是朝着正确方向迈出的一步。
希望对你有帮助。