How can I LINQ outer join two collections? - c#

I have the following query:
var rowData = companies.Select(
t => new CompanyDetail
{
CompanyID = t.Title,
Subjects = subjects.Count ( u => t.RowKey == "0000" + u.PartitionKey)
}).ToList();
public class CompanyDetail
{
[DisplayName("Company")]
public string CompanyID { get; set; }
[DisplayName("Subjects")]
public Int32 Subjects { get; set; }
}
The query output looks like this:
CompanyID Subjects
1 2
2 4
3 1
However I have a problem if the company has no subjects. I would like to see:
CompanyID Subjects
1 2
2 4
3 1
4 0
Is there a way that I can convert this LINQ query into an outer join so it always reports every company and then gives a count of how many subjects are connected through the row and partitionkey connector?

It's hard to answer this without knowing what LINQ provider you are using - your query would work as you expect in LINQ to Objects, for example.
Perhaps it would be worth a try to use an explicit GroupJoin to convince the provider to give you empty groups:
var rowData = from company in companies
join subject in subjects
on company.RowKey equals "0000" + subject.PartitionKey
into companySubjectGroup
select new
{
CompanyID = company.Title,
Subjects = companySubjectGroup.Count()
};

Related

LINQ join query with relational entities

I am using ef-core 2.1, I have the following simplified entities where one Account maps to zero or more Attribute objects:
public class Account
{
public int Id { get; set; }
public int LongId { get; set; }
public List<Attribute> Attributes { get; set; }
}
public class Attribute
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public Account Account { get; set; }
}
I have an initial collection of strings that resemble an Attribute.Value for a given fixed Attribute.Name, I want to find a second associated Attribute object from the same parent Account and obtain its Attribute.Value.
I want to left join the ef entities against the initial string collection so I can easily infer:
Whether no corresponding Account exists or an Account exists without the related Attribute objects (both equate to the same use case).
If an Account exists and it contains all the required Attribute objects, I want to obtain the value of the secondary Attribute.
Without LINQ and ef, I run the following SQL query which ignores the parent Account and produces the result set I want:
CREATE TABLE #Temp
(
id nvarchar(20) not null
);
INSERT INTO #Temp (id) VALUES ('cejawq'), ('issokq'), ('cqlpjq'), ('mbgzvi'), ('wqwlff'), ('iedifh');
SELECT t.[Id], attr2.[Value]
FROM #Temp t
LEFT OUTER JOIN [dbo].[Attributes] attr1
ON t.[Id]=attr1.[Value]
AND attr1.[Name]='uid'
LEFT OUTER JOIN [dbo].[Attributes] attr2
ON attr1.[AccountId]=attr2.[AccountId]
AND attr2.[Name]='objType';
I get the following result set:
id|objType
-----------
cejawq|ext
issokq|ext
cqlpjq|int
mbgzvi|int
wqwlff|ext
iedifh|null
I am struggling with mapping this to efficient LINQ such that the SQL generated produces the result set remotely and ships back data that I can project to an equivalent anonymous type. Do I need to care about the parent objects in the LINQ case? I don't have an index on the Attribute.Value column.
The Attributes table contains the following data:
Id|Name |Value |AccountId
1 |uid |cejawq|1
2 |objType|ext |1
3 |uid |issokq|2
4 |objType|ext |2
5 |uid |cqlpjq|3
6 |objType|int |3
7 |uid |mbgzvi|4
8 |objType|int |4
9 |uid |wqwlff|5
10|objType|ext |5
Since the EF Core does not support joins with in memory sequences (yet), you can split the query in two parts - one which takes the data server side ([Attributes to [Attributes join) using in memory collection as filter (SQL IN through LINQ Contains method), and second which performs left join in memory with the result of the db query:
DbContext db = ...;
var uids = new [] { "cejawq", "issokq", "cqlpjq", "mbgzvi", "wqwlff", "iedifh" };
var dbQuery =
from attr1 in db.Set<Attribute>()
where attr1.Name == "uid" && uids.Contains(attr1.Value)
join attr2 in db.Set<Attribute>()
on new { AccountId = attr1.Account.Id, Name = "objType" }
equals new { AccountId = attr2.Account.Id, attr2.Name }
into attr2Group from attr2 in attr2Group.DefaultIfEmpty() // left outer join
select new { uid = attr1.Value, objType = attr2.Value };
var query =
from uid in uids
join dbResult in dbQuery on uid equals dbResult.uid
into dbResultGroup from dbResult in dbResultGroup.DefaultIfEmpty() // left outer join
select new { uid, dbResult?.objType };
var result = query.ToList();
It translates to a single db query like this:
SELECT [attr1].[Value] AS [uid], [attr2].[Value] AS [objType]
FROM [Attributes] AS [attr1]
LEFT JOIN [Attributes] AS [attr2] ON ([attr1].[AccountId] = [attr2].[AccountId]) AND (N'objType' = [attr2].[Name])
WHERE ([attr1].[Name] = N'uid') AND [attr1].[Value] IN (N'cejawq', N'issokq', N'cqlpjq', N'mbgzvi', N'wqwlff', N'iedifh')

Creating a gridview or listview from different sql tables in asp.net

What i aim to achive with this is to have a grid or listview where the colums get data from different sql tabels.
Here are the different tabels
TradeItemIdentification
id GTIN
TradeItemDescriptionInformation
id brandName tradeItemFunctionalName
ClassificationCategory
id additionalClassificationCategoryCode
I don know if it is possible to show a grid with all of these attributes except the id's. What is the smartest way to do achive this, if possible?
They are all linked to this table. With these "many ot many" tabels.
TradeItemBasic
id
TradeItemIdentificationOnTradeItem
tradeItemId identificationId
TradeItemDescriptionInformationsOnTradeItem
tradeItemId descriptionId
And the same for the last table. So they have a connection.
Here is some joins of the tabels that i have made so far.
public List<string> GetAllProductsInfo()
{
var gtins = (from gtinss in _db.TradeItemIdentificationOnTradeItems
join gtin in _db.TradeItemIdentifications on gtinss.tradeItemIdentificationId equals gtin.id
select gtin.gtin);
var brandNames = (from descriptions in _db.TradeItemDescriptionInformationsOnTradeItems
join description in _db.TradeItemDescriptionInformations on descriptions.tradeItemDescriptionInformationId equals description.id
select description.brandName);
var article = (from articleNumbers in _db.ClassificationCategoryOnGDSNTradeItemClassifications
join articleNumber in _db.ClassificationCategories on articleNumbers.gDSNTradeItemClassificationId equals articleNumber.id
select articleNumber.additionalClassificationCategoryCode);
var allInfo = gtins.Concat(brandNames).Concat(article).ToList();
return allInfo;
}
This is what ive got so far, i am no able to get all of the items that i wanted. But when i do it like this the results is not divided in to sections, but instead it is all just gets put out as one long list.
Just join the different tables and select the properties you want to display.
There are two different use cases. If you want to display all lines where some data is missing then use left joins. Else use inner joins (just replace the left join). That reduce your resultset.
Here is an example how to join your tables:
SELECT tib.id, tii.GTIN, tidi.brandName, tidi.tradeItemFunctionalName
FROM TradeItemBasic AS tib
LEFT JOIN TradeItemIdentificationOnTradeItem AS tii2ti ON tib.id = tii2ti.tradeItemId
LEFT JOIN TradeItemIdentification AS tii ON tii.id = tii2ti.identificationId
LEFT JOIN TradeItemDescriptionInformationsOnTradeItem AS tidi2ti ON tib.id = tidi2ti.tradeItemId
LEFT JOIN TradeItemDescriptionInformation AS tidi ON tidi.id = tidi2ti.descriptionId
For the ClassificationCategory you don't post a mapping table, so i remove the selected property.
Hope that helps.
I solved it.
I Made a new C# class. Called ProductInfo that looks like this
public class ProductInfo
{
public ProductInfo()
{
}
public List<string> Gtin { get; set; }
public List<string> BrandName { get; set; }
public List<string> ArticleNr { get; set; }
}
this class takes three types of lists.
And here is the method for getting the specific information from the different classes.
public ProductInfo GetAllProductsInfo()
{
var gtins = (from gtinss in _db.TradeItemIdentificationOnTradeItems
join gtin in _db.TradeItemIdentifications on gtinss.tradeItemIdentificationId equals gtin.id
select gtin.gtin).ToList();
var brandNames = (from descriptions in _db.TradeItemDescriptionInformationsOnTradeItems
join description in _db.TradeItemDescriptionInformations on descriptions.tradeItemDescriptionInformationId equals description.id
select description.brandName).ToList();
var article = (from articleNumbers in _db.ClassificationCategoryOnGDSNTradeItemClassifications
join articleNumber in _db.ClassificationCategories on articleNumbers.gDSNTradeItemClassificationId equals articleNumber.id
select articleNumber.additionalClassificationCategoryCode).ToList();
ProductInfo pr = new ProductInfo { Gtin = gtins, BrandName = brandNames, ArticleNr = article };
return pr;
}
with this method i get three lists from the tabels that i wanted. And after i made the joins i just create a new ProductInfo and add the results to the right lists in the new class.

linq query for returning single row

For any transaction happening a row is inserted into my table.
i have table(Transactions)like following
ID----------Amount
1------------150.00
2----------- 246.00
3----------- 100.00
4----------- 201.00
If the above transaction is happening with multiple tender types(cash,creditcard etc),for each tender type a row is inserted into another table.
This new table(Tender) looks like following
ID---- TenderType----- TransactionId
1----------- 10 ---- 1
2------------ 20---- 2
3-------------10-----2
The above tells that,the 1st Transaction(from Transactions table,amount 150.00) happened with only one tender type(10).So one one row in Tender table.
The 2nd transaction(amount 246.00) happened with two tender types(10 and 20).So two rows in Tender table.
Now i want the output as
Amount------ TenderType
150.00 ------ 10
246.66-------10/20
Now i am writing a linq query in c# and it is returning two rows if there are multiple rows in Tender table for each Amount in Transaction table.
My query look like following:
(from T in context.Transactions
join TT in context.TenderType on T.ID equals TT.TransactionId
select new myModel
{
Amount = T.Amount,
TenderType = TT.TenderType
}).ToList();
For this query my output looks like this:
Amount------ TenderType
150.00 ------ 10
246.66-------10
246.66-------20
But my output should be like this :
Amount------ TenderType
150.00 ------ 10
246.66-------10/20
How to modify my query to achieve this ?
Thanks in advance.
It sounds like you can have a one to many association from Transaction to Tender.
public class Transaction {
public int ID { get; set; }
public double Amount { get; set; }
public virtual ICollection<Tender> Tenders { get; set; }
}
from T in context.Transactions
select new {
Amount = T.Amount,
TenderTypes = T.Tenders.Select(t => t.TenderType)
}
Try this
(from T in context.Transactions
select new myModel
{
Amount = T.Amount,
TenderType = context.TenderType.Where(x=>x.TransactionId==T.ID).Select(x=>x.TenderType).FirstOrDefault()
}).ToList();
Try this:-
var result= (from T in context.Transactions
join TT in context.TenderType
on T.ID equals TT.TranscationID into g
select new
{
Amount = t.Amount,
TenderType = String.Join("/",g.Select(x => x.TenderTypeID))
}).Where(x => !String.IsNullOrEmpty(x.TenderType));

EF Sum between 3 tables

Say we got a Database design like this.
Customer
Id Name
1 John
2 Jack
Order
Id CustomerId
1 1
2 1
3 2
OrderLine
Id OrderId ProductId Quantity
1 1 1 10
2 1 2 20
3 2 1 30
4 3 1 10
How would I create an entity framework query to calculate the total Quantity a given Customer has ordered of a given Product?
Input => CustomerId = 1 & ProductId = 1
Output => 40
This is what I got so far, through its not complete and still missing the Sum.
var db = new ShopTestEntities();
var orders = db.Orders;
var details = db.OrderDetails;
var query = orders.GroupJoin(details,
order => order.CustomerId,
detail => detail.ProductId,
(order, orderGroup) => new
{
CustomerID = order.CustomerId,
OrderCount = orderGroup.Count()
});
I find it's easier to use the special Linq syntax as opposed to the extension method style when I'm doing joins and groupings, so I hope you don't mind if I write it in that style.
This is the first approach that comes to mind for me:
int customerId = 1;
int productId = 1;
var query = from orderLine in db.OrderLines
join order in db.Orders on orderLine.OrderId equals order.Id
where order.CustomerId == customerId && orderLine.ProductId == productId
group orderLine by new { order.CustomerId, orderLine.ProductId } into grouped
select grouped.Sum(g => g.Quantity);
// The result will be null if there are no entries for the given product/customer.
int? quantitySum = query.SingleOrDefault();
I can't check what kind of SQL this will generate at the moment, but I think it should be something pretty reasonable. I did check that it gave the right result when using Linq To Objects.

LINQ to SQL, get most recent records for given ID

intHi,
Pretty new to LINQ.
I have a Ratings table.
A User adds a set of Ratings for an Entity
There can be more than one Rating set for an Entity per User.
For example, let's say the Entity is a car. The car is rated on Appearance and Performance. And a User can rate the given car's appearance and performance more than once. So my table looks something like this (the Rating field is not an Identity column; it is an int on a scale of 1 - 10):
ReviewID UserID EntityID CatID Rating Body DateSubmitted
1 3 6 1 7 "drives great" 8/01/2010 02:36:28 PM
2 3 6 2 8 "looks great" 8/01/2010 02:36:28 PM
3 3 6 1 2 "broke down" 8/18/2010 11:39:58 PM
4 3 6 2 1 "paint flaked off" 8/18/2010 11:39:58 PM
Now, I have a helper method where I supply the UserID and the EntityID and I want to return the most recent set of Ratings (into a ViewModel that includes the Rating Category).
public static IQueryable<RatingViewModel> GetRatingViewModel(int EntityID, int UserID)
{
DB _db = new DB();
var a =
from rating in _db.Ratings
join ratingCat in _db.RatingCategories
on rating.RatingCategoryID equals ratingCat.RatingCategoryID
where rating.UserID == UserID
&& rating.EntityID == EntityID
select new RatingViewModel
{
Rater = rating.User,
RaterRating = rating,
RatingCategory = ratingCat
};
return a;
}
What kind of "where" or "group by" or "order by" do I need to add to ONLY grab the most recent set of Ratings for the given UserID and EntityID?
Thanks!
Consider ordering by the DateSubmitted on the return of the method, and then taking the number of entries that you'd like.
var a = from rating in _db.Ratings
join ratingCat in _db.RatingCategories
on rating.RatingCategoryID equals ratingCat.RatingCategoryID
where rating.UserID == UserID
&& rating.EntityID == EntityID
orderby rating.DateSubmitted descending
select new RatingViewModel
{
Rater = rating.User,
RaterRating = rating,
RatingCategory = ratingCat
}
.Take(10);
.OrderByDescending(a => a.DateSubmitted).FirstOrDefault()

Categories