Automapper:忽略导航属性

Automapper : Ignore Navigation Properties

我在这里阅读了 2 个建议的答案,这 2 个答案与我想要做的不符,因为它们是手动工作。因此,如果我添加另一个导航 属性,我将需要修改 AutoMapper 配置。

主要目标是在不删除延迟加载的情况下使 EF 对象可序列化。

我正在寻找类似的东西:

cfg.CreateMap<EntityA, EntityA>(new IgnoreNavigationsProperties());

我不想一一识别每个 属性。它应该使用反射。 (我可以管理反射部分,只是需要一些帮助才能开始编写该代码。)

欢迎任何帮助或指导!

编辑:

所以我开始使用转换器。

/// <summary>
/// 
/// </summary>
/// <typeparam name="T"></typeparam>
public class EfEntityConverter<T> : ITypeConverter<T, T> where T : BaseEntity
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="source"></param>
    /// <param name="destination"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public T Convert(T source, T destination, ResolutionContext context)
    {
        
    }
}

但我收到了很多警告(我将其视为错误。如果我实现该转换器,那将是可怕的表现,因为每次都会执行 Convert,我会对每个对象进行 运行 反射.

那么,知道如何让它变得更好吗? (有时,我需要在 100.000 多个对象上 运行 它)

反射无法告诉您有关 EF Core 数据模型的所有信息。相反,您可以从 DbContext.Model;

构建所有导航属性的哈希集
using var scope = provider.CreateScope();

var db = scope.GetRequiredService<MyContext>();
var model = db.Model;
var entities = model.GetEntityTypes();

var navigationProperties = entities
    .SelectMany(e => e.GetNavigations())
    // ignore owned type navigations, treat them as part of the data;
    .Where(n => !n.ForeignKey.IsOwnership && n.PropertyInfo!=null)
    .Select(n => n.PropertyInfo)
    .Concat(
        entities
            .SelectMany(e => e.GetSkipNavigations())
            .Where(n => n.PropertyInfo!=null)
            .Select(n => n.PropertyInfo)
    )
    .ToHashSet();

您需要将此哈希集作为单例存储在某处,因此您只构建一次,或者每个实体类型一次。

从那里您可能想要动态构建一个等价于 t => new T{ A = t.A, .... }; 的表达式树,跳过模型中的任何导航属性。

var parm = Expression.Parameter(typeof(T), "t");

var expr = Expression.Lambda<Func<T,T>>(
    Expression.MemberInit(
        Expression.New(typeof(T)),
        typeof(T)
            .GetProperties()
            .Where(p => p.CanRead && p.CanWrite)
            .Except(navigationProperties)
            .Select(p => Expression.Bind(p, Expression.MakeMemberAccess(parm, p)))
    ),
    parm
);

var factoryMethod = expr.Compile();

现在您可以从 .Convert() 方法中调用此方法来创建对象的副本。

虽然这个简单的实现只会对每个 属性 进行浅表复制。对于某些 属性 类型,例如自有类型,需要深度复制。