I have been really struggling with this for the past couple of days and after exhaustive googling and haven't managed to work out quite how to do it. I am very new to LINQ and C#. Basically, I am trying to join two tables, all of one table, however the column I am trying to join to does contain nulls. Then joining onto that column a subset of another table.
What I would like to happen is that after joining I get all the information from the first table displayed, instead of what I am getting at the moment where only the columns which weren't null as displayed.
I am aware that this is a particually elegant explanation so I will post some simplified puesdo-code:
tblAsset tblLookUps
AssetTag int Domain String
Type int DomainID int
Model int Description String
So information which would be in the tables would resemble:
100, 1, 1 TYPE, 1, PC
101, 1, null TYPE, 2, Monitor
102, 1, 2 MODEL, 1, Old PC
103, 2, null MODEL, 2, New PC
104, 2, null MODEL, 3, Old Monitor
105, 2, 3 MODEL, 4, New Monitor
So what I would like the LINQ query to give me is something like this
AssetTag Type TypeDescription Model ModelDescription
100 1 PC 1 Old PC
101 1 PC null null
102 1 PC 2 New PC
103 2 Monitor null null
104 2 Monitor null null
105 2 Monitor 3 Old Monitor
However at the moment the LINQ returns this:
AssetTag Type TypeDescription Model ModelDescription
100 1 PC 1 Old PC
102 1 PC 2 New PC
105 2 Monitor 3 Old Monitor
So obviously when trying to join if the value is null it gets left out, which of course I understand, however I don't really care if it is null so would quite like to be able to see it!
My current LINQ looks like this:
var AllAssets = from assets in dContext.tblAssets
join type in dContext.tblLookups.Where(x => x.Domain == "ASTYPE")
on assets.Type equals type.DomainID
join model in dContext.tblLookups.Where(x => x.Domain == "MODEL")
on assets.Model equals model.DomainID
select new Asset
{
AssetTag = assets.AssetTag
TypeID = assets.Type
TypeDescription = type.Description
ModelID = assets.Model
ModelDescription = model.Description
}
return AllAssets;
I have tried fiddling around with .DefaultIfEmpty() and several other things but I haven't managed to solve it and feel like I have reached the end of capabilities, any hints or pointers would be excellent!
var q = from a in tblAssets
join l in tblLookUps on a.Model equals l.DomainID
into tblAssettblLookUps
from l in tblAssettblLookUps.DefaultIfEmpty()
select new
{
AssetTag = a.AssetTag,
Type = a.Type,
Model = a.Model
ModelDescription = l.Description
};
Try query like this.
Related
Let’s assume I have three tables in my database:
Authors(PK Id, AuthorName)
Books(PK Id, BookName)
Authors_Books(FK BookId, FK AuthorId)
I need to write a method which passes in a parameter int[] authorsIds and returns all books (Id, BookName, list of authors of this book) if it was written by any author whose Id contains in int[] authorsIds parameter.
I need LINQ query (query method).
I really need a help. Appreciate your help very much.
var int[] authors = { 1, 2, 3 };
var books = from book in books
where book.Authors.Any(x => authors.Contains(x.AuthorId))
select book;
PS: I assume book.Authors is your reference to Authors_Books table (or maybe has a different name).
this answer is based on the exact structure without considering that A obj has B in its structure
var book = from b in books
where book.Id in ( from ab in AuthersBooks
where selectedAuthers.contains(ab.AutherId)
select ab.bookId ) //now we have books that are authered by one of the selected authers
select new { BookId = b.Id, // now we create an anonymos object to contain any info we need
BookName = b.Name,
BookAuthers = (from a in authers
join ab in AuthersBooks
on a.Id == ab.AuthersId
where ab.bookid == b.Id
select a)
this will propably have syntax errors and after correcting you may get an error about multiple threads which already has an answer here
I have two similar queries, the first one:
var activatedSerialNumbers = (from activation in entities.Activations
where !canceledActivationsIds.Contains(activation.Id)
where activation.CustomerId == customerId
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals licenseConfiguration.ActivationId
where licenseConfiguration.ProductId == productId
join activatedSerialNumber in entities.ActivatedSerialNumbers
on activation.Id equals activatedSerialNumber.ActivationId
where deactivatedSams.All(dsn => dsn.ToLower() !=
activatedSerialNumber.Name.ToLower())
select new SamWithLicense
{
Name = activatedSerialNumber.Name,
Features = licenseConfiguration.LicenseFeatures
}).ToList();
The second:
var activationsForSam = (from activation in entities.Activations
where !canceledActivationsIds.Contains(activation.Id)
where activation.CustomerId == customerId
let activatedSerialNumbers = activation.ActivatedSerialNumbers
.Select(sn => sn.Name.ToLower())
where activatedSerialNumbers.Contains(loweredSn)
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals activatedProduct.ActivationId
select new SamWithLicense
{
Name = selectedSerialNumber,
Features = licenseConfiguration.LicenseFeatures
}).ToList();
In some situations I execute them one after another and in most cases it works fine, but somethimes - not. In the result of second query Counter takes from another row:
Visual Studio - Quick watch
SQL Management Studio
I guess it's a matter of a EF cache or smth, but don't know how to fix it properly.
In your first query you are joining the Activation Id (PK) to LicenseConfigurations ActivationId (FK)
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals licenseConfiguration.ActivationId
in your second query, it looks like you are joining on a value defined outside of the query "activatedProduct"
join licenseConfiguration in entities.LicenseConfigurations
on activation.Id equals activatedProduct.ActivationId
I have two tables, one contains entities other entitylog.
MyEntity:
id, lat, lon
A entity has a position in the world.
MyEntityLog:
id, otherid, otherlat, otherlon
Entity with id has interacted with otherid at otherid's latitude and longitude.
For instance, I have the following entities:
1, 4.456, 2.234
2, 3.344, 6.453
3, 6.234, 9.324
(not very accurate, but it serves the purpose).
Now, If entity 1 interact with 2 the result on the log table would look like:
1, 2, 3.344, 6.453
So my question is, how can I for listing entity 1's available interactions NOT include the ones on the log table?
The result of listing entity 1's available interactions should be only be entity 3 as it already has a interaction with 2.
First make a list of ids that interact with entity 1:
var id1 = 1;
var excluded = from l in db.EntityLogs
where l.id == id1
select l.otherid;
then find the entries not having an id in this list or equal to id1:
var logs= from l in db.EntityLogs
where !excluded.Contains(l.id) && l.id != id1
select l;
Note that linq will defer the execution of excluded and incorporate it in the execution of logs.
Not sure if I understand your question, I guess I need more details, but if you want to list the entities that have no entry in log table, one solution will be something like this, assuming myEntities is the collection of MyEntity and myEntityLogs is the collection of MyEntityLog
var firstList = myEntities.Join(myEntityLogs, a => a.Id, b => b.Id, (a, b) => a).Distinct();
var secondList = myEntities.Join(myEntityLogs, a => a.Id, b => b.OtherId, (a, b) => a).Distinct();
var result = myEntities.Except(firstList.Concat(secondList)).ToList();
Here a sample dataset:
OrderProduct is a table that contains the productIds that were part of a given order.
Note: OrderProduct is a database table and I am using EF.
OrderId, ProductId
1, 1
2, 2
3, 4
3, 5
4, 5
4, 2
5, 2
5, 3
What I want to be able to do is find an order that contains only the productIds that I am searching for. So if my input was productIds 2,3, then I should get back OrderId 5.
I know how I can group data, but I am unsure of how to perform the select on the group.
Here is what I have:
var q = from op in OrderProduct
group op by op.OrderId into orderGroup
select orderGroup;
Not sure how to proceed from here
IEnumerable<int> products = new List<int> {2, 3};
IEnumerable<OrderProduct> orderProducts = new List<OrderProduct>
{
new OrderProduct(1, 1),
new OrderProduct(2, 2),
new OrderProduct(3, 4),
new OrderProduct(3, 5),
new OrderProduct(4, 5),
new OrderProduct(4, 2),
new OrderProduct(5, 2),
new OrderProduct(5, 3),
};
var orders =
(from op in orderProducts
group op by op.OrderId into orderGroup
//magic goes there
where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
select orderGroup);
//outputs 5
orders.Select(x => x.Key).ToList().ForEach(Console.WriteLine);
Or you can have another version as pointed in another answer, just replace
where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
on
where products.All(pid => orderGroup.Any(op => op.ProductId == pid))
second one will have ~ 15% better performance (I've checked that)
Edit
According to the last requirement change, that you need orders that contain not all productIds you are searching, but exactly those and only those productIds, I wrote an updated version:
var orders =
(from op in orderProducts
group op by op.OrderId into orderGroup
//this line was added
where orderGroup.Count() == products.Count()
where !products.Except(orderGroup.Select(x => x.ProductId)).Any()
select orderGroup);
So the only thing you'll need is to add a precondition ensuring that collections contains the same amount of elements, it will work for both previous queries, and as a bonus I suggest 3rd version of the most important where condition:
where orderGroup.Select(x => x.ProductId).Intersect(products).Count() == orderGroup.Count()
At first glance, I'd try something like this:
var prodIds = new[] {2, 3};
from o in context.Orders
where prodIds.All(pid => o.OrderProducts.Any(op => op.ProductId == pid))
select o
In plain language: "get the orders that have a product with every ID in the given list."
Update
Since it appears you are using LINQ to SQL rather than LINQ to Entities, here's another approach:
var q = context.Orders;
foreach(var pid in prodIds)
{
q = q.Where(o => o.OrderProducts.Any(op => op.ProductId == pid));
}
Rather than using a single LINQ statement, you essentially build the query piecemeal.
Thanks to StriplingWarrior's answer I managed to figure this out. Not sure if this is the best way to do this, but it works.
List<int> prodIds = new List<int>{2,3};
var q = from o in Orders
//get all orderproducts that contain products in the ProdId list
where o.OrderProducts.All(op => prodIds.Contains(op.ProductId))
//now group the OrderProducts by the Orders
select from op in o.OrderProducts
group op by op.OrderId into opGroup
//select only those groups that have the same count as the prodId list
where opGroup.Count() == prodIds.Count()
select opGroup;
//get rid of any groups that may be empty
q = q.Where(fi => fi.Count()> 0);
(I am using LinqPad, which is why the query looks a little funky - no context, etc)
I have a T-SQL 2005 query which returns:
pid propertyid displayname value
----------- ----------- --------------- ---------------
14270790 74 Low Price 1.3614
14270790 75 High Price 0
14270791 74 Low Price 1.3525
14270791 75 High Price 0
14270792 74 Low Price 1.353
14270792 75 High Price 0
14270793 74 Low Price 1.3625
14270793 75 High Price 0
14270794 74 Low Price 1.3524
14270794 75 High Price 0
What I would like to do is essentially pivot on the displayname field, hopefully producing:
pid Low Price High Price
14270790 1.3614 0
14270791 1.3525 0
14270792 1.353 0
14270793 1.3625 0
14270794 1.3524 0
(Not sure how the propertyid field would be output, so I left it out (was hoping it would simply sit alongside the Low Price and High Price fields, to indicate their IDs, but I don't think that will work.)
The problem is that the content of the original displayname field is dynamic - it is produced from a join with a PropertyName' table, so the number of pivoted columns is variable. It could therefore containHigh Price,Low Price,OpenandClose`, depending on what the join with that table returns.
It is, of course, relatively easy (regardless of the trouble I'm having writing the initial query!) to produce this pivot in a fixed query or stored proc. However, is it possible to get LINQ to generate a SQL query which would name each column to be produced rather than having to write a dynamic (probably in a stored proc) query which lists out the column names?
Thanks,
Matt.
I'll give you a sample with a different data (that I needed). You can adapt that to your need. Note only two linq queries are used, most of the other fluff is to convert a list into a datatable.
var data = new[] {
new{Student=1, Subject="English", Marks=40},
new{Student=1, Subject="Maths", Marks=50},
new{Student=1, Subject="Science", Marks=60},
new{Student=1, Subject="Physics", Marks=70},
new{Student=1, Subject="Chemistry", Marks=80},
new{Student=1, Subject="Biology", Marks=90},
new{Student=2, Subject="English", Marks=4},
new{Student=2, Subject="Maths", Marks=5},
new{Student=2, Subject="Science", Marks=6},
new{Student=2, Subject="Physics", Marks=7},
new{Student=2, Subject="Chemistry", Marks=8},
new{Student=2, Subject="Biology", Marks=9}
};
/*Here the pivot column is the subject and the static column is student
group the data against the static column(s)*/
var groups = from d in data
group d by d.Student into grp
select new
{
StudentId = grp.Key,
Marks = grp.Select(d2 => new { d2.Subject, d2.Marks }).ToArray()
};
/*get all possible subjects into a separate group*/
var subjects = (from d in data
select d.Subject).Distinct();
DataTable dt = new DataTable();
/*for static cols*/
dt.Columns.Add("STUDENT_ID");
/*for dynamic cols*/
foreach (var subject in subjects)
{
dt.Columns.Add(subject.ToString());
}
/*pivot the data into a new datatable*/
foreach (var g in groups)
{
DataRow dr = dt.NewRow();
dr["STUDENT_ID"] = g.StudentId;
foreach (var mark in g.Marks)
{
dr[mark.Subject] = mark.Marks;
}
dt.Rows.Add(dr);
}
This is the closest I could get, but it's not LINQ...
create table #t
(
pointid [int],
doublevalue [float],
title [nvarchar](50)
)
insert into #t
select
distinct top 100
v.pointid, v.doublevalue, p.title
from [property] p
inner join pointvalue v on p.propertyid = v.propertyid
inner join point pt on v.pointid = pt.pointid
where v.pointid in (select top 5 p.pointid from point p where p.instanceid = 36132)
declare #fields nvarchar(250)
set #fields = (select STUFF((SELECT N',[' + title + ']' FROM [property] FOR XML PATH('')), 1, 1, N''))
--select #fields
declare #sql nvarchar(500)
set #sql = 'select * from #t
pivot
(
sum(doublevalue)
for [title] in ('+#fields+')
) as alias'
--select #sql
exec (#sql)
drop table #t
The kicker is that I'm simply asking for every entry in the Property table, meaning there's a lot of columns, in the resulting pivot, which have NULL values.
the code I think is like this:
var list = from table in Property
group table by table.pid into g
select new
{
pid = g.key,
LowPrice = g.Where(w => w.pid== g.key && w.priceType == "low").Select(s => s.value).FirstorDefault(),
HighPrice = g.Where(w => w.pid== g.key && w.priceType == "high").Select(s => s.value).FirstorDefault(),
};
Hope it can help you and have a nice day.