简体   繁体   English

实体框架中的“不在”

[英]“Not In” in Entity Framework

I have the following Entity 我有以下实体

public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

and have a list List<Person> badGuys 并列出List<Person> badGuys

what I want to do is select from all persons except those in badGuys List 我想要做的是从除badGuys列表中的人之外的所有人中选择

My Code 我的守则

context.Persons
    .where(p => !badGuys.Contain(p))
    .ToList()

but I get an error 但是我收到了一个错误

Only primitive types or enumeration types are supported in this context. 在此上下文中仅支持基元类型或枚举类型。

How to fix this? 如何解决这个问题?

You could make an array containing the ids of the bad guys and filter out those ids (which are of a primitive type, so it should work): 你可以创建一个包含坏人的ID的数组并过滤掉那些id(它们是原始类型,所以它应该工作):

var badGuyIds = badGuys.Select(x => x.PersonId).ToArray();

context.Persons
    .Where(p => !badGuyIds.Contain(p.PersonId))
    .ToList();

You can implement your own method to make necessary expression tree, like here: 您可以实现自己的方法来创建必要的表达式树,如下所示:

    public static IQueryable<TEntity> WhereNotIn<TEntity, TValue>(
        this IQueryable<TEntity> queryable,
        Expression<Func<TEntity, TValue>> valueSelector,
        IEnumerable<TValue> values)
        where TEntity : class
    {
        if (queryable == null)
            throw new ArgumentNullException("queryable");

        if (valueSelector == null)
            throw new ArgumentNullException("valueSelector");

        if (values == null)
            throw new ArgumentNullException("values");

        if (!values.Any())
            return queryable.Where(e => true);

        var parameterExpression = valueSelector.Parameters.Single();

        var equals = from value in values
                     select Expression.NotEqual(valueSelector.Body, Expression.Constant(value, typeof (TValue)));

        var body = equals.Aggregate(Expression.And);

        return queryable.Where(Expression.Lambda<Func<TEntity, bool>>(body, parameterExpression));
    }
}

And now you can call this extension method 现在你可以调用这个扩展方法

var badGuys = new int[] { 100, 200, 300 };
context.Persons.WhereNotIn(p => p.PersionId, badGuys);

This method makes the same thing as: 这个方法与以下内容相同:

context.Persons.Where(p => p.PersonId != badGuys[0]
                        && p.PersonId != badGuys[1]
                        . . .
                        && p.PersonId != badGuys[N]);

for each element of badGuys enumerable object. 对于badGuys可枚举对象的每个元素。

An additional advantage of the method is its universality, cause you can call it for any property of any entity, fe context.Persons.WhereNotIn(p => p.LastName, new[] { "Smith", "Brown", "Jones" }) . 该方法的另一个优点是它的普遍性,因为你可以为任何实体的任何属性调用它,fe context.Persons.WhereNotIn(p => p.LastName, new[] { "Smith", "Brown", "Jones" })

You can use the clause ALL with the distinct(!=) 您可以将子句ALL与distinct(!=)一起使用

var badBoys= from P in context.Persons
         where badGuys.All(a => a.PersonId!= P.PersonId)
         select P;

LINQ to ENTITIES: NOT CONTAINS METHOD: LINQ to ENTITIES:NOT CONTAINS METHOD:

The method should be .Contains() not .Contain. 方法应该是.Contains()而不是.Contain。 This requires a primitive type enumeration, so just enumerate your key field of your badGuys collection and call .Contains() on it in your Where clause when you query your context. 这需要一个原始类型枚举,所以只需枚举badGuys集合的关键字段,并在查询上下文时在Where子句中调用.Contains()

//get the list of bad guys
List<Person> badGuys = ...
//get simple primitive type enumeration
var badGuysIDs = badGuys.Select(t => t.PersonId).ToList();

using(Context c = new Context())
{
   var badPersons = c.Persons.Where(t => !badGuysIDs.Contains(t.PersonId)).ToList();
}

LINQ TO OBJECTS EXCEPT() METHOD: LINQ TO OBJECTS EXCEPT()方法:

Another option, if you already have your List<Person> of badguys is to use the .Except() method on your collection. 另一个选择,如果你已经有你的List<Person> of badguys,那就是在你的集合中使用.Except .Except()方法。 This will have to be done after you enumerate your collection to objects. 在将集合枚举到对象之后,必须执行此操作。

//get the list of bad guys
List<Person> badGuys = ...

using(Context c = new Context())
{
   //enumerate Persons, then exclude badPersons
   var goodPersons = c.Persons.ToList().Except(badPersons);
}

LINQ to ENTITIES EXCEPT() METHOD: LINQ to ENTITIES除外()方法:

The above method will not be ideal if you have a very large number of rows in your Persons table, because it will enumerate the entire collection. 如果您的Persons表中有非常多的行,则上述方法将不是理想的,因为它将枚举整个集合。 If you have a large number of rows, you will want to perform the exclusion on the database side before enumerating. 如果您有大量行,则需要在枚举之前在数据库端执行排除。 To do this, you simply have to tell the database how to compare two objects for equality as in This excellent SO answer . 要做到这一点,你只需要告诉数据库如何比较两个对象的相等性,如同这个优秀的SO答案

public class PersonComparer: IEqualityComparer<Persons> 
{
     public bool Equals(Person x, Person y) {return x.Id == y.Id;}
     public int GetHashCode(Person person) {return person.PersonId.GetHashCode();}
}


using(Context c = new Context())
{
    //get the list of bad guys
    List<Person> badGuys = ...

   var goodPersons = c.Persons.Except(badPersons, PersonComparer()).ToList();
}

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

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