linq join on guid and string column - c#

I'm new to linq. I need to run a query that joins two columns (AnonymousUser.AnonymousId being uniqueidentifier and comment.UserId being nvarchar(100)), something like below:
using (CommentEntities db = new CommentEntities())
{
// filteredComments is a query that is not run until the next .ToList()
IQueryable<Comment> filteredComments = this.CommentGetList(...);
var query = from comment in filteredComments
// following line is the syntax error, because columns' types don't match
join user in db.AnonymousUsers on comment.UserId equals user.AnonymousId into gj
from userNull in gj.DefaultIfEmpty()
select new CommentWithName
{
Comment = comment,
UserId = comment.UserId,
FirstName = (userNull == null ? "" : userNull.Name),
LastName = "",
Email = (userNull == null ? "" : userNull.Email)
};
return query.ToList();
}
First I was happy writing the query with .ToString() ! As it turns out that entity framework doesn't know how to translate it to sql. The same is true for Guid.Parse(string). Also new Guid(string) cannot be used in linq to entities (only parameterless constructors allowed)!
So after searching, I found out it's not possible doing such thing in EF 4.0! I migrated my code to a stored procedure that I'm not really happy about it.
Is it possible to tell entity framework to use a CAST in SQL?
Is there any solutions to this problem? Is there any way that I can bring the logic in code?
NOTE: I meant to do it in one GO. Otherwise one possible solution is to get Entities from first table, and put the Ids in a list and get entities from second table.

call toList() before applying those methods. Like:
var Product = db.Products.Where(p => p.ProductId == Guid.Parse("B4E913F9-166C-49BA-AADE-6DB889D1756F")).Single();
Would throw a
c# LINQ to Entities does not recognize the method "System.Guid Parse" (System.String)' method, and this method cannot be translated into a store expression
But this works:
var Product = db.Products.ToList().Where(p => p.ProductId == Guid.Parse("B4E913F9-166C-49BA-AADE-6DB889D1756F")).Single()
p.s.: I think you will lose lazyloading but you can do eagerloading with .Include before calling .ToList().

