I'm trying to use lambda to join 5 tables. What I would like to know is if I'm doing it right and if there's a better way of doing it. Any suggestion is much appreciated.
var myList = _context.Event
.Join(_context.EventHost,
e => e.EventHostId,
eh => eh.Id,
(e, eh) => new { Event = e, EventHost = eh })
.Join(_context.EventAddress,
eh => eh.EventHost.EventAddressID,
ea => ea.ID,
(eh, ea) => new { EventAddress = ea, EventHost = eh })
.Join(_context.OrderHeaderGroup,
ea => ea.EventHost.Event.Id,
ohg => ohg.EventId,
(ea, ohg) => new { EventAddress = ea, OrderHeaderGroup = ohg })
.Join(_context.OrderHeader,
ohg => ohg.OrderHeaderGroup.OrderHeaderGroupID,
oh => oh.OrderHeaderGroupID,
(oh, ohg) => new { OrderHeader = oh, OrderHeaderGroup = ohg })
.Select(c => new {
c.OrderHeader.OrderHeaderGroup.EventId,
c.OrderHeader.EventAddress.EventAddress.Name,
c.OrderHeader.EventAddress.EventAddress.Phone,
c.OrderHeader.EventAddress.EventAddress.Address1,
c.OrderHeader.EventAddress.EventAddress.Address2,
c.OrderHeader.EventAddress.EventAddress.City,
c.OrderHeader.EventAddress.EventAddress.Country,
c.OrderHeader.EventAddress.EventAddress.PostalCode,
c.OrderHeader.EventAddress.EventAddress.Email,
c.OrderHeader.EventAddress.EventHost.Event.ConsultantId,
c.OrderHeader.EventAddress.EventHost.Event.StartDate,
c.OrderHeader.EventAddress.EventHost.Event.EndDate,
}).Where(x => x.ContactId == contactId && x.StartDate >= newStartDate && x.EndDate <= newEndDate).ToList();
Yes, use the query syntax, as is the recommendation from Microsoft: Check here
At compile time, query expressions are converted to Standard Query Operator method calls according to the rules set forth in the C# specification. Any query that can be expressed by using query syntax can also be expressed by using method syntax. However, in most cases query syntax is more readable and concise.
from event in _context.Event
join eventHost in _context.EventHost
on event.EventHostId equals eventHost.Id
join eventAddress in _context.EventAddress
on eventHost.EventAddressId equals eventAddress.Id
// etc
select new {/*You new anonymous type here*/}
The issue here isn't preferential code readability. Which, BTW, I think Lambda is far more readable than Query form. But that's because I'm a programmer.
The issue is that you are trying to take a SQL Developer's perspective and SHOVE It into programming. I did that when LinqToEntities was first a thing, where I start in SSMS et al, I write a query, then I do Transliteration into C#.
The solution is to use your ORM. If you use a feature rich ORM, you should have something called Navigation Properties. These already have implicit joins for you that are either implicitly, or explicitly declared in your Fluent mapping. So you shouldn't have to re-describe every join every time you write a LINQ statement.
If you trust the language and configure your system correctly, you can end up with something like this:
// This is not shortened, this should be the whole thing.
var myList = _context.OrderHeader
.Where(x => x.EventAddress.Something.ContactId == contactId
&& x.EventAddress.Something.StartDate >= newStartDate
&& x.EventAddress.Something.EndDate <= newEndDate)
.Select(c => new DetailEntityView(c)).ToList();
I also suggest using "View" classes. These aren't entities that are configured to persist to the DB, but are like DB Views, in C#. They are less used classes that just transfer data.
public class DetailEntityView
{
public int EventId { get; set; }
public string Name { get; set; }
// ... etc
public DetailEntityView(OrderHeader c)
{
EventId = c.OrderHeaderGroup.EventId;
Name = c.EventAddress.EventAddress.Name;
// ... etc
}
}
Now, some ORMs are less featured than the EF's and NH's of the world. So in that case, I'm sorry you or your team chose something else.
Also, all of your joins are on IDs, which is as it should be. But SOME Custom queries join on field that are not constrained, like name fields or other values. In those cases, you do need to add those specific joins to your query. But 1.) Ew. And 2.) Why? And 3.) Then you can do what you did in your OP.
Related
Presumed I have a table "Article" with multiple columns, e.g.:
CREATE TABLE dbo.Article (
Id int NOT NULL,
ProducerId INT NOT NULL,
Barcode nvarchar(50) NOT NULL,
DescriptionText nvarchar(50) NOT NULL,
ActiveFlag BIT NOT NULL
)
In my ASP.NET Core application I am using LINQ to query that table, e.g.:
IQueryable<Article> query = _context.Article
.Where( p => p.Active == true );
That works of course.
Now I am getting some parameter, which is a very simple Object with a List < ArticleQuery > or alternativly a IEnumerable < ArticleQuery >.
ArticleQuery.cs:
public class ArticleQuery
{
[Required]
public IEnumerable<ArticleRequest> Parts { get; set; }
}
public class ArticleRequest
{
public int ProducerId { get; set; }
public string Barcode { get; set; }
}
Actually I have no clue how to integrate that into LINQ. I tried lots of stuff, but in the end I never figured out to get something like this join:
IQueryable<Article> query = _context.Article
.Join(articleQuery.ArticleRequest,
x => new { a = x.Barcode, b = x.Barcode},
y => new { a = y.ProducerId, b = y.ProducerId},
(x, y) => x);
Also this pseudo-code didn't work (but the Join seems to be the better try):
IQueryable<Article> query = _context.Article.Where( p =>
p.Active == true &&
articleQuery.ArticleRequest.ProducerId.Contains(p.ProducerId) &&
articleQuery.ArticleRequest.Barcode.Contains(p.Barcode)
);
Any ideas how that works correctly?
Unfortunately, this is a situation that normal LINQ to Entities code can't handle very well.
The syntax you'd want to use looks like this:
IQueryable<Article> query = _context.Article
.Where( p =>
p.Active == true &&
articleQuery.ArticleRequest.Any(
r => r.ProducerId == p.ProducerId &&
r.ArticleRequest.Barcode == p.Barcode
)
);
Unfortunately, LINQ to Entities can't handle mixing in-memory collections into database queries like this.
In theory, you could represent your ArticleRequest as a model in the database and perform a query similar to the one above, but based on an IQueryable<ArticleRequest> from your context rather than just a parameter variable. But that would be a terribly hacky workaround.
Some people resort to materializing the entire set of Article objects so they can use a LINQ to Objects provider:
IQueryable<Article> query = _context.Article
.AsEnumerable() // DANGER: materializes *all* the articles
.Where( p =>
p.Active == true &&
articleQuery.ArticleRequest.Any(
r => r.ProducerId == p.ProducerId &&
r.ArticleRequest.Barcode == p.Barcode
)
);
But this approach won't scale well as you get more articles.
Another approach is to build expressions trees dynamically, which would result in an Expression that looks like this:
p =>
p.Active == true &&
((p.ProducerId == 12 && p.BarCode == "12345") ||
(p.ProducerId == 13 && p.BarCode == "54321") ||
...
)
But the code required to produce this kind of statement dynamically is pretty ugly and very difficult to understand.
A better solution would probably be to use a bunch of Unions. One concise way to represent that is:
IQueryable<Article> query = articleQuery.ArticleRequest.Aggregate(
_context.Article.Where(p => false),
(q, r) => q.Union(context.Article.Where(p =>
r.ProducerId == p.ProducerId &&
r.ArticleRequest.Barcode == p.Barcode))
)
.Where(p => p.Active == true);
This will produce a SQL query that unions the results of several one-off queries against each other. With a little more C# code, you could remove the superfluous "Where False" part of the query.
There are other libraries like Dynamic LINQ and LINQKit which are designed to help with this sort of problem, but it's been a long time since I've played with them and I can't vouch for how well they'll work against modern versions of Entity Framework.
Alternatively, you can avoid using LINQ entirely and just construct some custom SQL for this one query. That's probably what I'd do. Just be careful to guard against SQL injection on that barcode!
I solved the problem by myself. Not that perfect, but it's working.
I created a new database for my webservice with a table "RequestArticles":
CREATE TABLE dbo.RequestArticle(
RequestId UNIQUEIDENTIFIER NOT NULL,
ProducerId INT NOT NULL,
Barcode NVARCHAR(30) NOT NULL
)
I wrote my requests into that table:
Guid requestGuid = Guid.NewGuid();
foreach (var requestArticle in requestedArticles.Articles) requestArticle.RequestId = requestGuid;
_context.RequestArticle.AddRange(requestedArticles.Articles); //IEnumerable list of articles
_context.SaveChanges();
After that I just manually joined my new request-table.
IQueryable<Articles> query = _context.Articles.FromSql<Articles>(
#"SELECT a.*, ra.Barcode AS RequestedBarcode
FROM dbo.Articles a
INNER JOIN Webservice.dbo.RequestArticle ra
ON ra.ProducerId = a.ProducerId AND ra.Barcode = a.Barcode
WHERE ra.RequestId = {1}",
requestGuid);
At the moment I am suffering to replace the new static database-name within the SQL-String with a different string from my new context, but the basics work fast like a charm. Even though it's not the solution I was hoping for, as I thought LINQ+EF are smarter than that. :-)
I can't read complicated lambda expression and only the super basic lambda expression only I know I am just starting to study lambda.
As my title above how to convert this lambda into linq?
var train = db.Certificates
.Join(db.TrainingSchedules, a => a.CertificateId, b => b.CertificateId, (a, b) => new { a, b })
.Where(x => x.a.Year.Value.Year == year && x.a.TrainingTypeId.Value == trainingTypeId && x.a.IsApproved.Value && x.b.EndDate >= DateTime.Now)
.Select(z => z.a).Distinct().Where(q => !db.Registrations.Where(s => s.EmployeeId == empId).Select(t => t.Certificate).Any(u => u.CertificateId == q.CertificateId));
Can someone explain to me why it has a different variables?. Like x , q , z , b?
As my title above how to convert this lambda into linq?
Have you every worked in Linq to know how does it looks like ?
If the above specified code is not Linq, then what is Linq, Lambda is an integral part of fluent representation of the Linq, since most of the APIs would need a Func delegate, that's where Lambda comes in.
Now regarding x , q , z , b, what do they represent ?
The Linq APIs are nothing but extension methods for IEnumerable<T>, like List<T>, Dictionary<TK,TV>, whether we write the fluent' or theSqlsyntax these variables represent, each and every element in the collection, which is processed as part of logic provided by theFunc Delegate, you can certainly use and should use more credible variable to represent the exact thing, similar to other parts of code whereint x, float y, DateTime z` is a bad way to code
Regarding the statement posted above consider following changes:
Rename a,b as cert,ts, which refers to a Certificate and Training Schedule classes respectively
Instead of generating anonymous type new { a, b }, create a class like CerificateTrainingSchedule that has all the elements of the Certificate and Training Schedule class respectively, it would be easier to work as you move forward
Rename x as cts, to represent combined CerificateTrainingSchedule
If its easy to read then separate Where clause in multiple chains, like:
.Where(cts => cts.a.Year.Value.Year == year)
.Where(cts => cts.a.TrainingTypeId.Value == trainingTypeId)
.Where(cts => cts.a.IsApproved.Value)
.Where(cts => cts.b.EndDate >= DateTime.Now)
Similarly the names of other variables can be modified to represent the true class and its objects, instead of random a,b,c,d. Also calls can be chained for clear understanding of the logic
Edit - // Modified Linq Query
// Joined / Merged version of Certificate and Training Schedule, add more fields as required, current one is based on certain assumptions of the fields / properties in the Certificate & TrainingSchedule classes respectively
public class CertificateTrainingSchedule
{
public int Year {get; set;} // Certificate Class Property
public int TrainingTypeId {get; set;} // Certificate Class Property
public bool IsApproved {get; set;} // Certificate Class Property
public DateTime EndDate {get; set;} // TrainingSchedule Class Property
}
var train = db.Certificates
.Join(db.TrainingSchedules, cert => cert.CertificateId, ts => ts.CertificateId, (cert, ts) => new CertificateTrainingSchedule{ Year = cert.Year, TrainingTypeId = cert.TrainingTypeId, IsApproved = cert.IsApproved,EndDate = ts.EndDate})
.Where(cts => cts.Year == year)
.Where(cts => cts.TrainingTypeId == trainingTypeId)
.Where(cts => cts.IsApproved)
.Where(cts => cts.EndDate >= DateTime.Now)
.Select(cts => new {cts.Year,cts.TrainingTypeId,cts.IsApproved})
.Distinct() // Allowing anonymous type to avoid IEqualityComparer<Certificate>
.Where(certMain => !db.Registrations.Where(s => s.EmployeeId == empId)
.Select(cert => new Certificate{Year = cert.Year,TrainingTypeId = cert.TrainingTypeId,IsApproved = cert.IsApproved})
.Any(cert => cert.CertificateId == certMain.CertificateId))
I assume that by your question, you mean you want a query expression that is equivalent to calling the LINQ methods explicitly. Without a good Minimal, Complete, and Verifiable code example, it's impossible to know for sure what a correct example would be. However, the following is I believe what you're looking for:
var train =
from q in
(from x in
(from a in db.Certificates
join b in db.TrainingSchedules on a.CertificateId equals b.CertificateId
select new { a, b })
where x.a.Year.Value.Year == year && x.a.TrainingTypeId == trainingTypeId &&
x.a.IsApproved.Value && x.b.EndDate >= DateTime.Now
select x.a).Distinct()
where !(from s in db.Registrations where s.EmployeeId == empId select s.Certificate)
.Any(u => u.CertificateId == q.CertificateId)
select q;
Note that not all of the LINQ methods have a C# query expression language equivalent. In particular, there's no equivalent for Distinct() or Any(), so these are still written out explicitly.
Can someone explain to me why it has a different variables?. Like x , q , z , b?
Each lambda expression has the input on the left side of the => and the result expression on the right. The variables you're referring to are the inputs. These are commonly written using single letters when writing lambda expressions, because the a lambda expression is so short, the meaning can be clear without a longer variable name. For that matter, independent lambda expressions could even use the same variable name.
Note that in the query expression syntax, not all of the variables "made it". In particular, we lost z and t because those variables were superfluous.
In an expression this long, it's possible you might find longer variable names helpful. But it's a trade-off. The query expression language is meant to provide a compact way to represent queries on data sources. Longer variable names could make it harder to understand the query itself, even as it potentially makes it easier to understand the intent of each individual part of the expression. It's very much a matter of personal preference.
I can't read complicated lambda expression and only the super basic lambda expression only I know I am just starting to study lambda.
Try reading this:
var train = db.Certificates
.Where(c => c.Year.Value.Year == year &&
c.TrainingTypeId.Value == trainingTypeId &&
c.IsApproved.Value &&
c.TrainingSchedules.Any(ts => ts.EndDate >= DateTime.Now) &&
!c.Registrations.Any(r => r.EmployeeId == empId));
If you can, then you are just fine.
Note that this is not an exact translation of the sample query, but is functionally equivalent (should produce the same result). The sample query is a good example of badly written query - variable naming, unnecessary multiplicative Join which requires then a Distinct operator (while GroupJoin would do the same w/o the need of Distinct), inconsistent handling of two similar detail criteria (Join for TrainingSchedules and Any for Registrations), overcomplicated criteria for Registrations part etc.
Shortly, don't write such queries. Concentrate on the desired result from the query and use the most logical constructs to express it. Avoid manual joins when you have navigation properties. If you don't have navigation properties, then add them to the model - it's easy one time action which helps a lot when writing queries. For instance, in my translation I assume you have something like this:
class Certificate
{
// Other properties ...
public ICollection<TrainingSchedule> TrainingSchedules { get; set; }
public ICollection<Registration> Registrations { get; set; }
}
class TrainingSchedule
{
// Other properties ...
public Certificate Certificate { get; set; }
}
class Registration
{
// Other properties ...
public Certificate Certificate { get; set; }
}
UPDATE: Here is the same using the query syntax:
var train =
from c in db.Certificates
where c.Year.Value.Year == year &&
c.TrainingTypeId.Value == trainingTypeId &&
c.IsApproved.Value &&
c.TrainingSchedules.Any(ts => ts.EndDate >= DateTime.Now) &&
!c.Registrations.Any(r => r.EmployeeId == empId)
select c;
This Linq statement works perfectly except that I realized that I am required to use the repository and unit of work ...
So I have this Linq query
var query = (from rg in ReportGroups
join rd in ReportDefinitions on rg.ReportGroupID equals rd.ReportGroupID
where rd.ReportGroupID == 5
select rg).Count();
What I am seeing is that
ReportGroups uses GetReportGroups ( for GetAll() )
ReportDefinitions uses GetReportDefinitions (for GetAll() )
So an example is
object responseObject = null;
responseObject = _reportService.GetReportGroups("en-us").ToList();
responseObject = _reportService.GetReportDefinitions("en-us").Where(e => e.ReportGroupID == Convert.ToInt32(id)).ToList();
So you can see that is how I'm currently having to retrieve data.
I am wanting to do a Join, but i'm not sure how I can do this.
I was thinking about multiple calls, many a for loop...
Also I noticed how an existing service is called in a different method with lambda statement
private IEnumerable<SelectListItem> GetActivityList()
{
return _activityService
.GetAllActivities()
.OrderBy(n => n.ActivityName)
.Select(a => new SelectListItem
{
Text = a.ActivityName,
Value = a.Id.ToString(CultureInfo.InvariantCulture)
});
}
I realize that people cannot see all the underlying data abstractions, and I can certainly provide anything asked, I'm just a bit stumped as I used Linqpad to connect to the database and wrote my query, but now I realize that the join etc.. is maybe not so easy with the service layers.
return DbSet.Include(x => x.ReportGroups )
.Include(x => x.ReportDefinitions ).Where(//id condition);
I am relatively new to Entity Framework 6.0 and I have come across a situation where I want to execute a query in my C# app that would be similar to this SQL Query:
select * from periods where id in (select distinct periodid from ratedetails where rateid = 3)
Is it actually possible to execute a query like this in EF or would I need to break it into smaller steps?
Assuming that you have in your Context class:
DbSet<Period> Periods...
DbSet<RateDetail> RateDetails...
You could use some Linq like this:
var distincts = dbContext.RateDetails
.Where(i => i.rateId == 3)
.Select(i => i.PeriodId)
.Distinct();
var result = dbContext.Periods
.Where(i => i.Id)
.Any(j => distincts.Contains(j.Id));
Edit: Depending on your entities, you will probably need a custom Comparer for Distinct(). You can find a tutorial here, and also here
or use some more Linq magic to split the results.
Yes, this can be done but you should really provide a better example for your query. You are already providing a bad starting point there. Lets use this one:
SELECT value1, value2, commonValue
FROM table1
WHERE EXISTS (
SELECT 1
FROM table2
WHERE table1.commonValue = table2.commonValue
// include some more filters here on table2
)
First, its almost always better to use EXISTS instead of IN.
Now to turn this into a Lambda would be something like this, again you provided no objects or object graph so I will just make something up.
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Type1>().Where(x => myContext.DbSet<Type2>().Any(y => y.commonValue == x.commonValue)).Select(x => x);
EDIT - updated after you provided the new sql statement
Using your example objects this would produce the best result. Again, this is more efficient than a Contains which translates to an IN clause.
Sql you really want:
SELECT *
FROM periods
WHERE EXISTS (SELECT 1 FROM ratedetails WHERE rateid = 3 AND periods.id = ratedetails.periodid)
The Lamda statement you are after
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Periods>()
.Where(x => myContext.DbSet<RateDetails>().Any(y => y.periodid == x.id && y.rateid == 3))
.Select(x => x);
Here is a good starting point for learning about lamda's and how to use them.
Lambda Expressions (C# Programming Guide).
this is your second where clause in your query
var priodidList=ratedetails.where(x=>x.rateid ==3).DistinctBy(x=>x.rateid);
now for first part of query
var selected = periods.Where(p => p.id
.Any(a => priodidList.Contains(a.periodid ))
.ToList();
I have an entity framework 3.5 project where I'm using TPH inheritence. Two of my concrete types are in their own DAO class and contain a selector that projects the Entity Class into a DTO. However, both of these concrete classes have similar relations with another table which I use a let statement to identity clearly. My question is, can I somehow refactor this let statement because as I create more inherited concrete classes I feel I'm violating DRY.
StudentDAO.cs:
var myDTO = from x in context.Students
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new StudentDTO { id = x.id, studentname = x.studentname, address = a.address};
ProfessorDAO.cs:
var myDTO = from x in context.Professors
let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
select new ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address};
so is there a way I can refactor the address out of both of these queries?
Short answer: I'm afraid there isn't much you could easily do. The problem is that you need to parameterize the query. The query however needs to be represented as an expression tree (so that it can be translated to SQL or whatever EF uses). Unfortunately, C# don't provide any elegant ways for composing expression trees.
Long answer: Some time ago, I wrote a C# article that explains how to compose queries (using some tricks) - you can take expression tree and splice it into a parameterized query. So, in principle you could use the techniques from the article to create a method that takes the changing parts of the expression tree and then compose the query.
I'll use the types used in LINQ to SQL, because I'm more familiar with it, but I believe the principle should be the same:
IQueryable<R> GetInfo<T>(IQueryable<T> source, // e.g. context.Students
Expression<Func<T, IQueryable<Address>>> getAddr, // x => x.adresses
Expression<Func<T, Address, R>> getRes // (x, a) => new StudentDTO { ... }
return
from x in source.AsExpandable()
let address = (from a in getAddr(x).Expand() where a.active == true
select a).FirstOrDefault()
select getRes(x, a).Expand();
}
// The use would look like this
var res = GetInfo(context.Professors, x => x.addresses, (x, a) => new
ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address });
So a summary is that C# and LINQ do not provide any built-in support that would make it easier. However, with some effort you can write query that is parameterized by some parts of the expression tree. In your case, it makes the code a little shorter and less repetitive, but it makes it a lot more complex (and you also need to use library like the one I referenced that provides AsExpandable and Expand).
In this case I'm afraid the it's not worth the effort. But it would be nice if C# v(Next)^x provided some more elegant support for things like this :-)