LINQ EF QUERY (view difference from condition & dynamic) - linq

I need to make a query to filter records, when get distinct records, get these records information by difference conditions. Also I need these to be dynamic(quantity filter in first select)
Let me show you an example:
I have 2 tables:
tblCustomers:
id customerName
1 John
2 Philip
3 Steve
tblOrders
id customerId ordId payment
1 1 100 True
2 1 101 True
3 1 102 False
4 2 101 True
5 2 102 True
6 2 103 False
7 3 101 True
My condition is:
where (orderId = 101 and orderId = 102)
but get all records of this customer that payment = true I mean my condition is different from what I need to see.
I want to receive all records with payment=True without care of orderId
I must get:
john 100
john 101
Philip 101
Philip 102
Clearing: I need two step - first filter customer who has orderId=101&102, in second step i want to show these selected customers' orderId which payment is true. so for example in first step i get john(who has order id =101&102) then show john 100 - john 101 (which payment istrue). consider tblorder.id=1 isn't in first query but I must show in final result.
#Raphael direct me to better expression:I want to see all payment true order for the customers that have orders (101 & 102). but orderids may be more than 2 (thanks #Raphael).
2nd problem is: it must be dynamic. Sometimes I have more than 10 orderId that must be checked - sometimes less. I mean my query must be flexible.
In SQL Server select command, I can prepare a string variable and use but in linq I can't do it.

From what I understood from your post and the comments, you need all customers, where the orderId is 101 or 102 and the payment is true.
You need the where clause with the orderIds to be dynamic so you can change the Ids to be checked against outside of the query.
List<int> IDList = new List<int>();
IDList.Add(101);
IDList.Add(102);
IDList.Add(110);
//...
var result = from cust in tblCustomers
join order in tblOrders on cust.id equals order.customerId
where IDList.Contains(order.ordId) && order.payment == true
select new {
Name = cust.customerName
OrderId = order.ordId
payment = order.payment
//...
}
With this you can store all orderIds which need to be checked against in a list, which in turn you can edit from your code.
EDIT
I really haven't found a clean solution to your problem, so I took a detour, which isn't very clean but should work. In my example I created 2 classes, Customer & Order and filled it with your data from above. Then I took my first query and attached a groupBy to it and a where-clause comparing the length of the grouping with the length of the list
var result = (from cust in Customers
join order in Orders on cust.Id equals order.customerId
where IDList.Contains(order.orderId) &&
order.payment == true
select new {
Name = cust.Name,
OrderId = order.orderId,
Payment = order.payment
//...
}).GroupBy (r => r.Name)
.Where (r => r.Count() == IDList.Count());
Output:
Name OrderId Payment
Philip 101 True
Philip 102 True
If you want/need it, I can provide you with the whole Linqpad query, so you can see my whole code and what I have done. Speaking of Linqpad: ignore the result.Dump() line. It won't work on visual Studio.

void Main()
{
List<Customer> customers = new List<Customer>
{
new Customer { Id = 1, Name = "John" },
new Customer { Id = 2, Name = "Philip" },
new Customer { Id = 3, Name = "Steve" }
};
List<Order> orders = new List<Order>
{
new Order { Id = 1, CustomerId = 1, OrderId = 100, Payment = true },
new Order { Id = 2, CustomerId = 1, OrderId = 101, Payment = true },
new Order { Id = 3, CustomerId = 1, OrderId = 102, Payment = false },
new Order { Id = 4, CustomerId = 2, OrderId = 101, Payment = true },
new Order { Id = 5, CustomerId = 2, OrderId = 102, Payment = true },
new Order { Id = 6, CustomerId = 2, OrderId = 103, Payment = false },
new Order { Id = 7, CustomerId = 3, OrderId = 101, Payment = true }
};
List<int> orderIds = new List<int> { 101, 102 };
var customersWithRelevantOrders =
from ord in orders
group ord by ord.CustomerId into customerOrders
where orderIds.All (
i => customerOrders.Select (co => co.OrderId).Contains(i))
select customerOrders.Key;
var paymentTrueOrdersForTheseCustomers =
from ord in orders
join cust in customers on ord.CustomerId equals cust.Id
where ord.Payment
where customersWithRelevantOrders.Contains(cust.Id)
select new
{
Name = cust.Name,
OrderId = ord.OrderId
};
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public int OrderId { get; set; }
public bool Payment { get; set; }
}

