开发者

"IN" Operator in Linq

I am trying to convert an old raw Sql query in Linq with Entity Framework here.

It was using the IN operator with a collection of items. The query was something like that:

SELECT Members.Name
FROM Members
WHERE Members.ID IN ( SELECT DISTINCT ManufacturerID FROM Products WHERE Active = 1)
ORDER BY Members.Name ASC

Since the return of the subquery is not a single string but a collection of strings I can't use the String.Contains() method.

I thought about doing something like :

var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);

and then

var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));

but it stops at the contains saying it has invalid arguments ... I can't select activeProducts.ManufacturerID because obviously the proprety is not there since it returns an IQueryable...

Bottom line what I'm trying to do here is to return a list of members who have at least one active product.

Any hint ?

[edit]

Here's the full query code ... I tried with the contains on the second expression, Linq didn't seem to like it :

Server Error in '/' Application. LINQ to Entities does not recognize the method 'Boolean Contains[String](System.Linq.IQueryable``1[System.String], System.String)' method, and this method cannot be translated into a store expression.

    var activeProduct =(from product in Master.DataContext.ProductSet
                        where product.Active == true
                           &a开发者_开发问答mp;& product.ShowOnWebSite == true
                           && product.AvailableDate <= DateTime.Today
                           && ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
                        select product.ManufacturerID.ToString() );

    var activeArtists = from artist in Master.DataContext.ContactSet
                        where activeProduct.Contains(artist.ID.ToString())
                        select artist;

    NumberOfArtists = activeArtists.Count();

    artistsRepeater.DataSource = activeArtists;
    artistsRepeater.DataBind();

[More details] ManufacturerID is a nullable GUID apparently...

For some reason the ContactSet class do not contain any reference to the products I guess I will have to do a join query, no clues here.


var activeMembers = (
from member in db.ContactSet
where activeProducts.Select(x=>x.ID).Contains(member.ID));


Try where activeProducts.Contains(member.ID).
EDIT: Did you try it without any ToStrings?


You can do it in one query:

var q = from member in db.ContactSet
        where member.Products.Any(p => p.IsActive)
        select member;


Try the solution posted by Colin Meek at: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/095745fe-dcf0-4142-b684-b7e4a1ab59f0/. It worked for me.


What about this:

from m in members
where products.FirstOrDefault(prod => prod.IsActive == 1 && prod.Id == m.Id) != null
select m;

you can chain any number of conditions required in the where clause using &&

Ash..


from m in members
where products.Any(p => p.Active && p.ManufacturerID == m.ID)
select m

or

from m in members
join p in products on m.ID equals p.ManufacturerID
where p.Active
select m


Instead of this:

var activeMembers = (
from member in db.ContactSet
where member.ID.ToString().Contains(activeProducts));

Try this:

var activeMembers = (
from member in db.ContactSet
where activeProducts.Contains(member.ID));


What if you swap the statement (untested)?

where activeProducts.Contains(member.ID)


How about this...

var activeProducts = (
from products in db.ProductSet
where product.Active == true
select product.ManufacturerID);

var activeMembers = (
from member in db.ContactSet
where activeProducts.Contains(member.ID.ToString()));


A helper or extension method will work fine when querying against objects in memory. But against an SQL database, your LINQ code will be compiled into an expression tree, analysed and translated into an SQL command. This functionality has no concept of custom-made extension methods or methods of other objects like .Contains(...).

It could be easily implemented into the standard LINQ-To-SQL functionality by Microsoft though. But as long as they don't want, we're helpless as long it's not an open source functionality.

All you can do is create your own QueryProvider that goes against an SQL database. But it will be hard and it would be only for that one in feature alone that you're missing.

However, if you really wanna go that route, have fun: LINQ: BUILDING AN IQUERYABLE PROVIDER SERIES


Finally I managed to code something really ugly, but that actually works! (lol)

    var activeProduct =(from product in Master.DataContext.ProductSet
                        where product.Active == true
                           && product.ShowOnWebSite == true
                           && product.AvailableDate <= DateTime.Today
                           && ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
                        select product.ManufacturerID ).Distinct();

    var artists = from artist in Master.DataContext.ContactSet
                        select artist;

    List<Evolution.API.Contact> activeArtists = new List<Evolution.API.Contact>();

    foreach (var artist in artists)
    {
        foreach(var product in activeProduct)
        {
            if (product.HasValue && product.Value == artist.ID)
                activeArtists.Add(artist);
        }
    }

    NumberOfArtists = activeArtists.Count();

    artistsRepeater.DataSource = activeArtists;
    artistsRepeater.DataBind();


I have already posted about the same at http://www.codeproject.com/Tips/336253/Filtering-records-from-List-based-similar-to-Sql-I


var q = (from p in db.DOCAuditTrails
        where p.ActionUser == "MyUserID"
        && p.ActionTaken == "Not Actioned"
        && p.ActionDate > DateTime.Parse("2011-09-13")
          select p.RequisitionId).Distinct();

var DocAuditResults = db.DOCAuditTrails.Where(p 
  => q.ToArray().Contains(p.RequisitionId));


Without know the exact mappings it is hard to tell what can be done and what can't. I will assume that there isn't any casting involved. Firstly you have to remember that everything in the Linq Expression tree must have an equivalent in SQL. As some others have noted, you have a object.ToString() in your Linq Statements.

However it seems that what people have neglected to mention is that you have TWO usages of object.ToSting(), both of which must be removed.

I would also make an extra variable to change the closure's capture type to be explicitly of DataContext (since the Linq statement is like a lambda, and delayed evaluated. It will need to take the whole of the Master variable. Earlier I stated that everything in your Linq must have an equivalent in SQL. Given that Master can't possibly exist in SQL, there is no DataContext property/column/mapping for the type of Master).

var context = Master.DataContext;
var activeProduct = from product in context.ProductSet
                    where product.Active == true
                       && product.ShowOnWebSite == true
                       && product.AvailableDate <= DateTime.Today
                       && ( product.DiscontinuationDate == null || product.DiscontinuationDate >= DateTime.Today )
                    select product.ManufacturerID;

var activeArtists = from artist in context.ContactSet
                    where activeProduct.Contains(artist.ID)
                    select artist;

I hope the above changes work for you.

In many cases issues with Linq to ORMs can be traced back to your Linq Expression capturing a non primative (DateTime, int, string etc) and non ORM based class (DataContext/EntityObject etc). The other major gotcha is usage of functions and operators that aren't exposed by the ORM (it is possible to map user defined functions to .net function through the ORM, but I would not recommend it due to indexing issues).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