如何使用反射对实体框架模型的属性执行ToString

时间:2015-04-29 03:45:59

标签: c# linq entity-framework reflection

我正在尝试写一些能做"包含"查询实体框架模型的所有属性。

我可以执行以下操作,例如没有问题:

var students = db.Students.AsQueryable();
var test = students.Where(x => x.FirstName.ToString().ToLower().Contains("1"));

但是,当使用反射时(如下面的代码所示),将返回以下错误:

  

LINQ to Entities无法识别方法' System.String ToString()'方法,并且此方法无法转换为商店表达式。

现在应该是supported

我已经读过这个错误,但正如您所见,ToString即使在使用IQueryable 时也完全有效(在我的情况下这是必需的,因为我不想发布过滤器数据)。

主要区别在于我需要通过反思来调用它。

private static readonly MethodInfo StringContainsMethod =
    typeof(string).GetMethod(@"Contains",
        BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null);

Type dbType = typeof(Student);

var dbFieldMemberInfo = dbType.GetMember("FirstName",
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).Single();          

// Create an "x" as TDbType
var dbTypeParameter = Expression.Parameter(dbType, @"x");

// Get at x.FirstName
var dbFieldMember = Expression.MakeMemberAccess(dbTypeParameter, dbFieldMemberInfo);         

// Create the criterion as a constant
var criterionConstant = new Expression[] { Expression.Constant(searchString) };          

var toStringMethod = typeof(Convert).GetMethod("ToString", Type.EmptyTypes);
var toStringCall = Expression.Call(dbFieldMember, toStringMethod);
var fancyContain = Expression.Call(toStringCall, StringContainsMethod, criterionConstant);

// Create a lambda like x => x.FirstName.ToString().Contains(criterion)
var lambda = Expression.Lambda(fancyContain, dbTypeParameter) as Expression<Func<Student, bool>>;

这会产生完全相同的lambda x.FirstName.ToString().Contains("")但它会返回一个不能使用ToString的错误。显然,从第一个例子和它添加到EF 6.1的事实,它可以使用。这是反思的限制吗?

2 个答案:

答案 0 :(得分:2)

您尝试执行的操作似乎是基于所有列进行搜索,最简单,最快捷的方式是从EF执行查询并进行全文搜索。

 var students = db.Database.SqlQuery<Student>("SELECT * FROM Student WHERE CONTAINS((Name, ID), '12')");

(只需确保清除字符串没有sql注入)

我认为当您尝试构建表达式树并对SQL执行它时,SQL无法识别它,因为当您使用Querable时尚未执行它。

另一种方法是使用SqlFunctions.StringConvert代替ToString

答案 1 :(得分:1)

对于应该相当简单的事情来说,这看起来很复杂。我建议在表格中创建一个textdata计算列,简单地连接您感兴趣的所有字段。然后您可以像这样在模型中添加属性:

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string TextData { get; set; }

最后,您可以针对这一个属性执行LINQ查询,以检查它是否.Contains(...)您感兴趣的任何文本。只要您对列进行索引,搜索就不应该是太糟糕了。您的主要处罚将是插入。