Multiple WHERE IN clause LINQ equivalent - c#

I have a web page where the user can restrict results based on three different multi select choices (please see attached picture).
A bit of background. Documents have many sectors, companies and analysts assigned to them. My EF objects have navigation properties which means I don't need to write explicit JOINS to get the data I need.
My problem is I can't construct the LINQ query I need to get the results I need. Using SQL it would be simple and I could easily use a combination of JOINS and WHERE IN ('Blah', 'Another blah'). In my controller I have the following code:
public JsonResult FilterResearch(FilterResearchModel filterResearch)
{
var db = new Context();
// home model
var model = new HomeModel();
var selectedSectors = from s in db.Sectors
where filterResearch.SelectedSectors.Contains(s.Name)
select s;
var selectedCompanies = from c in db.Companies
where filterResearch.SelectedCompanies.Contains(c.Name)
select c;
var selectedAnalysts = from a in db.Analysts
where filterResearch.SelectedAnalysts.Contains(a.Name)
select a;
var filteredResults = from d in db.Documents
where selectedSectors.Contains(d.Sectors)
select d;
FilterResearch.Selected"Something" are string arrays. My "filteredResults" query is what should contain the filtered documents I plan to return.
EDIT
Some people have commented on the fact I'm not being clear. I'm trying to filter my documents based on 3 string arrays. Each of these string arrays are navigation properties in the "document" object. On the client the user has three multi select controls so each array can have more than one element. Now they can choose any of the three and choose as many options as they wish.
THIS IS THE PROBLEM When I compile it I get the following error: "cannot convert from 'System.Linq.IQueryable' to 'System.Linq.ParallelQuery>"
EDIT AGAIN Picture of error included. It occurs on "where selectedSectors.Contains(d.Sectors)"
I have checked:
Convert SQL to LINQ to Entities WHERE IN clause
LINQ to Entities - Where IN clause in query [duplicate]
Where IN clause in LINQ [duplicate]
with little luck. Is there a way where I can just say "filter the documents based on the companies AND sectors AND analysts?

Maybe I'm misunderstanding this (and I don't know your full object model) but wouldn't the following work...
var filteredResults = from d in db.Documents
where d.Sectors.Exists(sect => filterResearch.SelectedSectors.Contains(sect.Name))
&& d.Companies.Exists(comp => filterResearch.SelectedCompanies.Contains(comp.Name))
&& d.Analysts.Exists(anyst => filterResearch.SelectedAnalysts.Contains(anyst.Name))
select d;
Edit: Just to add, I haven't tried this in Visual Studio so it may not compile. Maybe you'd need to use a function other than Exists - Any perhaps - but I'm sure you get the gist of what I'm thinking.

Related

C# Unable to create a constant value of type Data.Awards

var awards = proxy.Awards.Where(c => c.Sender_Id == Id || c.Receiver_Id == Id).ToList();
var Comments = (from comment in proxy.AwardsComments join award in awards on comment.AwardId equals award.Id select comment).ToList();
i'm using the above code to get the comments related to the awards but i'm getting the error Unable to create a constant value of type Data.Awards. Only primitive types or enumeration types are supported in this context
I suggest you rewrite your LINQ query as below and give it a go:
var comments = (from comment in proxy.AwardsComments
join award in proxy.Awards.Where(c => c.Sender_Id == Id
|| c.Receiver_Id == Id)
on comment.AwardId equals award.Id
select comment).ToList();
From your setup I assume that you might use an ORM (e.g. Entity Framework). Then you fetch some of the data in memory, by demanding the immediate execution of the query in the right side of awards (by calling ToList). After then you want to perform a join of the fetched rows with some rows in the database. Quite probably the latter is the problem and the reason you get this error message is the fact that your query cannot be translated to an equivalent SQL query that would be executed to your database.
By using the above query the required SQL statement would be created and the corresponding rows would be fetched out of the database.

Using two different data context in a LINQ JOIN Query

