为了账号安全,请及时绑定邮箱和手机立即绑定

C# LINQ - 根据运行时定义的属性选择动态对象

C# LINQ - 根据运行时定义的属性选择动态对象

C#
波斯汪 2022-01-09 10:10:41
我一直在尝试创建一个表达式,它可以将强类型的 EF Core 实体投影到一个动态对象中,该对象包含一个在运行时使用 REST API 调用定义的列表。这是我到目前为止所拥有的:Expression<Func<Message, dynamic>> DynamicFields(IEnumerable<string> fields){    var xParameter = Expression.Parameter(typeof(Message), "o");    var xNew = Expression.New(typeof(ExpandoObject));    var bindings = fields.Select(o => {        var mi = typeof(Message).GetProperty(o);        var xOriginal = Expression.Property(xParameter, mi);        return Expression.Bind(mi, xOriginal);    });    var xInit = Expression.MemberInit((dynamic)xNew, bindings);    return Expression.Lambda<Func<Message, dynamic>>(xInit, xParameter);}感觉就像我非常接近,但这在运行时会爆炸,说明 X 属性不是 ExpandoObject 的成员。我尝试改变动态和 ExpandoObject 的使用,但似乎没有任何效果 - 这甚至可能吗?如果我为 Message 切换出 dyanmic / ExpandoObject,它工作得很好,但会返回 Message 类的一个实例,其所有属性都为默认值。以前有人做过吗?
查看完整描述

2 回答

?
www说

TA贡献1775条经验 获得超8个赞

不能直接投影到ExpandoObject/ dynamic。


但是您可以在运行时投影到动态创建的类型。参见例如Microsoft.EntityFrameworkCore.DynamicLinq包 - 动态数据类。


如果您以 nuget 的形式安装该软件包(您可以自己开发类似的功能,但您应该基本上实现该软件包的所有功能),则可以按如下方式实现该方法:


using System.Linq.Dynamic.Core;


static Expression<Func<TSource, dynamic>> DynamicFields<TSource>(IEnumerable<string> fields)

{

    var source = Expression.Parameter(typeof(TSource), "o");

    var properties = fields

        .Select(f => typeof(TSource).GetProperty(f))

        .Select(p => new DynamicProperty(p.Name, p.PropertyType))

        .ToList();

    var resultType = DynamicClassFactory.CreateType(properties, false);

    var bindings = properties.Select(p => Expression.Bind(resultType.GetProperty(p.Name), Expression.Property(source, p.Name)));

    var result = Expression.MemberInit(Expression.New(resultType), bindings);

    return Expression.Lambda<Func<TSource, dynamic>>(result, source);

}


查看完整回答
反对 回复 2022-01-09
?
莫回无

TA贡献1865条经验 获得超7个赞

我对如何在不使用Expressions. 很显然,因为Reflection在C#中是缓慢,因为在此进一步解释第二条,我决定用一个叫库FastMember去获得对象的属性。


==============


安装 FastMember


在 PackageManagerConsole 中运行以下命令来安装库。


Install-Package FastMember -Version 1.4.1

写函数


我们的函数/方法将成为任何泛型类型的扩展方法,例如TEntity,它必须是 aclass并且将字段数组作为参数告诉函数TEntity要返回哪些属性。


using FastMember;

using Dynamic;

using System;

using System.Linq;

using System.Collections.Generic


public static class Utilities

{

    public static dynamic GetDynamicFields<TEntity>(this TEntity entity, params string[] fields)

        where TEntity : class

    {

        dynamic dynamic = new ExpandoObject();

        // ExpandoObject supports IDictionary so we can extend it like this

        IDictionary<string,object> dictionary = dynamic as IDictionary<string,object>;


        TypeAccessor typeAccessor = TypeAccessor.Create(typeof(TEntity));

        ObjectAccessor accessor = ObjectAccessor.Create(entity);

        IDictionary<string,Member> members = typeAccessor.GetMembers().ToDictionary(x => x.Name);


        for (int i = 0; i < fields.Length; i++)

        {

            if (members.ContainsKey(fields[i]))

            {

                var prop = members[fields[i]];


                if (dictionary.ContainsKey(prop.Name))

                    dictionary[prop.Name] = accessor[prop.Name];

                else

                    dictionary.Add(prop.Name, accessor[prop.Name]);

            }

        }


        return dynamic;

    }

}

如何使用


使用该方法很简单,可以这样做:


Message msg = new Message();

dynamic result = msg.GetDynamicFields("Id","Text");

在 c# 中,dynamic是ExpandoObject. 在查看代码后,ExpandoObject我意识到它实现了IDictionary<string,object>. 因此,这意味着我们可以扩展它并在运行时向它添加动态属性,从而使我们更容易。


测试用例时间


使用NUnit在上述方法上运行测试用例大约需要 40毫秒,这听起来足够好。


希望此解决方案对您有用,或者可能为您提供有关如何解决问题的想法。


查看完整回答
反对 回复 2022-01-09
  • 2 回答
  • 0 关注
  • 231 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信