linq simple query with 2 tables - c#

I am trying to get a simple join query from 2 tables , they have no relationship , so far i got this code but there is an error that i dont get it , please help
var query =
from p in db.Productos
from s in db.Stocks
where p.Nombre == "Suaje" && p.TipoProducto == tipo
where p.RecID == s.IDProducto
select new { s.Largo, s.Ancho };

Your query is well formed. I believe the error comes from how you're using the query object.
Are you returning it? What is the return type of the method?
Good practice dictates that you do not return anonymous types (or generic types with anonymous type parameters) from any method.
If all else fails, remove the var keyword and work from there.

Are you missing the 'and'?
and p.RecID == s.IDProducto
Consider writing your query like this:
var results = from p in db.Productos
join s in db.Stocks
on p.RecID equals s.IDProducto
where p.Nombre == "Suaje" && p.TipoProducto == tipo
select new { s.Largo, s.Ancho };

Related

Error converting SQL with join to LINQ

I have a SQL query that I'm trying to convert to LINQ and am having trouble understanding the obscure error messages when the query is enumerated.
The SQL query (which works as intended), is:
select a.TestGuid, MIN(a.StartTime) as StartTime, COUNT(b.TestCaseId) as NumTests, COUNT(DINSTINCT a.Id) as NumScenarios
from LoadTestSummary as a
join LoadTestTestSummaryData as b
on a.LoadTestRunid = b.LoadTestRunId
where
a.TargetStack = env and
a.TestGuid IS NOT NULL AND
a.StartTime IS NOT NULL AND
a.LoadTestRunId IS NOT NULL
group by a.TestGuid
Converting to LINQ, I get the following:
var q = from a in _context.LoadTestSummary
where
a.TargetStack == env &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
join b in _context.LoadTestTestSummaryData on new
{
LoadTestRunId = Convert.ToInt32(a.LoadTestRunId)
} equals new
{
LoadTestRunId = b.LoadTestRunId
}
group new { a, b } by new
{
a.TestGuid
}
into g
select new
{
DateCreated = g.Min(p => p.a.StartTime),
NumScenarios = g.Count(),
TestGuid = g.Key.TestGuid
NumTests = // ???
};
Two problems I have:
1) When the query is enumerated I get a run-time error that I'm having trouble deciphering. The query works fine in Linqpad, but gives me a run-time error in my program. I am not sure what would cause this. Just staring at this makes my head hurt:
ArgumentException: Expression of type 'System.Func``2[Microsoft.Data.Entity.Query.EntityQueryModelVisitor+TransparentIdentifier``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType7``1[System.String]]' cannot be used for parameter of type 'System.Func``2[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType7``1[System.String]]' of method 'System.Collections.Generic.IEnumerable``1[System.Linq.IGrouping``2[<>f__AnonymousType7``1[System.String],<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData]]] _GroupBy[<>f__AnonymousType5``2,<>f__AnonymousType7``1,<>f__AnonymousType5``2](System.Collections.Generic.IEnumerable``1[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData]], System.Func``2[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType7``1[System.String]], System.Func``2[<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData],<>f__AnonymousType5``2[PerfPortal.Models.LoadTestSummary,PerfPortal.Models.LoadTestTestSummaryData]])'
2) I am not quite sure how to get the COUNT(DISTINCT a.Id) into the NumTests field. It looks like this isn't supported in LINQ but it looks like other people have asked this question to so I may be able to figure it out once #1 is resolved.
Any thoughts on what's wrong here? I am not even sure exactly what the error is telling me.
All help is appreciated!
Looking just at the SQL query and your LINQ code, I came up with something like this:
from a in LoadTestSummary
join b in LoadTestTestSummaryData
on a.LoadTestRunId equals b.LoadTestRunId
where
a.TargetStack == env &&
a.TestGuid != null &&
a.StartTime != null &&
a.LoadTestRunId != null
group new { a, b } by a.TestGuid into g
select new
{
TestGuid = g.Key,
DateCreated = g.Min(el => el.a.StartTime),
NumTests = g.Select(el => el.b.TestCaseId).Count(),
NumScenarios = g.Select(el => el.a.Id).Distinct().Count()
};
Note, that you don't need to convert LoadTestRunId to int, you may just use standard string comparision.
That horrendous error is most likely caused by grouping and comparing using anonimous objects, thou I prefer not to read that error too much as it's an eldritch abomination not ment to be seen nor comprehend by mere mortals, it seems.

Entity Framework join statement in a function

