我有一个动态选择器表达式,它产生匿名类型。它在linq到对象中运行良好,但在linq到实体中,它抛出:
尝试1
NotSupportedException异常
LINQ to Entities中仅支持无参数构造函数和初始值设定项。
Expression<Func<User, T>> DynamicSelect<T>(T obj, ParameterExpression userParam)
{
var newExpression = Expression.New(
typeof(T).GetConstructor(typeof(T).GenericTypeArguments),
userParam,
Expression.Constant("X"),
Expression.Constant("Y")
);
return Expression.Lambda<Func<User, T>>(newExpression, userParam);
}
var userParam = Expression.Parameter(typeof(User), "u");
var obj = new { User = new User(), Address = string.Empty, Fax = string.Empty };
var arr = context.Set<T>()
.Select(DynamicSelect(obj, userParam))
.ToArray();
尝试2 ,如果我创建了一个自定义类型,它正在工作,但我不想这样做,因为我想重用这个帮助方法而不为每个实体创建额外的自定义类型,我想要能够根据消费者传递类型。
public class Container
{
public User User { get; set; }
public string Address { get; set; }
public string Fax { get; set; }
}
Expression<Func<User, T>> DynamicSelect<T>(T obj, ParameterExpression userParam)
{
var initExpression = Expression.MemberInit(
Expression.New(typeof(T)),
Expression.Bind(typeof(T).GetProperty("User"), userParam),
Expression.Bind(typeof(T).GetProperty("Address"), Expression.Constant("X")),
Expression.Bind(typeof(T).GetProperty("Fax"), Expression.Constant("Y"))
);
return Expression.Lambda<Func<User, T>>(initExpression, userParam);
}
var userParam = Expression.Parameter(typeof(User), "u");
var arr = context.Set<T>()
.Select(DynamicSelect<Container>(null, userParam))
.ToArray();
尝试3 ,我也尝试使用Tuple<User, string, string>
,但也不支持。
NotSupportedException异常
LINQ to Entities无法识别该方法 “System.Tuple`3 [用户,System.String,System.String] 创建[User,String,String](User,System.String,System.String)' 方法,并且此方法无法转换为商店表达式。
Expression<Func<User, T>> DynamicSelect<T>(T obj, ParameterExpression userParam)
{
var createExpression = Expression.Call(
typeof(Tuple),
"Create",
typeof(T).GenericTypeArguments,
userParam,
Expression.Constant("X"),
Expression.Constant("Y"));
return Expression.Lambda<Func<User, T>>(createExpression, userParam);
}
var userParam = Expression.Parameter(typeof(User), "u");
var arr = context.Set<User>()
.Select(DynamicSelect<Tuple<User, string, string>>(null, userParam))
.ToArray();
请帮忙。
更新
我试图在任何消费者(用户,客户,员工等)中重复使用此辅助方法,而无需为每个消费者提供特定的实现。
类结构看起来像。
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Type { get; set; }
public string Content { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string UserName { get; set; }
public ContactDto Contact { get; set; }
}
public class CustomerDto
{
public int Id { get; set; }
public string CompanyName { get; set; }
public ContactDto Contact { get; set; }
}
public class ContactDto
{
public string Email { get; set; }
public string Address { get; set; }
public string Fax { get; set; }
// other contact informations
}
我有很多联系人,每个消费者可能会有所不同。
var users = context.Set<User>()
.Select(x => new UserDto
{
Id = x.Id,
UserName = x.UserName,
Contact = new ContactDto
{
Email = x.Contacts.Where(c => c.Type == "Email").Select(c => c.Value).FirstOrDefault()
}
})
.ToArray();
var customers = context.Set<Customer>()
.Select(x => new CustomerDto
{
Id = x.Id,
CompanyName = x.CompanyName,
Contact = new ContactDto
{
Address = x.Contacts.Where(c => c.Type == "Address").Select(c => c.Value).FirstOrDefault(),
Fax = x.Contacts.Where(c => c.Type == "Fax").Select(c => c.Value).FirstOrDefault(),
}
})
.ToArray();
并将x.Contacts.Where(c => c.Type == "Address").Select(c => c.Value).FirstOrDefault()
重构为表达式,但我不能直接在方法中使用它,如:
var users = context.Set<User>()
.Select(x => new UserDto
{
Id = x.Id,
UserName = x.UserName,
Contact = new ContactDto
{
Email = GetContactExpression("Email").Compile()(x)
}
})
.ToArray();
它会抛出错误,因为linq to expression不支持Invoke
方法,因此我需要重构整个Select
表达式,但我需要将其设为泛型(User
有UserName
,但Customer
有CompanyName
,以及任何其他信息),并且可能在此问题解决后传递联系类型。目前的预期结果将是微不足道的:
var obj = new { User = new User(), Email = "" };
var users = context.Set<User>()
.Select(x => DynamicSelect(obj))
.Select(x => new UserDto
{
Id = x.User.Id,
UserName = x.User.UserName,
Contact = new ContactDto
{
Email = x.Email
}
})
.ToArray();
答案 0 :(得分:4)
当我将你的表达式与编译器为u => new { User = u, Address = "X", Fax = "Y" }
之类的东西进行比较时,不同之处在于后者填充了Members
。
我不太明白Members
存在的原因是什么,但我会尝试设置它,我的猜测是它会解决你的问题。代码可能类似于:
var constructor = typeof(T).GetConstructor(typeof(T).GenericTypeArguments);
var newExpression = Expression.New(
constructor,
new Expression[] { userParam, Expression.Constant("X"), Expression.Constant("Y") },
constructor.GetParameters().Select(p => typeof(T).GetProperty(p.Name)));