var sql = #"SELECT
a.id AS `Id`,
a.thing AS `Name`,
b.id AS `CategoryId`,
b.something AS `CategoryName`
FROM ..";
var products = connection.Query<Product, Category, Product>(sql,
(product, category) =>
{
product.Category = category;
return product;
},
splitOn: "CategoryId");
foreach(var p in products)
{
System.Diagnostics.Debug.WriteLine("{0} (#{1}) in {2} (#{3})", p.Name, p.Id, p.Category.Name, p.Category.Id);
}
Results in:
'First (#1) in (#0)'
'Second (#2) in (#0)'
CategoryId and CategoryName has values since the following
var products = connection.Query(sql).Select<dynamic, Product>(x => new Product
{
Id = x.Id,
Name = x.Name,
Category = new Category { Id = x.CategoryId, Name = x.CategoryName }
});
Results in:
'First (#1) in My Category (#10)'
'Second (#2) in My Category (#10)'
I'm connecting to a MySQL database if that has anything to do with it.
The simplest way is to call them all Id (case-insensitive, so just a.id and b.id are fine); then you can use:
public void TestMultiMapWithSplit()
{
var sql = #"select 1 as Id, 'abc' as Name, 2 as Id, 'def' as Name";
var product = connection.Query<Product, Category, Product>(sql,
(prod, cat) =>
{
prod.Category = cat;
return prod;
}).First();
// assertions
product.Id.IsEqualTo(1);
product.Name.IsEqualTo("abc");
product.Category.Id.IsEqualTo(2);
product.Category.Name.IsEqualTo("def");
}
If you can't do that, there is an optional splitOn (string) parameter that takes a comma-separated list of columns to treat at splits.
Related
I want to convert this SQL query to EF Core code.
I don't want to use LINQ, any possibility?
with cat (id, id_parent, name) as
(
select id, id_parent, name
from categories
where categories.id = 9
union all
select e.id, e.id_parent, e.name
from dbo.categories e
inner join brands b on b.id_parent = e.id
);
select * from cat;
If I get your question correctly this would work for you:
var result = await _dbContext.Categories
.Where(x => x.Id == 9)
.Select(x => new { Id = x.Id, Parent = x.IdParent, Name = x.Name})
.Concat(
_dbContext.Categories.Join(_dbContext.Brands, cat=> cat.Id, brand=> brand.IdParent, (cat,brand)=>cat)
.Select(x => new { Id = x.Id, Parent = x.IdParent, Name = x.Name }))
.ToListAsync();
I have two tables.
Order:
ID
ProductID
DeliveryDate
Product:
ID
Name
Description
I want to get the list of all product with earliest delivery date.
I am getting correct data by using following sql.
This gives me list of all products with earliest delivery date.
SELECT
p.Name, p.Description, min(o.DeliveryDate)
FROM Product p
JOIN Order o
On o.ProductID = p.ID
Group By p.ID;
I have tried to write it using Linq but something is missing.
I am not able to identify how can I write this query.
I have tried out relative stack overflow solutions but not helped in my case.
My Linq is :
await (from p in dbContext.Product
join o in dbContext.Order
on o.ProductID equals p.ID
select new
{
p.ID,
p.Name,
p.Description,
o.DeliveryDate
}).GroupBy(g => g.ID).ToListAsync();
It gives me data after join and group by. Where should I put Min in the Linq to get the Earliest DeliveryDate for the Product?
I assume you have a navigation property Product.Orders. If not, it's highly recommended to add it to your code. Having that property, this will achieve what you want:
from p in dbContext.Product
select new
{
p.ID,
p.Name,
p.Description,
MinDeliveryDate = (DateTime?)o.Orders.Min(o => o.DeliveryDate)
}
The cast to DateTime? is to prevent exceptions when products don't have orders.
If for some reason you don't have the navigation property and really can't add it at the moment you can use:
from p in dbContext.Product
select new
{
p.ID,
p.Name,
p.Description,
MinDeliveryDate = (DateTime?)dbContext.Order
.Where(o => o.ProductID == p.ID)
.Min(o => o.DeliveryDate)
}
Note that by starting the query at Product and not joining with Order you don't need the grouping any more.
I used some classes to stand in for the database, but does this work?
class Product
{
public int ID;
public string Name;
public string Description;
}
class Order
{
public int ProductID;
public DateTime DeliveryDate;
}
class Program
{
static void Main(string[] args)
{
Product[] proTestData = new Product[]
{
new Product() {ID=1, Name="Widget", Description="Banded bulbous snarfblat"},
new Product() {ID=2, Name="Gadget", Description="Hitchhiker's guide to the galaxy"}
};
Order[] ordTestData = new Order[]
{
new Order() {ProductID=1, DeliveryDate=new DateTime(2017,12,14)},
new Order() {ProductID=1, DeliveryDate=new DateTime(2017,12,20)},
new Order() {ProductID=2, DeliveryDate=new DateTime(2017,12,23)},
new Order() {ProductID=2, DeliveryDate=new DateTime(2017,12,22)},
new Order() {ProductID=2, DeliveryDate=new DateTime(2017,12,21)},
new Order() {ProductID=1, DeliveryDate=new DateTime(2017,12,18)}
};
var rows =
(from p in proTestData
join o in ordTestData
on p.ID equals o.ProductID
group o.DeliveryDate by p into grp
select new {
grp.Key.ID,
grp.Key.Name,
grp.Key.Description,
minDate = grp.Min()}
).ToList();
foreach (var row in rows)
{
Console.WriteLine("{0}: {1}", row.Name, row.minDate);
}
}
}
The output is
Widget: 12/14/2017 12:00:00 AM
Gadget: 12/21/2017 12:00:00 AM
If you're trying to order your groups you can use the "OrderBy" linq method.
Apply the following after the .GroupBy()
.OrderBy(group => group.First().DeliveryDate).ToListAsync();
Say I have a
TableA
string Name
string Description
TableB
string Name
string Value
TableA and TableB are joined by Name. (In theory, i.e. not enforced in DB)
I want to create an object:
public MyObject
{
string Name
string Description
List<string> Values
}
I'm tying to understand how to combine these using LINQ.
var tableA = _oda.GetTableA();
var tableB = _oda.GetTableB();
var model = from a in tableA
join b in tableB on a.NAME equals b.NAME
select new MyObject
{
Name= a.Name,
Description = a.Description,
Values = "<Not sure to get list of tableb.Value>"
};
If it's an inner join, you can use GroupBy after your join:
var tableA = new List<TableA> { new TableA { Name = "1", Description = "D1" }, new TableA { Name = "2", Description = "D2"} };
var tableB = new List<TableB> { new TableB { Name = "1", Value = "V1" }, new TableB { Name = "1", Value = "V2"} };
var result = tableA.Join(tableB, a => a.Name, b => b.Name, (a, b) => new { A = a, B = b})
.GroupBy(k => k.A, e => e.B.Value)
.Select(g => new MyObject
{
Name = g.Key.Name,
Description = g.Key.Description,
Values = g.ToList()
});
foreach (var res in result)
{
Console.WriteLine("Name: {0}, Description: {1}, Value: {2}", res.Name, res.Description, string.Join(", ", res.Values));
}
Didn't try the code, but something like this should work.
var result = from a in TableA
join b in TableB on a.Name equal b.Name
group b.Value by a into g
select new MyObject
{
Name = g.Key.Name,
Description = g.Key.Description,
Values = g.ToList()
}
Sorry if the title was confusing.
Currently I am practicing with Entity Framework and LINQ expressions and got stuck on this.
I have a table with columns:"Personal ID", "Name", "Surname", "Phone ID" and all values on Phone ID are unique. I would like to create another table in which there would be same columns except for last being "Phone Count" which shows how many phones are associated with same Person(Personal ID). I want the table to show only 3 first highest count rows.
Here is the code i've wrote to make table that i've described above.
using (var db = new PRDatabaseEntities())
{
var query = from a in db.Person
join t in db.Repairs on a.PID equals t.Repairman_PID
orderby a.PID
select new
{
a.PID,
a.Name,
a.Surname,
t.Phone_ID
};
}
You could try with following group by LINQ query:
// First, generate a linq query
var query = from a in Persons
join t in Repairs on a.PID equals t.Repairman_PID
group new { a, t } by new { a.PID, a.Name, a.Surname } into g
select new
{
PID = g.Key.PID,
Name = g.Key.Name,
Surname = g.Key.Surname,
PhoneCount = g.Count()
};
// Then order by PhoneCount descending and take top 3 items
var list = query.OrderByDescending(t => t.PhoneCount).Take(3).ToList();
Try this:
using (var db = new PRDatabaseEntities())
{
var query = db.Person
.GroupJoin(db.Repairs,
p => p.PID, r => r.PID,
(p, r) => new {
PID = p.PID,
Name = p.Name,
Surname = p.Surname,
Count = r.Count()
};
}
I am trying out C# LINQ Joins from this msdn page.
I am not able to apply order by on inner join even though it works with group join.
The data on which queries are run are:
class Product
{
public string Name { get; set; }
public int CategoryID { get; set; }
}
class Category
{
public string Name { get; set; }
public int ID { get; set; }
}
// Specify the first data source.
static List<Category> categories = new List<Category>()
{
new Category(){Name="Beverages", ID=001},
new Category(){ Name="Condiments", ID=002},
new Category(){ Name="Vegetables", ID=003},
new Category() { Name="Grains", ID=004},
new Category() { Name="Fruit", ID=005}
};
// Specify the second data source.
static List<Product> products = new List<Product>()
{
new Product{Name="Cola", CategoryID=001},
new Product{Name="Tea", CategoryID=001},
new Product{Name="Mustard", CategoryID=002},
new Product{Name="Pickles", CategoryID=002},
new Product{Name="Carrots", CategoryID=003},
new Product{Name="Bok Choy", CategoryID=003},
new Product{Name="Peaches", CategoryID=005},
new Product{Name="Melons", CategoryID=005},
};
The desired output is (list order by categories in brackets and then by product) :
Cola(Beverages)
Tea(Beverages)
Mustard(Condiments)
Pickles(Condiments)
Melons(Fruit)
Peaches(Fruit)
Bok Choy(Vegetables)
Carrots(Vegetables)
I was able to produce this output using group-join with orderby and 2nd from-select to deform group hierarchy and produce plain list as follows:
var listGroupJoinOrderBy =
from category in categories
join product in products on category.ID equals product.CategoryID into prodGroup
from prod in prodGroup
orderby category.Name, prod.Name //first order by category name then product name
select
new
{
Category = category.Name,
Product = prod.Name
};
But then I am unable to produce this same output with orderby applied to inner join (that is group-join without into clause). I tried following variants:
Variant #1
var innerJoinOrderBy =
from category in categories
orderby category.Name //orderby category name
join product in products on category.ID equals product.CategoryID
orderby product.Name //orderby product name
select
new
{
Category = category.Name,
Product = product.Name
};
Variant #2
var innerJoinOrderBy =
from category in categories
join product in products on category.ID equals product.CategoryID
orderby category.Name, product.Name //orderby category first and then by product name
select
new
{
Category = category.Name,
Product = product.Name
};
However both variants give the same output as if no orderby is used, and produces the following output:
Cola (Beverages)
Tea (Beverages)
Mustard (Condiments)
Pickles (Condiments)
Carrots (Vegetables)
Bok Choy (Vegetables)
Peaches (Fruit)
Melons (Fruit)
Q. How can I produce the desired output with inner-join and orderby?
Anyway, to print the query result one can use following foreach (just change the query variable name) :
foreach (var product in simpleInnerJoin)
{
Console.WriteLine(product.Product + " (" + product.Category + ")");
}
Your second option should work fine
var innerJoinOrderBy =
from c in categories
join p in products on c.ID equals p.CategoryID
orderby c.Name, p.Name
select new {
Category = c.Name,
Product = p.Name
};
Output:
[
{ Product="Cola", Category="Beverages" },
{ Product="Tea", Category="Beverages" },
{ Product="Mustard", Category="Condiments" },
{ Product="Pickles", Category="Condiments" },
{ Product="Melons", Category="Fruit" },
{ Product="Peaches", Category="Fruit" },
{ Product="Bok Choy", Category="Vegetables" },
{ Product="Carrots", Category="Vegetables" }
]
From your output I see that items go in their original order. Make sure you have applied orderby operator in query.
Take the Orderby out of the main Linq and use
foreach (var product in simpleInnerJoin.Orderby(i=> i.Category.Name).ThenBy(i=>i.Product.Name))
{
Console.WriteLine(product.Product + " (" + product.Category + ")");
}
I think this should give you the output you require.