实体框架通用表达式以有选择地隐藏属性 [英] Entity Framework generic expression to selectively hide property

查看:59
本文介绍了实体框架通用表达式以有选择地隐藏属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想选择性地忽略表中的属性。
我有一个公开以下方法的API。

I would like to selectively ignore a property from a table. I have an API which exposes the following methods.

public interface IReadService 
{
   FullDTO Get();
   HeaderDTO[] GetList();
}

我的数据结构如下:

public ServiceDTO : ServiceHeaderDTO 
{
    public string LargeXMLData { get; set; }
}

public ServiceHeaderDTO 
{
    public int Id { get; set; }    
    public string Description { get; set; }
    //.... Other properties
}

我有一些服务类似的问题,因此在某些情况下,我希望能够忽略XML属性,因此,我不会花费额外的时间发送将被忽略的大字符串属性。

I have a few services which have similar issues, So I would like to be able to ignore the XML property in some cases, so I'm not using extra time to send a large string property which will be ignored.

通常您可以这样写来隐藏属性

Normally you might write something like this to hide a property

 var entities = context.Services.Select(x => 
    new Service { Id = Id, Description = Description, LargeXMLData = "" }).ToArray();

 var dtos = this.AdaptToDTO(entities);

现在,如果我必须在单个服务中执行此操作,则可以,但是当您有20个服务重复逻辑时

Now this would be fine if I had to do this in a single service, but when you have 20 services duplicating the logic it gets annoying.

我希望能够说:

 var entities = context.Services.Excluding(x => x.LargeXMLData).ToArray();
var dtos = this.AdaptToHeaderDTO(entities);

编辑::我没有使用自动映射器。我们的许多代码都有无法转换为表达式的映射。我不想指定地图

I'm not using automapper. Alot of our code has mappings which cannot translate to expressions. I do not want to have to specify maps

有没有一种简单的方法可以从查询中排除属性?

Is there a simple way I can exclude a property from a query? Without having to manually build maps.

最好是一种使用EF内部现有映射将实体映射到db对象的方法

Preferably a way which uses the existing mappings internal to EF which maps the entity to the db object

推荐答案


通常,您可能会写这样的东西来隐藏属性

Normally you might write something like this to hide a property

var entities = context.Services.Select(x => 
   new Service { Id = Id, Description = Description, LargeXMLData = "" })


如果您可以手动执行此操作,则应该使用完全相同的概念自动进行操作,几乎不需要反思和 Expression API。

If you can do that manually, it should be doable automatically using the exact same concept, with little reflection and Expression APIs.

但是请注意,这仅适用于EF Core,因为EF6不支持投影到实体类型,例如新服务{...} ,在运行时投影为动态类型并非易事,而且还会破坏DTO映射。

But note that this woult work only for EF Core, since EF6 does not support projecting to entity types, like new Service { ... } here, and projecting to dynamic types at runtime is not trivial and also will break the DTO mapping.

有人说,以下是示例实现前述概念:

With that being said, following is a sample implementation of the aforementioned concept:

public static partial class QueryableExtensions
{
    public static IQueryable<T> Excluding<T>(this IQueryable<T> source, params Expression<Func<T, object>>[] excludeProperties)
    {
        var excludeMembers = excludeProperties
            .Select(p => ExtractMember(p.Body).Name)
            .ToList();
        if (excludeMembers.Count == 0) return source;
        // Build selector like (T e) => new T { Prop1 = e.Prop1, Prop2 = e.Prop2, ... }
        // for each public property of T not included in the excludeMembers list,
        // which then will be used as argument for LINQ Select
        var parameter = Expression.Parameter(typeof(T), "e");
        var bindings = typeof(T).GetProperties()
            .Where(p => p.CanWrite && !excludeMembers.Contains(p.Name))
            .Select(p => Expression.Bind(p, Expression.MakeMemberAccess(parameter, p)));
        var body = Expression.MemberInit(Expression.New(typeof(T)), bindings);
        var selector = Expression.Lambda<Func<T, T>>(body, parameter);
        return source.Select(selector);
    }

    static MemberInfo ExtractMember(Expression source)
    {
        // Remove Convert if present (for value type properties cast to object)
        if (source.NodeType == ExpressionType.Convert)
            source = ((UnaryExpression)source).Operand;
        return ((MemberExpression)source).Member;
    }
}

用法将完全符合要求:

var entities = context.Services.Excluding(x => x.LargeXMLData).ToArray();

尽管如此,但问题在于它会自动包含导航属性和/或未映射的属性。

The problem with this though is that it will automatically "include" navigation properties and/or unmapped properties.

因此,最好使用EF模型元数据而不是反射。问题在于,当前EF Core无法提供一种很好的公开方式来插入其基础结构,也无法访问 DbContext (因此是 Model )来自 IQueryble ,因此必须将其作为自定义方法的参数传递:

So it would be better to use EF model metadata instead of reflection. The problem is that currently EF Core does not provide a good public way of plugging into their infrastructure, or to get access to DbContext (thus Model) from IQueryble, so it has to be passed as argument to the custom method:

public static IQueryable<T> Excluding<T>(this IQueryable<T> source, DbContext context, params Expression<Func<T, object>>[] excludeProperties)
{
    var excludeMembers = excludeProperties
        .Select(p => ExtractMember(p.Body).Name)
        .ToList();
    if (excludeMembers.Count == 0) return source;
    // Build selector like (T e) => new T { Prop1 = e.Prop1, Prop2 = e.Prop2, ... }
    // for each property of T not included in the excludeMembers list,
    // which then will be used as argument for LINQ Select
    var parameter = Expression.Parameter(typeof(T), "e");
    var bindings = context.Model.FindEntityType(typeof(T)).GetProperties()
        .Where(p => p.PropertyInfo != null && !excludeMembers.Contains(p.Name))
        .Select(p => Expression.Bind(p.PropertyInfo, Expression.MakeMemberAccess(parameter, p.PropertyInfo)));
    var body = Expression.MemberInit(Expression.New(typeof(T)), bindings);
    var selector = Expression.Lambda<Func<T, T>>(body, parameter);
    return source.Select(selector);
}

这使用法不是那么优雅(但能胜任工作):

which makes the usage not so elegant (but doing the job):

var entities = context.Services.Excluding(context, x => x.LargeXMLData).ToArray();

现在唯一剩下的潜在问题是阴影属性,但无法通过投影处理,因此无法使用此技术对于具有阴影属性的实体。

Now the only remaining potential problem are shadow properties, but they cannot be handled with projection, so this technique simply cannot be used for entities with shadow properties.

最后,上述的纯EF Core替代方法是将 LargeXMLData 放入单独的单个属性 ; entity并使用表拆分将其映射到同一张表。然后,您可以使用常规的 Include 方法将其包含在需要的位置(默认情况下将被排除)。

Finally, the pure EF Core alternative of the above is to put the LargeXMLData into separate single property "entity" and use table splitting to map it to the same table. Then you can use the regular Include method to include it where needed (by default it would be excluded).

这篇关于实体框架通用表达式以有选择地隐藏属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