I went through the many questions that were asked regarding this and tried to find solution but no luck. So here is my situation:
private IQueryable<tblB> MT;
var IDs = (from z in db1.tblA
where z.TA == User.Identity.Name
select z).ToArray();
MT = from s in db2.tblB
join a in IDs on s.BP equals a.BP
select new tblB() { LastName = s.LastName});
return View(MT.ToPagedList(pageNumber, pageSize));
I'm getting exception at the return statement - $exception {"Unable to create a constant value of type 'tblA'. Only primitive types or enumeration types are supported in this context."} System.NotSupportedException
When I debug IDs array, I see it has data from tblA but 2nd query with join doesn't seem to work. What mistake am I making. Help!!!
You need to use Contains in order to generate and IN sql clause:
First, change the first query to return the primitive data you need:
var IDs = (from z in db1.tblA
where z.TA == User.Identity.Name
select z.BP).ToArray();
Then use that in-memory list in the second query:
MT = from s in db2.tblB
where IDs.Contains(s.BP)
select new tblB() { LastName = s.LastName});
By the way, this is not a 2 contexts operations. You're loading data from the first context into memory (notice the .ToArray()) and then using these in-memory data to query the second context.
So you want as a result all elements of tblB that have a property BP equal to at least one of the BP properties of the elements in IDs.
The problem is that after ToList() IDs is in local memory, while your db1.tblB is on the database. You have to bring both sequences to the same platform, either both to your database, or both to local memory. Whatever is more efficient depends on the actual sizes of the sequences and of the results
Use Contains if you want to perform the query on database side. The complete list of IDs will be transferred to the database, where the query will be executed and the results will be transferred to local memory.
Use this method if IDs is fairly short and your result is not almost the complete tblA
var result = db2.tblB
.Where(elementOfTableB => IDs.Contains(elementOftableB);
No need to create a new tblB object, apparently you want the complete tblB object.
Use AsEnumerable if you expect that there are a lot of IDs in comparison to the number of element in tblB. Transferring the IDs to the database would take considerably more time than transferring the complete tblB to local memory.
db2.TblB // take all tblBelements
.AsEnumerable() // bring to local memory
.join(IDs, // join with list IDs
tblBElement => tblBElement.BP // take from each tblBElement the BP
idElement => idElement.BP // take from each ID the BP
(tblBElement, idElement) => // when they match take the two records
// and get what you want.
tblBElement); // in this case the complete tblBElement

Filter a given List based on external List in Linq in one query

I've a list :
List<Student> lstStudents = GetConditionalStudents();
I've another list:
List<School> allSchools = dbContext.Schools.ToList();
Every School has list of Students
public class School
{
///other properties
public List<Student> {get;set;}
}
I'am forced to do this:
List<School> schools = from p in allSchools
where p.LocationId==this.LocationId
where p.Students.Any(d=>lstStudents.Contains(d))
select p;
but it doesn't work: gives error
unable to create a constant value for .. only primitive types
Edit
I can make it work by doing this:
List<int> integers = lstStudents.Select(s=>s.Id).ToList();
List<School> schools = from p in allSchools
where p.LocationId == this.LocationId
where p.Students.Any(d=>integers.Contains(d.Id))
select p;
but I don't want to use it, coz I hav situations where I have to compare more than 2 Ids, so that implies, I will have to make more than 2 separate primitive datatype List and use them in the query,which I don't want.
how to directly use the external list in the linq query.??
I cannot use this:
allSchools.Where(s=>s.LocationId==this.LocationId ||
lstStudents.Contains(s)).ToList();
Please help...
I went through this and this..but they are no help to me..
The problem is that the Entity Framework is unable to translate your list of students into a valid SQL statement - obviously the database server knows nothing about you Student class and therefore the Entity Framework can not translate the LINQ query into something the database server could understand. There are three solutions.
Get rid of your method GetConditionalStudents() and directly include the query building this list in the main query if possible.
Fetch both lists - lstStudents and allSchools - into memory by calling ToList() on the respective queries and process both lists using LINQ to Objects.
Use object IDs instead of the objects because the Entity Framework is able to translate lists of integers, strings and so on into a IN statement.
To me it looks like you are already doing option two but obviously you are not because the code fails but I am unable to pin the exact flaw.
Try:
var schools = from p in allSchools
where (p.LocationId==this.LocationId && p.Students.Any(s => lstStudents.Contains(s)))
select p;

LINQ to Entities does not recognize the method 'System.String ToString(Int32)' method, and this method cannot be translated into a store expression

Using MVC3 VS2010 and SQL Server 2008 Express
I am trying to filter based on two SQL Server tables and display the result.
One table is clients table and the other is agent. They have in common ClientAgentID in the clients table and ID in the Agents table. An agent logs and should be able to see the clients assigned to the agent. If you have any ideas on the best way to do this please help me. So far I am trying to filter in the clients controller and here is what I have but the message is I am getting is in the title.
public ActionResult Index()
{
//This displays all the clients not filtered by the Agent ID number
//var clientItems = db.MVCInternetApplicationPkg;
//return View(clientItems.ToList());
//Trying to filter by the agent name given in the login page then finding
//the agent ID
var getAgentID = from a in db.AgentsPkg
where a.AgentLogin == User.Identity.Name
select a.ID;
var clientItems = from r in db.MVCInternetApplicationPkg
where Convert.ToString(r.ClientAgentID)
== Convert.ToString(getAgentID)
select r;
//THIS IS THE LINE OF CODE THAT SHOWS THE ERROR MESSAGE
return View(clientItems.ToList());
}
This is my first MVC project after the Music Store so am willing to learn and accept any help or advice.
Cheers
Here is the solution that I used in the end. Any feed back on if this is a good approach would be appreciated
public ActionResult Index()
{
var innerJoint = from agents in db.AgentsPkg where agents.AgentLogin == User.Identity.Name
join clients in db.MVCInternetApplicationPkg on agents.ID equals clients.ClientAgentID
select clients;
return View(innerJoint.ToList());
}
you do not want to use the Convert in your linq statement!!!!!!
string clientagentid=Convert.ToString(r.ClientAgentID);
string getagentid= Convert.ToString(getAgentID);
var clientItems = (from r in db.MVCInternetApplicationPkg
where clientagentid==getagentid
select r).ToList();
return View(clientItems);
1. Reason for the error:
As others have stated, it's due to the use of Convert.ToString() within your where clause, which Linq cannot convert into SQL. I would expect your original query to work just by removing the two Convert.ToString() functions.
2. "....best way to do this":
Well, a better way.... :)
In Entity Framework, the easy way to navigate between related entities is via Navigation Properties. If your approach is "Database First", these should be generated for you in your EDMX. If your approach is "Code First", there's a good post here describing how to set this up.
Either way, I'd expect your Client class to have a navigation property to Agent (i.e. similar to OrderDetail's Order property in the MvcMusicStore sample you mention):
public virtual Agents Agent { get; set; }
Then your method becomes very simple (i.e. similar to many of the controller methods in MvcMusicStore) ...no Joins or multiple statements required:
var clients = db.MVCInternetApplicationPkg.Where(c => c.Agent.AgentLogin == User.Identity.Name);
return View(clients.ToList());
The answer is to use SqlFunctions.StringConvert (use 'decimal' or 'double' but 'int' won't work) see example
using System.Data.Objects.SqlClient ;
var clientItems = from r in db.MVCInternetApplicationPkg
where SqlFunctions.StringConvert((double ), r.ClientAgentID)
== SqlFunctions.StringConvert((decimal) , getAgentID)
select r;
see http://msdn.microsoft.com/en-us/library/dd466166.aspx for more info.
What is going on is the Linq provider (Linq to Entities) is trying to convert your query to SQL, and there is no mapping for Convert. These errors are sometimes hard to decipher, but the clue is in the "String.ToString()" line. Also realize that because of deferred execution, the error won't show up until the clientItems is iterated, in your case with the call to toList() in return View(clientItems.ToList());
LINQ cannot map Convert.ToInt32() into equivalant T-SQL, so it throws exception. As COLD TOLD said you have to convert the value and then use the converted value in the query.
please check : Why do LINQ to Entities does not recognize certain Methods?

How do I apply the LINQ to SQL Distinct() operator to a List<T>?

I have a serious(it's getting me crazy) problem with LINQ to SQL. I am developing an ASP.NET MVC3 application using c# and Razor in Visual Studio 2010.
I have two database tables, Product and Categories:
Product(Prod_Id[primary key], other attributes)
Categories((Dept_Id, Prod_Id) [primary keys], other attributes)
Obviously Prod_Id in Categories is a foreign key. Both classes are mapped using the Entity Framework (EF). I do not mention the context of the application for simplicity.
In Categories there are multiple rows containing the Prod_Id. I want to make a projection of all Distinct Prod_Id in Categories. I did it using plain (T)SQL in SQL Server MGMT Studio according to this (really simple) query:
SELECT DISTINCT Prod_Id
FROM Categories
and the result is correct. Now I need to make this query in my application so I used:
var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();
I go to check the result of my query by using:
query.Select(m => m.Prod_Id);
or
foreach(var item in query)
{
item.Prod_Id;
//other instructions
}
and it does not work. First of all the Intellisense when I attempt to write query.Select(m => m. or item.shows just suggestions about methods (such as Equals, etc...) and not properties. I thought that maybe there was something wrong with Intellisense (I guess most of you many times hoped that Intellisense was wrong :-D) but when I launch the application I receive an error at runtime.
Before giving your answer keep in mind that;
I checked many forums, I tried the normal LINQ to SQL (without using lambdas) but it does not work. The fact that it works in (T)SQL means that there is something wrong with the LINQ to SQL instruction (other queries in my application work perfectly).
For application related reasons, I used a List<T> variable instead of _StoreDB.Categories and I thought that was the problem. If you can offer me a solution without using a List<T> is appreciated as well.
This line:
var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();
Your LINQ query most likely returns IEnumerable... of ints (judging by Select(m => m.Prod_Id)). You have list of integers, not list of entity objects. Try to print them and see what you got.
Calling _StoreDB.Categories.Select(m => m.Prod_Id) means that query will contain Prod_Id values only, not the entire entity. It would be roughly equivalent to this SQL, which selects only one column (instead of the entire row):
SELECT Prod_Id FROM Categories;
So when you iterate through query using foreach (var item in query), the type of item is probably int (or whatever your Prod_Id column is), not your entity. That's why Intellisense doesn't show the entity properties that you expect when you type "item."...
If you want all of the columns in Categories to be included in query, you don't even need to use .Select(m => m). You can just do this:
var query = _StoreDB.Categories.Distinct();
Note that if you don't explicitly pass an IEqualityComparer<T> to Distinct(), EqualityComparer<T>.Default will be used (which may or may not behave the way you want it to, depending on the type of T, whether or not it implements System.IEquatable<T>, etc.).
For more info on getting Distinct to work in situations similar to yours, take a look at this question or this question and the related discussions.
As has been explained by the other answers, the error that the OP ran into was because the result of his code was a collection of ints, not a collection of Categories.
What hasn't been answered was his question about how to use the collection of ints in a join or something in order to get at some useful data. I will attempt to do that here.
Now, I'm not really sure why the OP wanted to get a distinct list of Prod_Ids from Categories, rather than just getting the Prod_Ids from Projects. Perhaps he wanted to find out what Products are related to one or more Categories, thus any uncategorized Products would be excluded from the results. I'll assume this is the case and that the desired result is a collection of distinct Products that have associated Categories. I'll first answer the question about what to do with the Prod_Ids first, and then offer some alternatives.
We can take the collection of Prod_Ids exactly as they were created in the question as a query:
var query = _StoreDB.Categories.Select(m => m.Prod_Id).Distinct();
Then we would use join, like so:
var products = query.Join(_StoreDB.Products, id => id, p => p.Prod_Id,
(id,p) => p);
This takes the query, joins it with the Products table, specifies the keys to use, and finally says to return the Product entity from each matching set. Because we know that the Prod_Ids in query are unique (because of Distinct()) and the Prod_Ids in Products are unique (by definition because it is the primary key), we know that the results will be unique without having to call Distinct().
Now, the above will get the desired results, but it's definitely not the cleanest or simplest way to do it. If the Category entities are defined with a relational property that returns the related record from Products (which would likely be called Product), the simplest way to do what we're trying to do would be the following:
var products = _StoreDB.Categories.Select(c => c.Product).Distinct();
This gets the Product from each Category and returns a distinct collection of them.
If the Category entity doesn't have the Product relational property, then we can go back to using the Join function to get our Products.
var products = _StoreDB.Categories.Join(_StoreDB.Products, c => c.Prod_Id,
p => p.Prod_Id, (c,p) => p).Distinct();
Finally, if we aren't just wanting a simple collection of Products, then some more though would have to go into this and perhaps the simplest thing would be to handle that when iterating through the Products. Another example would be for getting a count for the number of Categories each Product belongs to. If that's the case, I would reverse the logic and start with Products, like so:
var productsWithCount = _StoreDB.Products.Select(p => new { Product = p,
NumberOfCategories = _StoreDB.Categories.Count(c => c.Prod_Id == p.Prod_Id)});
This would result in a collection of anonymous typed objects that reference the Product and the NumberOfCategories related to that Product. If we still needed to exclude any uncatorized Products, we could append .Where(r => r.NumberOfCategories > 0) before the semicolon. Of course, if the Product entity is defined with a relational property for the related Categories, you wouldn't need this because you could just take any Product and do the following:
int NumberOfCategories = product.Categories.Count();
Anyway, sorry for rambling on. I hope this proves helpful to anyone else that runs into a similar issue. ;)

Categories