If your list is object list you could convert it to the type which has Guid as identifier, first create new anonymous type and then filter it base on UserId, sure UserId which is of type int, wont include in join:
int output = 0;
var secondList = list.Where(x=>!int.TryParse(x.UserID, out output))
.Select(x=>new {Comment = x, ID = new Guid(x.UserID))
.ToList();
Now you could run your query on db by using secondList.

Related

ServiceStack ORMLite: Best way to alias primary table and select list in query with join?

I have the following ORMLite query in which I wish to return the columns from the primary table, i.e. the one referenced by the From<T>() method, filtered by a join to a secondary table.
var query = conn
.From<SurveyQuestionOptionDTO>()
.Join<SurveyQuestionDTO>((o, q) => o.SurveyQuestionId == q.Id, conn.JoinAlias("q"))
.Where<SurveyQuestionDTO>(q => q.SurveyId == surveyId);
return conn.Select(query);
This generates the following SQL query
SELECT "Id", "SurveyQuestionId", "Option", "Selected", "Sequence"
FROM "dbo"."SurveyQuestionOptions"
INNER JOIN "dbo"."SurveyQuestions" q
ON ("dbo"."SurveyQuestionOptions"."SurveyQuestionId" = "q"."Id")
WHERE ("dbo"."SurveyQuestions"."SurveyId" = #0)
This would be fine except that both tables have Id and Sequence columns so the query fails with ambiguous column references. If I was hand-coding the SQL I would simply alias the SurveyQuestionOptions table, for instance with o and use that alias on each column in the select list, like o.Id, o.SurveyQuestionId, o.Option, o.Selected, o.Sequence or even just o.* as all columns are being returned. My question is, what is the best way to make ORMLite generate such code?
I have found a way to do it, by adding a Select<T>() method returning an anonymous class, as follows
var query = conn
.From<SurveyQuestionOptionDTO>()
.Join<SurveyQuestionDTO>(
(o, q) => o.SurveyQuestionId == q.Id && q.SurveyId == surveyId,
conn.JoinAlias("q"))
.Select<SurveyQuestionOptionDTO>(o => new
{
o.Id,
o.SurveyQuestionId,
o.Option,
o.Selected,
o.Sequence
});
return conn.Select(query);
This works, but it seems like a lot of extra code to achieve a simple result, and because columns are explicitly returned, requires this code to change if the table ever gets a new column and the DTO class is re-generated. Is there a better, simpler way?
I have found a simpler way that also resolves future impact of column changes. Instead of returning a new anonymous class from the Select<T>() method you can simply return the instance that's passed in. So the code now looks like this, and still works as expected.
var query = conn
.From<SurveyQuestionOptionDTO>()
.Join<SurveyQuestionDTO>(
(o, q) => o.SurveyQuestionId == q.Id && q.SurveyId == surveyId,
conn.JoinAlias("q"))
.Select<SurveyQuestionOptionDTO>(o => o);
return conn.Select(query);

C# Linq Group by Object

I have an issue of using group by in LINQ to SQL statement.
The cod I have is
var combinedItems = (from article in articles
join author in authors
on article.AuthorId equals author.Id into tempAuthors
from tempAuthor in tempAuthors.DefaultIfEmpty()
select new { article , author = tempAuthor});
var groups1 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article into g
select g.Key).ToList();
var groups2 = (from combinedItem in combinedItems
group combinedItem by combinedItem.article.Id into g
select g.Key).ToList();
I tried to group in two different ways. The first way, I group by an object and the second way I just group by a field in one of the objects.
When I run groups1, I got an error saying need to evaluate in client side, while when I use groups2, it works all good. Can I ask what could be wrong? If I want to group by object, is there any way to do it?
In case you want to group by object, as you've not overridden Equals and GetHashCode() in your Article class or implemented IEqualityComparer<Article> you're just getting the default comparison, which checks if the references are equal. So what you need is something like this:
class GroupItemComparer : IEqualityComparer<Article>
{
public bool Equals(Article x, Article y)
{
return x.Id == y.Id &&
x.Name == y.Name;
}
public int GetHashCode(Article obj)
{
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode();
}
}
And then you need to change your query to lambda expression:
var groups1 = combinedItems.GroupBy(c => c.article , new GroupItemComparer())
.Select(c => c.Key).ToList();
In case you got any exception regarding translation your method to SQL, you can use AsEnumerable or ToList methods before your GroupBy method, with this methods after data is loaded, any further operation is performed using Linq to Objects, on the data already in memory.
As others have pointed out, the GroupBy is using reference equality by default, and you could get around it by specifying one or more properties to group by. But why is that an error?
The whole point of the query is to translate your Linq query into SQL. Since object reference equality on the client can't be easily translated to SQL, the translator doesn't support it and gives you an error.
When you provide one or more properties to group by, the provider can translate that to SQL (e.g. GROUP BY article.Id), and thus the second method works without error.

Marc Gravell's Dynamic OrderBy works in one case but not in other

I am trying to do a dynamic order by on columns using Marc Gravell's code. I am posting the 2 queries. It works in one case but doesn't work in 2nd case. Can anybody tell me what changes I need to make to make both queries run perfectly?
This is the link to the Marc Gravell's answer:
https://stackoverflow.com/a/233505
I am using Northwind database. These are both my queries:
var query = (from cust in northwindEntities.Customers
select new
{
City = cust.City ,
Orders = northwindEntities.Orders
.Where(o => o.CustomerID == cust.CustomerID)
.OrderBy("OrderID")
}); // doesn't work.
var query = (from cust in northwindEntities.Customers
select new
{
City = cust.City ,
//Orders = northwindEntities.Orders.Where(o => o.CustomerID == cust.CustomerID).
// OrderBy("OrderID")
}).OrderBy("City"); // works
Here is the exception of the 1st query:
LINQ to Entities does not recognize the method
'System.Linq.IOrderedQueryable1[ConsoleApplication12.Order]
OrderBy[Order](System.Linq.IQueryable1[ConsoleApplication12.Order],
System.String)' method, and this method cannot be translated into a
store expression.
You need to order the final set of result, like you do it in the second case. In first case you order only northwindEntities.Orders.Where( result and not final one.
The correct query is the second.
Obviously it would not work because of the same reason as
var query = (from cust in northwindEntities.Customers
select new
{
City = cust.City ,
Orders = northwindEntities.Orders
.MyCustomMethod()
});
will not work. LINQ-to-Entities will walk through this expression tree and try to convert it to SQL. It can work on known sub set of methods to translate to SQL.
But in the second query, custom OrderBy method dynamically creates the OrderBy that LINQ-to-Entities knows.

How do I implement a dynamic 'where' clause in LINQ?

I want to have a dynamic where condition.
In the following example:
var opportunites = from opp in oppDC.Opportunities
join org in oppDC.Organizations
on opp.OrganizationID equals org.OrgnizationID
where opp.Title.StartsWith(title)
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
Some times I have Title and sometimes I don't. And also I want to add date in where clause dynamically.
For example, like this SQL:
string whereClause;
string SQL = whereClause == string.Empty ?
"Select * from someTable" : "Select * from someTable" + whereclause
You can rewrite it like this:
var opportunites = from opp in oppDC.Opportunities
join org in oppDC.Organizations on opp.OrganizationID equals org.OrgnizationID
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
if(condition)
{
opportunites = opportunites.Where(opp => opp.Title.StartsWith(title));
}
EDIT: To answer your question in the comments, yes, you can keep appending to the original Queryable. Remember, this is all lazily executed, so at this point all it's doing it building up the IQueryable so you can keep chaining them together as needed:
if(!String.IsNullOrEmpty(title))
{
opportunites = opportunites.Where(.....);
}
if(!String.IsNullOrEmpty(name))
{
opportunites = opportunites.Where(.....);
}
You can dynamically add a where clause to your IQueryable expression like this:
var finalQuery = opportunities.Where( x => x.Title == title );
and for the date similarly.
However, you will have to wait to create your anonymous type until after you've finished dynamically added your where clauses if your anonymous type doesn't contain the fields you want to query for in your where clause.
So you might have something that looks like this:
var opportunities = from opp in oppDC.Opportunities
join org in oppDC.Organizations on
opp.OrganizationID equals org.OrgnizationID
select opp
if(!String.IsNullOrEmpty(title))
{
opportunities = opportunities.Where(opp => opp.Title == title);
}
//do the same thing for the date
opportunities = from opp in opportunities
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
The WHERE clause could be done something like
//...
where string.IsNullOrEmpty(title) ? true : opp.Title.StartsWith(title)
//...
Dynamically returning records I don't think is possible in LINQ since it needs to be able to create a consistent AnonymousType (in the background)
Because queries are composable, you can just build the query in steps.
var query = table.Selec(row => row.Foo);
if (someCondition)
{
query = query.Where(item => anotherCondition(item));
}
If you know in advance all possible where queries like in the SQL example you have given you can write the query like this
from item in Items
where param == null ? true : ni.Prop == param
select item;
if you don't know all possible where clauses in advance you can add where dymically for example like this:
query = query.Where(item => item.ID != param);
The following questions and answers address this quite well:
Dynamic where clause in LINQ - with column names available at runtime
Is there a pattern using Linq to dynamically create a filter?
I was searching for creating a dynamic where clause in LINQ and came across a very beautifull solution on the web which uses ExpressionBuilder in C#.
I am posting it here since none of the above solution uses this approach. It helped me. Hope it helps you too
http://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq

LINQ to Entity: using Contains in the "select" portion throws unexpected error

I've got a LINQ query going against an Entity Framework object. Here's a summary of the query:
//a list of my allies
List<int> allianceMembers = new List<int>() { 1,5,10 };
//query for fleets in my area, including any allies (and mark them as such)
var fleets = from af in FleetSource
select new Fleet
{
fleetID = af.fleetID,
fleetName = af.fleetName,
isAllied = (allianceMembers.Contains(af.userID) ? true : false)
};
Basically, what I'm doing is getting a set of fleets. The allianceMembers list contains INTs of all users who are allied with me. I want to set isAllied = true if the fleet's owner is part of that list, and false otherwise.
When I do this, I am seeing an exception: "LINQ to Entities does not recognize the method 'Boolean Contains(Int32)' method"
I can understand getting this error if I had used the contains in the where portion of the query, but why would I get it in the select? By this point I would assume the query would have executed and returned the results. This little ditty of code does nothing to constrain my data at all.
Any tips on how else I can accomplish what I need to with setting the isAllied flag?
Thanks
This poached from a previous answer...
Contains not supported.
IN and JOIN are not the same operator (Filtering by IN never changes the cardinality of the query).
Instead of doing it that way use the join method. It's somewhat difficult to understand without using the query operators, but once you get it, you've got it.
var foo =
model.entitySet.Join( //Start the join
values, //Join to the list of strings
e => e.Name, // on entity.Name
value => value, //equal to the string
(ModelItem ent, String str) => ent);//select the entity
Here it is using the query operators
var foo = from e in model.entitySet
join val in values on
e.Name equals val
select e;
Basically the entity framework attempts to translate your LINQ query into a SQL statement but doesn't know how to handle the Contains.
What you can do instead is retrieve your fleets from the database and set the isAllied property later:
var fleets = (from af in FleetSource
select new Fleet
{
fleetID = af.fleetID,
fleetName = af.fleetName,
userId = af.userId
}).AsEnumerable();
foreach (var fleet in fleets)
{
fleet.isAllied = (allianceMembers.Contains(fleet.userID) ? true : false);
}
Everyone above me is wrong!!! (No offense ...) It doesn't work because you are using the IList overload of "Contains" and not the IEnumerable overload of "Contains". Simply change to:
allianceMembers.Contains<int>(af.userID)
By adding the <int>, you are telling the compiler to use the IEnumerable overload instead of the IList overload.
var fleets = from af in FleetSource;
var x = from u in fleets.ToList()
select new Fleet
{
fleetID = u.fleetID,
fleetName = u.fleetName,
isAllied = (allianceMembers.Contains(u.userID) ? true : false)
}
calling ToList() on fleets the query is executed, later you can use Contains().

Categories