目前我们有一个包,可以从字符串中动态生成linq select。它可以很好地处理平面属性,但不能处理嵌套字段,比如someObj.NestedObj.SomeField。
我们当前的代码在服务方法中按以下方式工作:
_context.Shipments
.Where(s => s.Id == request.Id) // it does not matter just an example
.Select(request.Fields)
.ToPage(request); // ToPage extension comes from a nuget package
请求对象的参数"fields"只是一个字符串,其中包括逗号分隔的Shipment对象属性。
我对Shipment进行了一些重构,将一些字段分组到一个名为Address的新类中,并将其添加到Shipment中,如下所示:
// before refactoring
class Shipment {
// other fields...
public string SenderAddress;
public string SenderCityName;
public string SenderCityId;
public string RecipientAddress;
public string CityName;
public string CityId;
}
// after refactoring
class Shipment {
// other fields...
public Address Sender;
public Address Recipient;
}
class Address {
public string AddressText;
public string CityName;
public string CityId;
}
为了当前数据库映射的缘故,我添加了相应的映射:
public class ShipmentMap : DataEntityTypeConfiguration<Shipment>
{
public ShipmentMap()
{
ToTable("Shipments");
// other property mappings
Property(s => s.Recipient.AddressText).HasMaxLength(1100).HasColumnName("RecipientAddress");
Property(s => s.Recipient.CityName).HasMaxLength(100).HasColumnName("CityName");
Property(s => s.Recipient.CityId).IsOptional().HasColumnName("CityId");
Property(s => s.Sender.AddressText).HasMaxLength(1100).HasColumnName("SenderAddress");
Property(s => s.Sender.CityName).HasMaxLength(100).HasColumnName("SenderCityName");
Property(s => s.Sender.CityId).IsOptional().HasColumnName("SenderCityId");
}
}
DataEntityTypeConfiguration来自nuget包,如下:
public abstract class DataEntityTypeConfiguration<T> : EntityTypeConfiguration<T> where T : class
{
protected virtual void PostInitialize();
}
所以,我的问题是使用select(fields)时,当fields = "Recipient.CityId"时无法正常工作。
如何动态生成用于选择嵌套字段的linq?
我尝试了下面使用 LINQ:动态选择,但它不起作用。
// assume that request.Fields= "Recipient.CityId"
// in the service method
List<Shipment> x = _context.Shipments
.Where(s => s.Id == request.Id)
.Select(CreateNewStatement(request.Fields))
.ToList();
// I tried to generate select for linq here
Func<Shipment, Shipment> CreateNewStatement(string fields)
{
// input parameter "o"
var xParameter = Expression.Parameter( typeof( Shipment ), "o" );
// new statement "new Data()"
var xNew = Expression.New( typeof( Shipment ) );
// create initializers
var bindings = fields.Split( ',' ).Select( o => o.Trim() )
.Select(o =>
{
string[] nestedProps = o.Split('.');
Expression mbr = xParameter;
foreach (var prop in nestedProps)
mbr = Expression.PropertyOrField(mbr, prop);
// property "Field1"
PropertyInfo mi = typeof( Shipment ).GetProperty( ((MemberExpression)mbr).Member.Name );
//
// original value "o.Field1"
var xOriginal = Expression.Property( xParameter, mi );
MemberBinding bnd = Expression.Bind( mi, xOriginal );
return bnd;
});
// initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }"
var xInit = Expression.MemberInit( xNew, bindings );
// expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }"
var lambda = Expression.Lambda<Func<Shipment,Shipment>>( xInit, xParameter );
// compile to Func<Data, Data>
return lambda.Compile();
}
在循环结束后,mbr变成了CityId,且shipment上没有CityId字段,因此"mi"为null,导致抛出异常。我错过了什么?如何创建给定字符串的动态选择器,包含嵌套属性?
更新:
我找到了解决方案,并将其添加为答案,同时我创建了一个github gist作为解决方案。
https://gist.github.com/mstrYoda/663789375b0df23e2662a53bebaf2c7c
foreach
循环中,您正在重复使用一个新值覆盖mbr
,但并没有使用它。您是有意这样做的吗? - Abion47