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()
方法中调用此方法来创建对象的副本。
虽然这个简单的实现只会对每个 属性 进行浅表复制。对于某些 属性 类型,例如自有类型,需要深度复制。
我在这里阅读了 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()
方法中调用此方法来创建对象的副本。
虽然这个简单的实现只会对每个 属性 进行浅表复制。对于某些 属性 类型,例如自有类型,需要深度复制。