I'm trying to create a function to grab a List<>() of type "tableContact" which is an entity (sort of like when you do a simple select statement: query.ToList();)
But this is becoming frustrating because I kept getting "null object reference" errors. For some reason "join" statement doesn't work, so I tried an alternate linq statement.
I have an Object2Contacts table that I want to LEFT JOIN and I'm looking up my Object and trying to see all the contacts for this Object. I also can't figure out how to change an "anonymous type" back into an entity table.
Also not every object has contacts, so sometimes null List<> should be returned.
public List<tableContact> getContactsForObject(int oid)
{
if (oid > 0)
{
var query = (from s in entities.tableContacts
from o in entities.tableObject2Contacts
where s.contact_id == o.contact_id
where o.object_id == oid
select new { s });
if (query != null)
{
IEnumerable<tableContact> e = (IEnumerable<tableContact>)query.ToList();
return (List<tableContact>)e;
}
}
return new List<tableContact>();
}
I want to then loop through the object returned... e.g.:
foreach ( tableContact c in MyList){
WriteLine(c.Name);
}
EDIT
I also tried:
List<tableContacts> contacts = (from s in entities.tableContacts
join o in entities.tableObject2Contacts
on s.contact_id equals o.contact_id
where o.object_id == oid
select s).ToList();
Then I can't convert it back into a List and still "null reference".
EDIT 2
Yes the query I am running may definitely bring in an "empty" list. I don't mind empty lists, but it shouldn't give "object null reference."
I would write the join statement a little bit different:
var query = (from s in entities.tableContacts
join o in entities.tableObject2Contacts
on new { ContactID = s.contact_id, ObjectID = oid }
equals new { ContactID = o.contact_id, ObjectID = o.objectID }
into oTemp
from o in oTemp.DefaultIfEmpty()
select new { s });
This would be a proper left-join using linq.
Maybe your NullReferenceException is caused by a wrong join or something.
Nevermind, The "I also tried this" is actually correct code, but for some reason I hadn't established my entities properly so that was returning null.
Although I'm still super confused about "anonymoustypes" I wouldn't know how to make a function that returns "anonymoustype".

LINQ join on columns AND nullable variable

I am trying to join tables using LINQ by matching columns where a column in the joined table is equal to a variable or the variable is null (at which point the join still needs to happen just not on that field).
My LINQ is something like:
var data = (
from lt in cxt.CmsPageRow
join page in cxt.CmsPage on new { lt.CmsPageID, cmsSiteID.Value } equals new { page.CmsPageID, page.CmsSiteID }
...
cmsSiteID is a nullable INT.
I cannot compile my code as it is complaining about "Type inference failed in the call to 'Join'."
On top of that I need to only join on page.CmsSiteID when cmsSiteID is not null. If cmsSiteID is null then the join on lt.CmsPageID still needs to happen.
* EDIT *
The question has kind of changed now. I can get it to do what I want by using a WHERE clause on the join in my LINQ.
join page in cxt.CmsPage.Where(p=>(cmsSiteID==0||p.CmsSiteID==cmsSiteID)) on lt.CmsPageID equals page.CmsPageID
However, this still runs slow. If I change the parameter passed through to a literal it executes instantly.
Slow runner
(#p__linq__1 = 0 OR [Extent2].[CmsSiteID] = #p__linq__1)
Fast runner
(267 = 0 OR [Extent2].[CmsSiteID] = 267)
Is there a way to speed this up?
join in LINQ assumes an inner join (no nulls). Try pulling the null stuff out into separate where clauses. I think something along these lines should work for what you're describing.
from lt in cxt.CmsPageRow
join page in cxt.CmsPage on lt.CmsPageID == page.CmsPageID
where cmsSiteID == null ||
(cmsSiteID != null && (page.CmsSiteID == null || page.CmsSiteId == cmsSiteID.Value))
select ...
Update
I didn't realize that performance was an issue for you. In that case, I'd suggest creating a different query structure based on values that are known at run-time and don't depend on individual rows:
var rows =
from lt in cxt.CmsPageRow
join page in cxt.CmsPage on lt.CmsPageID == page.CmsPageID
select new {lt, page};
if (cmsSiteID != null)
{
rows = rows.Where(r => r.page.CmsSiteID == null ||
r.page.CmsSiteId == cmsSiteID.Value));
}
var data = rows.Select(...);
Also, if your data context is set up right, you should be able to use navigation properties to simplify your code somewhat.
var rows = ctx.CmsPageRow;
if (cmsSiteID != null)
{
rows = rows.Where(r => r.CmsPage.Any(p => p.CmsSiteID == null ||
p.CmsSiteId == cmsSiteID.Value));
}
var data = rows.Select(...);

Converting t-sql query into EF's method syntax

What would be an EF method syntax equivalent for the following TSQL query?
select istb.service_id, ss.service_desc, selected=1
from istb_services istb
inner join setup_services ss on istb.service_id=ss.service_id
where istb.istb_id=3
union
select ss.service_id, ss.service_desc, selected=0
from setup_services ss
where ss.service_id not in (select service_id from istb_services where istb_id=3)
I tried converting the not in part of the query like following:
var _existing = context.istb_services.Where(e => e.istb_id == IstbID);
var _others = context.setup_services.Except(_existing);
but it is generating compile-time error:
The best overloaded method match for 'System.Data.Objects.ObjectQuery.Except(System.Data.Objects.ObjectQuery)' has some invalid arguments
I understand I can't pass different type of ObjectQuery to the .Except method but then what would be the alternative code?
Thanks,
Try the following:
var resultA =
from istb in istb_services
join ss in setup_services on istb.service_id equals ss.service_id
where istb.istb_id == 3
select new { istb.service_id, ss.service_desc, selected = true };
var resultB =
from ss in setup_services
where !istb_services.Any(istb =>
istb.service_id == ss.service_id &&
istb.istb_id == 3)
select new { ss.service_id, ss.service_desc, selected = false };
var result = resultA.Union(resultB);
Anonymous type initializers having identical fields should be compiled to the same anonymous type, making the two sequences compatible for the Union operation.

"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
&& 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).

Categories

Resources