Linq- left join on dbset - linq

I have 2 tables:
A {
IDA
propA1
propA2
}
B {
IDA,
IDB,
propB1,
propB2
}
I want to get all records from table A such that IDA is not contained in table B where IDB = "someValue".
The query is using linq.

I assume you're using Linq on C#.
var someIDAs = B.Where(b => b.IDB == "someValue").Select(b => b.IDA);
var result = A.Where(a => !someIDAs.Contains(a.IDA));
Edit: And, as Florian says, that's something different than what is called left join.

Related

How to write SQL translateable linq code that groups by one property and returns distinct list

I want to change code below to be sql translateable because now i get exception.
Basicallly i want list of customers from certain localisation and there could be more than one customer with the same CustomerNumber so i want to take the one that was most recently added.
In other words - distinct list of customers from localisation where "distinct algorithm" works by taking the most recently added customer if there is conflict.
The code below works only if it is client side. I could move Group By and Select after ToListAsync but i want to avoid taking unnecessary data from database (there is include which includes list that is pretty big for every customer).
var someData = await DbContext.Set<Customer>()
.Where(o => o.Metadata.Localisation == localisation)
.Include(nameof(Customer.SomeLongList))
.GroupBy(x => x.CustomerNumber)
.Select(gr => gr.OrderByDescending(x => x.Metadata.DateAdded).FirstOrDefault())
.ToListAsync();
Short answer:
No way. GroupBy has limitation: after grouping only Key and Aggregation result can be selected. And you are trying to select SomeLongList and full entity Customer.
Best answer:
It can be done by the SQL and ROW_NUMBER Window function but without SomeLongList
Workaround:
It is because it is not effective
var groupingQuery =
from c in DbContext.Set<Customer>()
group c by new { c.CustomerNumber } into g
select new
{
g.Key.CustomerNumber,
DateAdded = g.Max(x => x.DateAdded)
};
var query =
from c in DbContext.Set<Customer>().Include(x => x.SomeLongList)
join g in groupingQuery on new { c.CustomerNumber, c.DateAdded } equals
new { g.CustomerNumber, g.DateAdded }
select c;
var result = await query.ToListAsync();

Linq Left Outer Join Two Tables on Two Fields

How do I left outer join two tables on two fields in linq?
I have a sql:
select a.*, b.* from courselist as a
left outer join Summary as b
on a.subject = b.Subject and a.catalog =
b.Catalogno
where a.degree_id = 1
order by a.sequenceNo
Below is my linq query, but there is error underline "join", failed in the call to "Groupjoin". I don't know how to correct that.
var searchResults = (from a in db.courselist
join b in db.Summary on
new { a.subject,a.catalog } equals
new { b.Subject, b.Catalogno } into ab
where a.degree_id == 1
orderby a.degree_sequenceNo
from b in ab.DefaultIfEmpty()
select new
{
Courselist = a,
Summary = b
}
).ToList();
Thanks.
I've checked your code again,
I found it's fault
you just need to specify join parameters name like this:
new { suject = a.subject, catalog = a.catalog } equals
new { suject = b.subject, catalog = b.Catalogno } into ab
It seems you are missing the reference, the query doesn't have an error
try to use this:
using System.Linq;
The main issue when people start using LINQ is that they keep thinking in the SQL way, they design the SQL query first and then translate it to LINQ. You need to learn how to think in the LINQ way and your LINQ query will become neater and simpler. For instance, in your LINQ you don't need joins. You should use Associations/Navigation Properties instead. Check this post for more details.
There should be a relationship between courselist and Summary, in which case, you can access Summary through courselist like this:
var searchResults = (from a in db.courselist
where a.degree_id == 1
orderby a.degree_sequenceNo
select new {
Courselist = a,
Summary = a.Summary
}).ToList();
If there is no relationship between the two, then you should reconsider your design.

Many to Many EF LINQ

