简体   繁体   English

无法在Entity Framework中执行LINQ复杂对象搜索

[英]Cannot perform LINQ complex object search in Entity Framework

I have entities in the DB which each contain a list of key value pairs as metadata. 我在DB中有实体,每个实体都包含一个键值对列表作为元数据。 I want to return a list of object by matching on specified items in the metadata. 我想通过匹配元数据中的指定项来返回对象列表。

Ie if objects can have metadata of KeyOne, KeyTwo and KeyThree, I want to be able to say "Bring me back all objects where KeyOne contains "abc" and KeyThree contains "de" 即如果对象可以包含KeyOne,KeyTwo和KeyThree的元数据,我希望能够说“将我带回KeyOne包含的所有对象”abc“,而KeyThree包含”de“

This is my C# query 这是我的C#查询

 var objects = repository.GetObjects().Where(t =>
                    request.SearchFilters.All(f =>
                        t.ObjectChild.Any(tt =>
                            tt.MetaDataPairs.Any(md =>
                                md.Key.ToLower() == f.Key.ToLower() && md.Value.ToLower().Contains(f.Value.ToLower())
                            )
                        )
                    )
                ).ToList();

and this is my request class 这是我的请求类

[DataContract]
public class FindObjectRequest
{
    [DataMember]
    public IDictionary<string, string> SearchFilters { get; set; }
}

And lastly my Metadata POCO 最后是我的元数据POCO

[Table("MetaDataPair")]
    public class DbMetaDataPair : IEntityComparable<DbMetaDataPair>
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }

        [Required]
        public string Key { get; set; }

        public string Value { get; set; }
}

The error I get is 我得到的错误是

Error was Unable to create a constant value of type 'System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'. 错误是无法创建类型'System.Collections.Generic.KeyValuePair`2 [[System.String,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089],[System.String,mscorlib, Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]]'。 Only primitive types or enumeration types are supported in this context. 在此上下文中仅支持基元类型或枚举类型。

So it looks like the f variable in your query is a KeyValuePair<string, string> . 所以看起来你的查询中的f变量是KeyValuePair<string, string> What you need to do is save them as local variables before you use them as a KVP cannot be converted to SQL. 您需要做的是在使用它们之前将它们保存为局部变量,因为KVP无法转换为SQL。

var filters = request.SearchFilters.Select(kvp => new[] { kvp.Key, kvp.Value }).ToArray();

var objects = repository.GetObjects().Where(t =>
    filters.All(f =>
        t.ObjectChild.Any(tt =>
            tt.MetaDataPairs.Any(md =>
                md.Key.ToLower() == f[0] && md.Value.ToLower().Contains(f[1])
                )
            )
         )
    ).ToList();

What you have to remember is that anything you do in LINQ in EF when it is still of the type IQueryable<T> - which is before you call ToList() , ToArray() , ToDictionary() and maybe even AsEnumerable() (I have honestly never tried AsEnumerable() ) - must be able to be represented as SQL. 你必须要记住的是,当你仍然使用IQueryable<T>类型时,你在LINQ中做的任何事情 - 这是在你调用ToList()ToArray()ToDictionary()甚至AsEnumerable() (我)诚实地从未尝试过AsEnumerable() ) - 必须能够表示为SQL。 That means that only SQL types (string, int, long, byte, date, etc..) and entity types defined in your DbContext can be used in the query. 这意味着只能在查询中使用DbContext定义的SQL类型(字符串,int,long,byte,date等)和实体类型。 Everything else needs to be broken apart into one of those forms. 其他一切都需要分解为其中一种形式。

EDIT: 编辑:
You could also try coming from the other side of the query, but you are going to need a few things first.. 你也可以试试来自查询的另一面,但你首先需要一些东西..

The Model ... 该模型 ...

[Table("MetaDataPair")]
public class DbMetaDataPair : IEntityComparable<DbMetaDataPair>
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }

    [Required]
    public string Key { get; set; }

    public string Value { get; set; }

    // this is the navigation property back up to the ObjectChild
    public virtual ObjectChild ObjectChild { get;set; }
}

public ObjectChild
{
    ...
    public ICollection<MetaDataPair> MetaDataPairs { get; set; }

    // this is the navigation property back up to the "Object"
    public virtual Object Object { get; set; }
    ...
}

Now for the query... 现在查询...

public IEnumerable<Object> GetObjectsFromRequest(FindObjectRequest request)
{
    foreach(var kvp in request.SearchFilters)
    {
        var key = kvp.Key;
        var value = kvp.Value;

        yield return metaDataRepository.MetaDataPairs
            .Where(md => md.Key.ToLower() == key && md.Value.ToLower().Contains(value))
            .Select(md => md.ObjectChild.Object)
    }
}

This should execute 'n' number of SQL queries for the number of meta pairs you need to match. 对于您需要匹配的元对数,这应该执行'n'个SQL查询。 The better option would be trying to union them somehow but without some code to play with that might be a mission. 更好的选择是尝试以某种方式联合它们,但没有一些代码可以使用它可能是一个任务。

As a side note: I don't really know the names of your classes so I have used > > what I can work out. 作为旁注:我真的不知道你的课程名称,所以我使用了>>我可以解决的问题。 Obviously Object is not the name of the model.. 显然, Object不是模型的名称..

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM