简体   繁体   English

Automapper:忽略导航属性

[英]Automapper : Ignore Navigation Properties

I've read the 2 proposed answers here and these 2 answers do not match what I want to do as they are manual work.我在这里阅读了 2 个建议的答案,这 2 个答案与我想做的不匹配,因为它们是手动工作。 So If I add another navigation property, I would need to modify AutoMapper config.因此,如果我添加另一个导航属性,我将需要修改 AutoMapper 配置。

The main goal is to make EF objects serializable, without removing lazy loading.主要目标是在不删除延迟加载的情况下使 EF 对象可序列化。

I'm looking to write something like that:我想写这样的东西:

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

I'm not looking to identify each property one by one.我不想一一识别每个属性。 It should use reflecion.它应该使用反射。 (I can manage reflection part, just need some help to get started to how I could do that code.). (我可以管理反射部分,只需要一些帮助来开始我如何编写该代码。)。

Any help or direction is welcome !欢迎任何帮助或指导!

Edit:编辑:

So I start to get somewhere with converters.所以我开始使用转换器。

/// <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)
    {
        
    }
}

But I get a lot of warnings (Which I treat as errors. And If I implement that converter, that will be horrible performances as Convert will be executed each time and I would run reflexion on each object.但是我收到了很多警告(我将其视为错误。如果我实现该转换器,那将是可怕的表现,因为每次都会执行 Convert,并且我会在每个 object 上运行反射。

So, any idea how to make it better?那么,知道如何让它变得更好吗? (Sometiomes, I need to run it on 100.000+ objects) (有时,我需要在 100.000 多个对象上运行它)

Reflection can't tell you everything about EF Core's data model. Instead you can build a hashset of all your navigation properties from your DbContext.Model ;反射无法告诉您有关 EF Core 数据 model 的所有信息。相反,您可以从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();

You'll want to store this hashset somewhere as a singleton, so you only build it once, or perhaps once per entity type.您需要将此哈希集作为 singleton 存储在某处,因此您只需构建一次,或者每个实体类型一次。

From there you probably want to dynamically build an expression tree equivalent to t => new T{ A = tA, .... };从那里你可能想动态构建一个表达式树,相当于t => new T{ A = tA, .... }; , skipping any navigation properties from your model. , 跳过 model 中的任何导航属性。

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();

Now you can call this method from your .Convert() method to create the copy of the object.现在您可以从.Convert()方法调用此方法来创建 object 的副本。

Though this trivial implementation will only make a shallow copy of each property.尽管这个微不足道的实现只会对每个属性进行浅表复制。 For some property types, such as owned types, a deep copy would be needed.对于某些属性类型,例如自有类型,需要深拷贝。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM