I've got Two tables
Attribute(AID, Name, Description)
AttributeVal(AID,Value)
Now, Using LINQ I need to get the Name, Description and Value by joining these two tables. So, far I got here..
var displayAttributeMap = dctx.Attributes.Join(dctx.AttributeDisplayNames, a => a.AttributeID, adn => adn.AttributeID,
(a, adn) => new
{
AttributeName = a.Name,
AttributeDispName = adn.DisplayName
}
).ToDictionary(a => a.AttributeName, a => a.AttributeDispName);
But here I don't know where to insert a condition on the foreign key, in my case it is Attribute ID. Am I missing something here or should I use LINQ directly?
From what I gather, you need the value AND the display name of the attribute put into a dictionary.
All you need to do is add another join to your current query and change your select and ToDictionary around a bit. I find using the query syntax easier to read and use when dealing with joins.
(from attr in dctx.Attributes
join attrDisplayName in dctx.AttributeDisplayNames on attr.AttributeID equals attrDisplayName.AttributeID
join attrVal in dctx.AttributeVal on attr.AttributeID equals attrVal.AttributeID
select new
{
AttributeName = attr.Name,
AttributeDispName = attrDisplayName.DisplayName,
AttributeID = attr.AttributeID,
AttributeValue = attrVal.Value
}).ToDictionary(key => key.AttributeID, (value) => { return new { DisplayName = value.AttributeDispName, Value = value.AttributeValue };})
I haven't tested it but LinqPad is happy with the syntax, so I think it should work.
Related
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);
This question already has answers here:
An anonymous type cannot have multiple properties with the same name
(2 answers)
Closed 2 years ago.
I need to join three entities from which I need to sum one column and the name of the column is same in two entity. In sql I can achieve this by using following query
select
a.ObjectHeadId,
a.DepartmentId,
b.Name as ObjectHeadName,
c.Name as DepartmentName,
SUM(BudgetEstimate)
from ConsolidatedAppendix1s a
inner join ObjectHeads b on b.ObjectHeadId=a.ObjectHeadId
inner join Departments c on a.DepartmentId=c.DepartmentId
group by a.ObjectHeadId, a.DepartmentId, b.Name, c.Name
While trying same in EF Core I am doing like this
consolidatedAppendixI = await _context.ConsolidatedAppendix1s
.Include(entity => entity.Department)
.Include(entity => entity.ObjectHead)
.GroupBy(r => new {
r.Department.Name,
r.DepartmentId,
r.ObjectHeadId,
r.ObjectHead.Name
})
.Select(a => new
{
ObjectHeadId = a.Key.ObjectHeadId,
DepartmentId=a.Key.DepartmentId
BudgetEstimate = a.Sum(r => r.BudgetEstimate),
ObjectHeadName= a.Key.Name,
DepartmentName = a.Key.Name,
}).ToListAsync(cancellationToken);
While doing so it shows An anonymous type cannot have multiple properties with the same name.
I can achieve this using
GroupBy(x=> 1)
but the problem here is I cannot be able to get other columns in Key while Selecting likewise it is done above for ObjectHeadId, ObjectHeadName etc.
This is because the name in the group by is same even though they are the property of different entity. How to group those column to get the sum or sql equivalent linq query?
Give the properties different names; it is essentially the same thing as the way you used AS in your SQL SELECT block:
new {
DeptName = r.Department.Name,
DeptId = r.DepartmentId, //optional rename
HeadId = r.ObjectHeadId, //optional rename
HeadName = r.ObjectHead.Name
})
And refer to these new names in the Select. You don't have to rename the DepartmentId and ObjectHeadId because they're already distinct but I find it makes code neater than to have a mix of renamed and non renamed:
new {
DeptName = r.Department.Name,
r.DepartmentId,
r.ObjectHeadId,
HeadName = r.ObjectHead.Name
})
You're of course free to choose this route if you like!
If you do not specify a property name when creating an anonymous type (by writing a name on th left hand side of an =), the compiler will infer one from the name of the property whose value you are using. Because you have two properties with the name Name (on different objects), the compiler will essentially try and create an anonymous type like this:
var x = new {
Name = thing1.Name,
Name = thing2.Name
}
Of course, this couldn't work because if you said var y = x.Name it isn't clear which Name you're talking about
As an aside,
My table contains several columns and I need to select distinct rows in two specific columns using Linq.
My SQL equivalent is:
Select distinct Level1Id, Level1Name
from levels
What I currently do is:
db.levels.GroupBy(c=> c.Level1Id).Select(s => s.First())
This will retrieve the whole row not only Level1Id and Level1Name. How can I specify the columns I want to retrieve in this linq query?
With Select, you can specify the columns in an anonymous object and then use Distinct on that:
db.levels.Select(l => new{ l.Level1Id, l.Level1Name }).Distinct();
try
db.levels.Select(c => new {c.Level1Id, c.Level1Name}).Distinct();
Specify the two columns in your LINQ query select, create an anonymous object with Level1Id and Level1Name properties:
var query = (from v in db.levels
select new { Level1Id = v.Level1Id, Level1Name = v.Level1Name }).Distinct();
and use each item like this:
foreach (var r in query){
int valId = r.LevelId;
int level = r.Level1Name;
//do something
}
You are so close, one more step:
var result = db.levels.GroupBy(c=> new { c.Level1Id, c.Level1Name })
.Select(s => s.First())
The key thing is: Anonymous type uses structural comparison, that's why GroupBy or any other answer do work.
I am trying to take the TSQL below and convert it into LINQ. I'm obviously not very good at this and not sure how to go about doing a join in Linq. My confusion goes a little beyond just the simple expression. In my basic uses my result set is a class that of course represents a table row...but a join would not have this one to one ratio so do I have to create a custom result set?
TSQL:
SELECT
SimEA.*,
SimE.*
FROM
dbo.SSimEmailsAdressees SimEA
JOIN dbo.SSimEmails SimE ON
SimEA.EmailID = SimE.EmailMsgID
WHERE
SimEA.UserId = var
The closest I have come:
this.GetAll<SSimEmails>()
.Where(e => e.SSimEmailsAdressees.Any(p => p.UserId.ToString() == usrID));
Which of course return an object that is a mimic of the single table being queried. So I need help getting the join added and I'm guessing I'll have to create a return object something like this?
this.GetAll<MyJoinResultsObject>......
I think that you should write something like:
var q =
from SimEA in SSimEmailsAdressees
where SimEA.UserId = usrID
join SimE in SSimEmails on SimEA.EmailID equals SimE.EmailMsgID
into joinSimE
from SimE in joinSimE.DefaultIfEmpty()
select new { EmailID = SimEA.EMailID, UserId = SimEA.UserId, OtherField = SimE.OtherField };
var result = from ea in SimEA
join e in SimE
on ea.EmailID equals e.EmailMsgID
where ea.UserId = userId
select new { properties you want here or new object based on properties}
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.