How to write this code using the Linq Extension Method-Syntax? - c#

I have these following classes from an Linq Example:
public class Customer
{
public Customer();
public Cities City { get; set; }
public string Name { get; set; }
public Order[] Orders { get; set; }
}
public class Product
{
public Product();
public double Price { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
}
public class Order
{
public Order();
public int OrderID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
public bool Shipped { get; set; }
}
static void Main(string[] args)
{
var allOrders = from cust in customers
from ord in cust.Orders
join prod in products on ord.ProductID equals prod.ProductID
select new
{
cust.Name,
ord.ProductID,
OrderAmount = ord.Quantity * prod.Price
};
}
I want to create the same collection (Name, ProductID, Orderamount) with the linq Extension Method Syntax. My problem is that I don't know how to realize the two datasources from cust in customers from ord in cust.Orders in the Extension Method Syntax.
Does anyone have any idea how it could work?
I got this but I have no access to the `CustomerName in the collection.
var allOrders2 =
customers.SelectMany(cust => cust.Orders)
.Join(products,
ord => ord.ProductID,
prod => prod.ProductID,
(ord, prod) => new
{
ord.ProductID,
OrderAmount = ord.Quantity * prod.Price
});

If you order does not refer back to a customer, the trick is to first create a dataset which keeps the customers and orders linked together:
customers
.SelectMany(c => c.Orders.Select(o => new {
cust = c,
ord = o
}))
Then on this CustomerOrder (co) you can apply your join:
...
.Join(products,
co => co.ord.ProductID,
prod => prod.ProductID,
(co,prod) => new {
co.cust.Name,
co.ord.ProductID,
OrderAmount = ord.Quantity * prod.Price});

Related

LINQ query on entities with join returning items by category

I'm using Entity Framework core. I have the following entities:
public class Category {
public long Id { get; set; }
public string Name { get; set; }
}
public class Product {
public long Id { get; set; }
public long CategoryId { get; set; }
public string Name { get; set; }
}
I'd like to get a grouping whereby my key is my Category and I have a list of Products. I'd like to do this running a single query against the DB.
var data = context.Categories
.Join(
Products,
c => c.Id,
p => p.CategoryId,
(c, p) => new {
Category = c,
Product = p
}
)
.ToList();
This runs the query I want and seems to produce a list with an anonymous object that has a Category and Product. If I then do the following, it kind of does what I want:
var grouped = data.GroupBy(x => new { x.Category });
The Key is fine, but the list of values seems to repeat the Categories. Is there a way to make it so I have a Key that's the Category and then the values is a list of Product objects? Would prefer method syntax, but at this point I can hopefully figure out query syntax if that's what somebody can get me.
Try following :
class Program
{
static void Main(string[] args)
{
Context context = new Context();
var results = (from c in context.Categories
join p in context.Products on c.Id equals p.CategoryId
select new { category = c, product = p })
.GroupBy(x => x.category.Id)
.ToList();
}
}
public class Context
{
public List<Category> Categories { get; set; }
public List<Product> Products { get; set; }
}
public class Category
{
public long Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public long Id { get; set; }
public long CategoryId { get; set; }
public string Name { get; set; }
}
This will great a list of products grouped by category
public class Category {
public long Id { get; set; }
public string Name { get; set; }
}
public class Product {
public long Id { get; set; }
public long CategoryId { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
ICollection<Category> categories = new Collection<Category>(new List<Category>());
ICollection<Product> products = new Collection<Product>(new List<Product>());
var data = categories
.Join(
products,
c => c.Id,
p => p.CategoryId,
(c, p) => new {
Category = c,
Product = p
}
)
.ToList();
var grouped = data.GroupBy(o => o.Category, o => o.Product);
}
}
The correct solution would be something like this:
var result = (from p in context.Products
join c in context.Categories
on p.CategoryId equals c.Id
select new
{
Product = p,
Category = c
})
.ToList() // because still grouping object is not supported in EF Core we have to materialize first and then do our grouping
.GroupBy(g => g.Category, g => g.Product)
.ToDictionary(p => p.Key, p => p.ToList());
This is how I ended up solving it:
var grouped = data
.GroupBy(x => new { x.Category })
.Select(x => new {
Category = x.Key.Category,
Products = x.ToList().Select(x => x.Product).ToList()
});;

LINQ - Group by with SUM IF

Im trying to write in linq something which is easy (for me) in SQL. Any idea how to do something like this:
select Items.IdItem,
Items.Name,
count(1) as Quantity,
SUM(IF(State.IdStatus=1,1,0)) as Availible
from Items
inner join State
on Items.IdItem=State.IdItem
group by Items.IdItem
I wrote something like that:
var result = from items in _context.Items
join state in _context.State on items.IdItem equals State.IdItem
group items by { items.IdItem, items.Name } into g
select new { Name= g.Key.Name, IdItem=g.Key,IdItem, Quantity=g.Count(), Availible= ???? }
Any tips?
Provided you have set your relations in the database and navigational properties in your model (which is done by generators), then in Linq you seldom need joins (for tables that don't have a direct relation).
Second, you are not really after Sum() here, are you. Looking at your sum function and field name, it more looks like you are after "Is Available" check.
var result = from i in _context.Items
group i by i.IdItem into g
select new {
IdItem = g.Key,
Name = g.First().Name,
Quantity = g.Count(),
Available = g.Any(it => it.State.IdStatus == 1)
};
EDIT: if your Sum was intentional, then you can replace the Available part by (it is a bit, right?):
Available = g.Sum(it => it.State.IdStatus)
EDIT: This one is based on your data/model and SQL at top:
var result = from i in _context.Items
select new
{
i.IdItem,
i.Name,
Quantity = i.States.Count(),
Available = i.States.Count(x => x.IdStatus == 1)
};
Sample code and results:
string defaultConString = #"server=.\SQLExpress;Database=SampleDb;Trusted_Connection=yes;";
void Main()
{
var _context = new MyContext(defaultConString);
var result = from i in _context.Items
select new
{
i.IdItem,
i.Name,
Quantity = i.States.Count(),
Available = i.States.Count(x => x.IdStatus == 1)
};
result.Dump(); // linqPad
}
public class MyContext : DbContext
{
public MyContext(string connectionString)
: base(connectionString)
{ }
public DbSet<Item> Items { get; set; }
public DbSet<State> States { get; set; }
}
[Table("Items")]
public partial class Item
{
public Item()
{
this.States = new List<State>();
OnCreated();
}
[Key]
public virtual int IdItem { get; set; }
public virtual string Name { get; set; }
public virtual System.DateTime ImportDate { get; set; }
public virtual System.DateTime ReturnDate { get; set; }
public virtual IList<State> States { get; set; }
partial void OnCreated();
}
[Table("States")]
public partial class State
{
public State()
{
OnCreated();
}
[Key]
public virtual int IdState { get; set; }
public virtual string SmId { get; set; }
public virtual string Number { get; set; }
public virtual int IdItem { get; set; }
public virtual int IdStatus { get; set; }
public virtual Item Item { get; set; }
partial void OnCreated();
}
Result:
IdItem Name Quantity Available
1 Test01 3 2
2 Test02 2 1
3 Test03 1 0

LINQ - nested GroupJoin

I have looked at many LINQ examples on how to do GroupJoin. But I am not sure how to perform nested GroupJoin. Does somebody have any idea?
I have a following simple classes:
public class Subject
{
public int SubjectID { get; set;}
public string Name { get; set; }
}
public class SubjectStudent
{
public int SubjectStudentID { get; set; }
public int SubjectID { get; set; }
public int StudentID { get; set; }
}
public class StudentGrade
{
public int StudentGradeID { get; set;}
public int StudentID { get; set; }
public int GradeID { get; set; }
}
var subjects = (from s in Subject
join ss in SubjectStudent on s.SubjectID equals ss.SubjectID into SubjectStudents
select new
{
SubjectID = s.SubjectID,
Students = SubjectStudents
})
.ToList();
foreach (var subject in subjects)
{
foreach(var student in subject.Students)
{
//foreach(var grade in student.Grades)
//{
// I want to get grades for each subject.Students
//}
}
}
Can I have another GroupJoin after SubjectStudents, i.e. StudentGrades? I want to be able to iterate over StudentGrades in each subject.Students.
Thank you for any help.
Your data structure looks a bit confusing to me. Also, not sue if this is what you expect:-
var result = (from s in subjects
join ss in subjectStudents on s.SubjectID equals ss.SubjectID into SubjectStudents
select new
{
SubjectID = s.SubjectID,
Students = from ss in SubjectStudents
join g in studentsGrade on ss.StudentID equals g.StudentID
select new
{
StudentId = ss.StudentID,
GradeId = g.GradeID
}
})
.ToList();
Sample Fiddle

Adding list values to a LINQ query

I have the following object (view model) that I want to use:
public class assignmentViewModel
{
public string choreName { get; set; }
public List<string> personName { get; set; }
}
LINQ statement:
var avm = (from a in Assignments
join c in Chores on a.ChoreID equals c.ChoreId
join p in Persons on a.PersonID equals p.PersonID
select new assignmentViewModel
{
personName = p.PersonName.ToList(),
choreName = c.ChoreName
}).ToList();
I can have multiple people in an assignment. I want to be able to pull back my data into this ViewModel. The error I'm getting currently is:
Cannot implicitly convert type 'System.Collections.Generic.List<char>'
to 'System.Collections.Generic.List<string>'
My data (if it helps) is:
chore #1
person 1
chore #2
person 1
person 2
The Person model is:
public partial class person
{
public int personID { get; set; }
public string personName { get; set; }
public System.DateTime personDOB { get; set; }
public string personEmail { get; set; }
public string personPhone { get; set; }
public string personPhoneCarrier { get; set; }
public bool isActive { get; set; }
}
You are looking for Grouping here, you need to group the records based on choreName, I would do it like this:
(from a in Assignments
join c in Chores on a.ChoreID equals c.ChoreId
join p in Persons on a.PersonID equals p.PersonID
select new
{
person = p,
choreName = c.ChoreName
})
.GroupBy(x => x.choreName)
.Select(g => new assignmentViewModel
{
personName = g.Select(x => x.person.PersonName).ToList()
choreName = g.Key
}).ToList();

best way to extract a one to many relationship with dapper dot net orm?

I have two classes 'Product' and 'Seller'.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public Seller Seller { get; set; }
public int? SellerId { get; set; }
}
public class Seller
{
public int Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
I want to extract a list of Sellers with all of their products using dapper.
Currently i'm doing it like this :
Dictionary<int, Seller> dic = new Dictionary<int, Seller>();
Conn.Query<Seller, Product, int>
(#"select s.*,p.* from Sellers s Join Products p
on p.SellerId = s.Id",
(s, p) => {
if (dic.ContainsKey(s.Id))
dic[s.Id].Products.Add(p);
else
{
s.Products = new List<Product>();
s.Products.Add(p);
dic.Add(s.Id, s);
}
return s.Id;
});
var sellers = dic.Select(pair => pair.Value);
Is there any better way?
I think you are mixing how you want to store the data with how you want to use it. I would suggest normalizing your database.
//Normalized Database classes
public class Seller
{
public int Id { get; set; } // primary key
public string Name { get; set; }
}
public class Product
{
public int Id { get; set; } // primary key
public int SellerId { get; set; } // foreign key
public string Name { get; set; }
public decimal Price { get; set; }
}
Then you can query the Seller and product tables directly.
var Sellers = connection.Query<Seller>("Select * from Seller");
var Products = connection.Query<Product>("Select * from Product");
Then use linq "group by" to throw Product into a dictionary
var SellerWithProductsDict =
(from prod
in Products
group prod by prod.SellerId
into groupedProducts
select groupedProducts)
.ToDictionary(gp => gp.SellerId, gp => gp.ToList());
Then you can loop through the SellerWithProductsDict to see all the seller products, and if you need the seller name just get it by index from the Sellers query result
**************************************
That's the end of the answer, but if you really need the products mixed in with the sellers, you could use the same database structure above and do something like:
var qry = #"Select s.Id as SellerId,
s.Name as SellerName,
p.Id as ProductId,
p.Name as ProductName,
p.Price as ProductPrice
From Seller as s, Product as p
Where s.Id = p.id"
var SellerWithProducts = connection.Query(qry)
Then use linq "group by" functions to throw that into a dictionary. I would suggest looking at this "Linq group-by multiple fields" post for help with the group by linq
My last post went crazy for some reason.
First off, it might be a bad idea to get everything from your database. You might want to consider limiting your query. Even though your table might be small now, you don't want to back yourself against a wall.
With that being said, I would recommend simplifying the code to get the Sellers and Products by adding static methods onto Seller and Product.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int? SellerId { get; set; }
public Seller Seller { get; set; }
public static Product GetProductById(int id)
{
Product product = null;
using (SqlConnection connection = new SqlConnection("connectionString"))
{
product = connection.Query<Product>("select * from Product where Id = #Id", new { Id = id }).SingleOrDefault();
product.Seller = connection.Query<Seller>("select * from Seller where Id = #Id", new { Id = product.SellerId }).SingleOrDefault();
}
return product;
}
}
public class Seller
{
public int Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
public static Seller GetSellerById(int id)
{
Seller seller = null;
using (SqlConnection connection = new SqlConnection("connectionString"))
{
seller = connection.Query<Seller>("select * from Seller where Id = #Id", new { Id = id }).SingleOrDefault();
if(seller != null)
{
seller.Products = connection.Query<Product>("select * from Product where SellerId = #Id", new { Id = id }).ToList();
seller.Products.ForEach(p => p.Seller = seller);
}
}
return seller;
}
}
Keep in mind, this is rough and doesn't represent everything you may need, but it is easier to use in the long run and follows Object Oriented Design better than making one of Queries to get your data.
You can further expand on this idea by adding methods that query for other things about the seller or product like GetSellersInZipCode(int zipCode) and return a List via that method.

Categories