Related

Issue On The Linq Query

I am trying to do a very simple task with Linq and already stuck. It's really horrible but I guess, it's better to know using this post. I've two tables. One is Products and another is Ratings. Here is the demo script:
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) PRIMARY KEY,
[ProductName] [varchar](40) NULL,
[Price] [float] NULL,
[Details] [varchar](max) NULL,
[CategoryID] [int] NULL
)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (1, N'Denim', 1200, NULL, 1)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (2, N'Denim 2', 220, NULL, 1)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (3, N'Pringles', 240, NULL, 2)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (4, N'Pringles 2', 260, NULL, 2)
INSERT [dbo].[Products] ([ProductID], [ProductName], [Price], [Details], [CategoryID]) VALUES (5, N'Pringles 3', 240, NULL, 2)
CREATE TABLE [dbo].[Ratings](
[AutoId] [int] IDENTITY(1,1) PRIMARY KEY,
[ProductId] [int] NOT NULL,
[UserRating] [float] NOT NULL
)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (4, 2, 1.5)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (5, 4, 2.5)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (6, 1, 5)
INSERT [dbo].[Ratings] ([AutoId], [ProductId], [UserRating]) VALUES (7, 2, 2.5)
And the output should be the following:
ProductId - ProductName - User Rating
1 - Denim - 5
2 - Denim 2 - 2
3 - Pringles - 0
4 - Pringles 2 - 2.5
5 - Pringles 3 - 0
This is the rating system of a project and I am trying to get the above output using Linq. With the following Sql, I got the result:
SELECT m.ProductId, m.ProductName, ISNULL(SUM(k.UserRating) / COUNT(k.ProductId), 0) AS 'User Rating' FROM Products m
LEFT JOIN Ratings k ON k.ProductId = m.ProductID
GROUP BY m.ProductID, m.ProductName
Unfortunately, with the following Linq, I get only the rating details that exist in the Ratings table:
var con = (from c in db.Ratings
join d in db.Products on c.ProductId equals d.ProductID into ps
from rt in ps.DefaultIfEmpty()
group new { c, ps } by new { rt.ProductID, rt.ProductName } into g
select new ProductRating
{
ProductId = g.Key.ProductID,
ProductName = g.Key.ProductName,
Total = g.Sum(c => c.c.UserRating) / g.Count()
}).ToList();
Note: My goal is to get the rating details (sum of a product's rating) that don't exist in the Ratings table (Should return '0' if no data) along with the ratings details that exist.
Update 1 - Class ProductRating:
public class ProductRating {
public int ProductId { get; set; }
public string ProductName { get; set; }
public double Total { get; set; } //Property for the rating
}
Update 2 - Class Products and Ratings:
public partial class Products
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public Nullable<double> Price { get; set; }
public string Details { get; set; }
public Nullable<int> CategoryID { get; set; }
public ICollection<Ratings> Rating { get; set; }
}
public partial class Ratings
{
public int AutoId { get; set; }
public int ProductId { get; set; }
public double UserRating { get; set; }
}
Used the following the Linq query and got this error - The specified type member 'Rating' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported:
var con = db.Products.Select(c => new ProductRating
{
ProductId = c.ProductID,
ProductName = c.ProductName,
Total = c.Rating.Average(d => (double?)d.UserRating) ?? 0
}).ToList();
Update 3: Now getting this error - error 2016: The value specified for the condition is not compatible with the type of the member and this is how I tried for the navigation property:
Note: I've modified Andrés Robinet's query and it worked though using the Join.
from c in db.Ratings
join d in db.Products on c.ProductId equals d.ProductID into ps
from rt in ps.DefaultIfEmpty()
is the LINQ equivalent of the SQL Ratings LEFT OUTER JOIN Products, i.e. exactly the opposite of your SQL query.
While the LINQ query can be modified to match the SQL query, it doesn't make sense because EF provides a much better way which totally eliminates the need of using joins in the LINQ query - the so called navigation properties (see Don’t use Linq’s Join. Navigate!).
Normally the Product class would have a collection navigation property
public ICollection<Rating> Ratings { get; set; }
representing the one to many relationship between Product and Rating, and the equivalent LINQ query using it would be simply:
var result = db.Products
.Select(p => new ProductRating
{
ProductId = p.ProductId,
ProductName = p.ProductName,
Total = p.Ratings.Average(r => (double?)r.UserRating) ?? 0
}).ToList();
Update: In case you haven't set up correctly the relationship in the entity model, you could use the manual join equivalent of the above query by replacing the navigation property accessor with group join:
var result = (
from p in db.Products
join r in db.Ratings on p.ProductId equals r.ProductId into ratings
select new ProductRating
{
ProductId = p.ProductId,
ProductName = p.ProductName,
Total = ratings.Average(r => (double?)r.UserRating) ?? 0
}).ToList();
You have to start with the Product. Also, the order of your joins in your Linq expression is the opposite than in your SQL. And, the group ... by ... needs just rt.UserRating as the value selector
var con = (from d in db.Products
join c in db.Ratings on d.ProductId equals c.ProductId into ps
from rt in ps.DefaultIfEmpty()
group new { rt.UserRating } by new { d.ProductId, d.ProductName } into g
select new ProductRating
{
ProductId = g.Key.ProductId,
ProductName = g.Key.ProductName,
Total = g.Sum(r => r.UserRating) / g.Count()
})
.ToList();
Also, this would break if g.Count() is zero. But you have a few choices anyways:
Add more complexity to the query and pray for EF to be able to translate it into SQL
select new ProductRating
{
ProductName = g.Key.ProductName,
Total = g.Count() > 0 ? (g.Sum(r => r.UserRating) / g.Count()) : 0
}
Redefine ProductRating or use a helper DTO, so that it takes Sum and Count properties (calculate the average on-demand, let's say, you can still have a Total property)
select new ProductRating
{
ProductName = g.Key.ProductName,
SumRating = g.Sum(r => r.UserRating),
CountRating = g.Count()
}
Your LINQ statement has an inner join, whereas your SQL has a left join.

Linq to Sql Group By without count

I have a linq to sql and have been researching how to use linq to sql to group your results. I only see samples with count and sum in them. My model is that each customer order has a variety of notes and could have multiple notes. Right now it's listing all the customer orders and multiple times if it has multiple notes.
How do I use group by in Linq to Sql without the sums/counts aggregate
I have tried:
public IQueryable<object> getAllamcase()
{
try
{
var q = (from c in _context.Customer
join am in _context.table2 on c.id equals am.id
join ampn in _context.table3 on am.id equals ampn.id
join ay in _context.tabl4 on am.id equals ay.id
join oim in _context.table5 on am.id equals oim.id
group c.FileNum by new
{
FileNum = c.order,
assignmentdt = am.Assignment_DT,
oimname = oim.FullName,
notes = ampn.ProgressNotes,
years = ay.AMYear
}).AsQueryable();
return q;
}
catch (Exception ex)
{
_logger.LogError("Could not get......", ex);
return null;
}
}
My results are look as multiple jsons
Customer notes
1 notes 1
1 notes 2
1 notes 3
2 notes 1
2 notes 2
I just want it to return in one json like
Customer notes
1 notes 1
notes 2
notes 3
2 notes 1
notes 2
Your question is unclear, but as I #GertArnold stated, if you want to load notes of customers you should use Navigation properties. Also, please look at naming conventions. Your code will be much cleaner if you name variables, etc. correctly. But according to your question header, I can suggest you following. Imagine that you have Note class:
public class Note
{
public int CustomerId { get; set; }
public string NoteName { get; set; }
}
And you have list of Notes as following:
List<Note> notes = new List<Note>
{
new Note { CustomerId = 1, NoteName = "note 1" },
new Note { CustomerId = 1, NoteName = "note 2" },
new Note { CustomerId = 1, NoteName = "note 3" },
new Note { CustomerId = 1, NoteName = "note 4" },
new Note { CustomerId = 2, NoteName = "note 1" },
new Note { CustomerId = 2, NoteName = "note 2" },
new Note { CustomerId = 3, NoteName = "note 1" },
};
If you want to get CustomerId-s and related notes from this list you can easyli achieve it by grouping them:
var result = notes
.GroupBy(m => m.CustomerId)
.Select(g => new
{
CustomerId = g.Key,
Notes = g.Select(m => m.NoteName).ToList()
});
The result will be:
CustomerId || NoteName
1 note 1
note 2
note 3
note 4
2 note 1
note 2
3 note 1
I hope, it will help you.

Comparing two lists with multiple conditions

I have two different lists of same type. I wanted to compare both lists and need to get the values which are not matched.
List of class:
public class pre
{
public int id {get; set;}
public datetime date {get; set;}
public int sID {get; set;}
}
Two lists :
List<pre> pre1 = new List<pre>();
List<pre> pre2 = new List<pre>();
Query which I wrote to get the unmatched values:
var preResult = pre1.where(p1 => !pre
.any(p2 => p2.id == p1.id && p2.date == p1.date && p2.sID == p1sID));
But the result is wrong here. I am getting all the values in pre1.
Here is solution :
class Program
{
static void Main(string[] args)
{
var pre1 = new List<pre>()
{
new pre {id = 1, date =DateTime.Now.Date, sID=1 },
new pre {id = 7, date = DateTime.Now.Date, sID = 2 },
new pre {id = 9, date = DateTime.Now.Date, sID = 3 },
new pre {id = 13, date = DateTime.Now.Date, sID = 4 },
// ... etc ...
};
var pre2 = new List<pre>()
{
new pre {id = 1, date =DateTime.Now.Date, sID=1 },
// ... etc ...
};
var preResult = pre1.Where(p1 => !pre2.Any(p2 => p2.id == p1.id && p2.date == p1.date && p2.sID == p1.sID)).ToList();
Console.ReadKey();
}
}
Note:Property date contain the date and the time part will be 00:00:00.
I fixed some typos and tested your code with sensible values, and your code would correctly select unmatched records. As prabhakaran S's answer mentions, perhaps your date values include time components that differ. You will need to check your data and decide how to proceed.
However, a better way to select unmatched records from one list compared against another would be to utilize a left join technique common to working with relational databases, which you can also do in Linq against in-memory collections. It will scale better as the sizes of your inputs grow.
var preResult = from p1 in pre1
join p2 in pre2
on new { p1.id, p1.date, p1.sID }
equals new { p2.id, p2.date, p2.sID } into grp
from item in grp.DefaultIfEmpty()
where item == null
select p1;

How to match two Lists with only items that are different in Linq

I have a StudentData class
public class StudentData
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public int? Bonus { get; set; }
public int? Subject1Mark { get; set; }
public int? Subject2Mark { get; set; }
public int? Subject3Mark{ get; set; }
}
Each student has a unique Id that identifies him
I have a List<StudentData> CurrentData that has data of
1, John, Smith, 10, 50 ,50 ,50
2, Peter, Parker, 10, 60 ,60 ,60
3, Sally, Smart, 10, 70 ,70 ,70
4, Danny, Darko, 20, 80, 80, 80
I then have a List<StudentData> DataToUpdate which only contains the Id and Marks fields. Not the other fields.
1, null, null, null, 50 ,50 ,50
2, null, null, null, 65, 60 ,60
3, null, null, null, 70 ,70 ,70
The Ids of the list are not necessary in the same order
If you compare the two lists only Peter Parker's marks have changed in one subject.
I want to get the output to return
2, Peter, Parker, 10, 65 ,60 ,60
I want to takeList<StudentData> CurrentData inner join this with List<StudentData> DataToUpdate but only where marks are different
So in SQL it want the following
SELECT
CurrentData.Id,
CurrentData.FirstName ,
CurrentData.Surname,
CurrentData.Bonus,
DataToUpdate.Subject1Mark,
DataToUpdate.Subject2Mark,
DataToUpdate.Subject3Mark
FROM CurrentData
INNER JOIN DataToUpdate
ON CurrentData.Id= DataToUpdate.Id
AND (
CurrentData.Subject1Mark<> DataToUpdate.Subject1Mark
OR
CurrentData.Subject2Mark<> DataToUpdate.Subject2Mark
OR
CurrentData.Subject3Mark<> DataToUpdate.Subject3Mark
)
How do I do the above in LINQ?
In the Linq select how do I take all properties from CurrentData but include the 3 Subject properties from DataToUpdate in it to give me List<ChangedData>?
I could map each and every property but my StudentData has 100 fields and I would prefer to have something like
select new StudentData {
this=CurrentData,
this.Subject1Mark=DataToUpdate.Subject1Mark,
this.Subject2Mark=DataToUpdate.Subject2Mark,
this.Subject3Mark=DataToUpdate.Subject3Mark,
}
but I'm not sure how to write this
There is an answer in another stackoverflow question which should work but it doesn't. If I implement that solution (I simplify the example for simplicity)
var changedData = currentData
.Join(dataToUpdate, cd => cd.Id, ld => ld.Id, (cd, ld) => new { cd, ld })
.Select(x => { x.cd.Subject1Mark= x.ld.Subject1Mark; return x.cd; })
;
but the above x.cd.Subject1Mark isn't updated by x.ld.Subject1Mark although I use the answer in the linked stackoverflow question
The structure of LINQ query looks very similar to SQL:
var res =
from cur in CurrentData
join upd in DataToUpdate on upd.Id equals cur.Id
where (cur.Subject1Mark != upd.Subject1Mark || cur.Subject2Mark != upd.Subject2Mark || cur.Subject3Mark != upd.Subject3Mark)
select new {
Current = cur
, UpdatedSubject1Mark = upd.Subject1Mark
, UpdatedSubject2Mark = upd.Subject2Mark
, UpdatedSubject3Mark = upd.Subject3Mark
};
The main difference is that filtering out by inequality has moved from the on clause in SQL to a where clause of LINQ.