I have a database that has the following tables:
dbo.Administrator
dbo.Application
dbo.AdminApplication
dbo.Proficiency
dbo.ProficiencyLevel
Administrators contain 1 to many Applications. Application contains many administrators
Applications contain 1 to many Proficiency(s)
Proficiency contains 1 to many ProficiencyLevels
Using EF Code First, the AdminApplication is not mapped as an entity and this is what is causing me issues. What I want to answer is the following:
"Return all the ProficiencyLevels of the Administrator named "danhickman".
In SQL, the query would look like this:
Select * from dbo.ProficiencyLevel pl
inner join dbo.Proficiency p on p.Id = pl.ProficiencyId
inner join dbo.Application a on a.Id = p.ApplicationId
inner join dbo.AdminApplication aa on aa.ApplicationId = a.Id
inner join dbo.Administrator ad on ad.Id = aa.AdministratorId
where ad.Name = 'danhickman'
I solved this with the following C# code:
public IQueryable<LobGame.Model.ProficiencyLevel> GetAllByAdminName(string administratorName)
{
var context = this.DbContext as LobGameDbContext;
var admin = context.Administrators.Include(i => i.Applications).Include("Applications.Proficiencies").Include("Applications.Proficiencies.ProficiencyLevels").Single(o => o.Name == administratorName);
List<LobGame.Model.ProficiencyLevel> list = new List<ProficiencyLevel>();
foreach (var app in admin.Applications)
{
foreach (var prof in app.Proficiencies)
{
list.AddRange(prof.ProficiencyLevels);
}
}
return list.AsQueryable();
}
It bugs me that I have to foreach and add to a list. I was unable to figure out a way to do this in a single LINQ statement. any thoughts?
Another option using query syntax. This uses SelectMany under the covers.
var queryableList =
from admin in context.Administrators
where admin.Name = administratorName
from app in admin.Applications
from proficiency in app.Proficiencies
from level in proficiency.ProficiencyLevels
select level;
Note: this will be an IQueryable, so you don't need the .ToList().AsQueryable().
return context.Administrators
.Single(o => o.Name == administratorName)
.Applications
.SelectMany(app => app.Proficiencies)
.SelectMany(prof => prof.ProficiencyLevels)
.ToList()
.AsQueryable();
Use SelectMany():
var queryableList =
context.Administrators.Single(o => o.Name.Equals(administratorName))
.SelectMany(adm => adm.Applications.Select(app => app.Proficiencies.SelectMany(prof => prof.ProficiencyLevels))).ToList().AsQueryable();

LINQ queries with many-to-many tables in Entity Data Model

I'm trying to use LINQ to query the following Entity Data Model
based on this db model
I'd like to be able to pull a list of products based on ProductFacets.FacetTypeId.
Normally, I'd use joins and this wouldn't be a problem but I don't quite understand how to query many-to-many tables under the Entity DataModel.
This is an example sql query:
select p.Name, pf.FacetTypeId from Products p
inner join ProductFacets pf on p.ProductId = pf.ProductId
where pf.FacetTypeId in(8, 12)
Presuming EF 4:
var facetIds = new [] { 8, 12 };
var q = from p in Context.Products
where p.FacetTypes.Any(f => facetIds.Contains(f.FacetTypeId))
select p;
In EF (assuming the mapping is done correctly), joins are hardly ever used; navigation properties are used instead.
Your original SQL returns a tuple with repeated Name entries. With LINQ, it's often easier
to "shape" the queries into non-tuple results.
The following should be the same as the SQL, only instead of returning (Name, FacetTypeId) pairs with repeated Names, it will return a type that has a Name and a sequence of FacetTypeIds:
var facetIds = new [] { 8, 12 };
var result = from p in db.Products
select new
{
p.Name,
FacetTypeIds = from pf in p.FacetTypes
where pf.FacetTypeId == 8 || pf.FacetTypeId == 12
select pf.FacetTypeId,
};

How to write linq query based on EF?

Suppose I have three tables:
Person(pid, ...)
PersonAddress(pid, aid,...)
Address(aid, ...)
Then I want to get the person address like sql:
select a.* from address a join PersonAddress pa on a.addressID=pa.addressID
where pa.personID = myPersonID
Use Entity Framework to create Entity model, then want to write a linq equivalent as above sql.
I tried it in following way:
var addresses = this.GetAddress();
var personaddresses = this.GetPersonAddress();
var query = from ad in addresses
from pa in personaddresses
where ((ad.AddressID == pa.AddressID)&&(pa.PersonID==person.personID))
select ad;
but I got error. Or I try to start from:
var result = this.Context.Address;
var result = result.Join .... //how to write linq in this way?
How to write the linq?
This is untested but if you have all of your relationships setup and you create the model (I have used Model as the name for this) from this you should be able to use the following:
var values = this.Model.Address.Select(a => a.PersonAddress.Where(pa => pa.Id == myPersonID));
You almost never use join in LINQ to Entities.
Try:
var q = from p in Context.People
where p.PersonId == personId
from a in p.Addresses // presumes p.Addresses is 1..*
select a;
Assuming you have three entities: Person, PersonAddress and Address, here is a query that should meet your needs (this example assumes an Entity Framework context named context):
var values = context.PersonAddress.Where(pa => pa.Person.PersonId == myPersonId).Select(pa => pa.Address);
However, if the PersonAddress table exists as a pure many-to-many relationship table (i.e. contains only keys), you'd be better off setting up your Entity Framework model in such a way that the intermediate table isn't necessary, which would leave you with the much simpler:
var values = context.Person.Where(p => p.PersonId == myPersonId).Addresses;
Based on the additional feedback
Because you need to include the country table, you should originate your query from the Address table. In that case:
var values = context.Address.Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)
To include the Country table in the result:
var values = context.Address.Include("Country").Where(a => a.PersonAddress.Where(pa => pa.Product.Id == myProductId).Count() > 0)

Resources