我一直在尝试创建一个表达式,该表达式可以将强类型的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的用法,但似乎无济于事-甚至有可能吗?
如果我将dyanmic / ExpandoObject切换为Message,它可以正常工作,但是返回Message类的一个实例,其所有属性均为默认值。
有人做过吗?
干杯。
答案 0 :(得分:1)
无法直接投影到ExpandoObject
/ dynamic
。
但是您可以在运行时投影为动态创建的类型。例如,参见Microsoft.EntityFrameworkCore.DynamicLinq软件包- Dynamic Data Classes。
如果您安装了package形式的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);
}
答案 1 :(得分:0)
我对不使用Expressions
的情况有一个想法。显然,由于c#中的Reflection
较慢,如本Article所述,我决定使用名为FastMember的库来获取对象属性。
==============
安装FastMember
在PackageManagerConsole中运行以下命令以安装该库。
Install-Package FastMember -Version 1.4.1
写入功能
对于任何泛型类型,例如TEntity
,我们的函数/方法将是Extension Method,必须是class
并采用 fields 数组参数,告诉函数要返回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在上述方法上运行测试用例大约需要 40ms ,这听起来足够好。
希望此解决方案对您有用,或者可能为您提供解决问题的方法。
干杯!