Find / Count Redundant Records in a List<T>

I am looking for a way to identify duplicate records...only I want / expect to see them.
So the records aren't duplicated completely but the unique fields I am unconcerned with at this point. I just want to see if they have made X# payments of the exact same amount, via the exact same card, to the exact same person. (Bogus example just to illustrate)
The collection is a List<> further whatever X# is the List<>.Count will be X#. In other words all the records in the list match (again just the fields I am concerned with) or I will reject it.
The best I can come up with is to take the first record get value of say PayAmount and LINQ the other two to see if they have the same PayAmount value. Repeat for all fields to be matched. This seems horribly inefficient but I am not smart enough to think of a better way.
So any thoughts, ideas, pointers would be greatly appreciated.
JB
Something like this should do it.
var duplicates = list.GroupBy(x => new { x.Amount, x.CardNumber, x.PersonName })
.Where(x => x.Count() > 1);
Working example:
class Program
{
static void Main(string[] args)
{
List<Entry> table = new List<Entry>();
var dup1 = new Entry
{
Name = "David",
CardNumber = 123456789,
PaymentAmount = 70.00M
};
var dup2 = new Entry
{
Name = "Daniel",
CardNumber = 987654321,
PaymentAmount = 45.00M
};
//3 duplicates
table.Add(dup1);
table.Add(dup1);
table.Add(dup1);
//2 duplicates
table.Add(dup2);
table.Add(dup2);
//Find duplicates query
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
foreach (var item in query)
{
Console.WriteLine("{0}, {1}, {2}, {3}", item.name, item.cardNumber, item.amount, item.count);
}
Console.ReadKey();
}
}
public class Entry
{
public string Name { get; set; }
public int CardNumber { get; set; }
public decimal PaymentAmount { get; set; }
}
The meat of which is this:
var query = from p in table
group p by new { p.Name, p.CardNumber, p.PaymentAmount } into g
where g.Count() > 1
select new
{
name = g.Key.Name,
cardNumber = g.Key.CardNumber,
amount = g.Key.PaymentAmount,
count = g.Count()
};
You're unique entries are based off of the 3 criteria of Name, Card Number, and Payment Amount so you group by them and then use .Count() to count how many of those unique values exist. where g.Count() > 1 filters the group to duplicates only.

Resources