This query below doesn't work because String.Join is not translatable.
PostgreSQL has the string_agg(expression, delimiter) feature though.
Is there anyway to use it from Linq?
var vwTourWithCategorieses = Context.Tours
.Join(Context.TourCategories, t => t.TourId, tc => tc.TourId,
(t, tc) => new { t.TourId, t.Name, tc.CategoryId})
.Join(Context.Categories, x => x.CategoryId, c => c.CategoryId,
(x, c) => new { x.TourId, TourName = x.Name, CategoryName = c.Name})
.GroupBy(x => new { x.TourId, x.TourName },
(key, c) => new VwTourWithCategories
{
TourId = key.TourId,
Name = key.TourName,
Categories = string.Join(",", c.Select(i => i.CategoryName))
})
.ToList();
Yes, unfortunately String.Join is not supported by EF, but I think you could project the result that you expect using Linq to objects after you materialize your query:
var query= Context.Tours
.Join(Context.TourCategories, t => t.TourId, tc => tc.TourId,
(t, tc) => new { t.TourId, t.Name, tc.CategoryId})
.Join(Context.Categories, x => x.CategoryId, c => c.CategoryId,
(x, c) => new { x.TourId, TourName = x.Name, CategoryName = c.Name})
.GroupBy(x => new { x.TourId, x.TourName }).ToList()
var result=query.Select( g=> new VwTourWithCategories
{
TourId = g.Key.TourId,
Name = g.Key.TourName,
Categories = string.Join(",", g.Select(i => i.CategoryName))
});
If you want to see all the CLR methods that are supported, you can check this link.
Update
Your query could be simpler if you use navigation properties. I think that is a many to many relationship, so you could do something like this:
var query= Context.Tours.Select(t=> new
{
t.TourId,
t.Name,
CategoryNames = t.TourCategories.Select(tc=>tc.Category.Name)
}
).ToList();
var result=query.Select( g=> new VwTourWithCategories
{
TourId = g.Key.TourId,
Name = g.Key.TourName,
Categories = string.Join(",", g.Select(i => i.CategoryName))
});
Related
I have the following Entity Framework Core query:
var result = await products
.Select(x => new {
PackageId = x.PackageId,
Price = x.Price
})
.GroupBy(x => x.PackageId)
.Select(x => new {
PackageId = x.Key,
TotalPrice = x.Sum(y => y.Price)
})
.ToDictionaryAsync(x => x.PackageId, x => new Model {
TotalPrice = x.TotalPrice
});
I was able to make this query evaluate on the server.
Is there a way to simplify it and still make sure it evaluates on the server?
Try this.
var result = await products
.GroupBy(x => x.PackageId)
.Select(x => new {
PackageId = x.Key,
TotalPrice = x.Sum(y => y.Price)
})
.ToDictionaryAsync(x => x.PackageId, x => new Model {
TotalPrice = x.TotalPrice
});
EF will take care about not to load unnecessary data.
I've been asked to simplify the following Linq query:
var orders = db.Orders
.Join(db.Shipments,
o => o.OrderID,
s => s.OrderID,
(o, s) => new { Order = o, Shipment = s })
.Join(db.LineItems,
s => s.Shipment.ShipmentID,
l => l.ShipmentID,
(s, l) => new { Order = s.Order, Shipment = s.Shipment, LineItem = l })
.Join(db.StatusTypes,
s => s.Shipment.StatusTypeID,
st => st.StatusTypeID,
(s, st) => new { Order = s.Order, Shipment = s.Shipment, LineItem = s.LineItem, Description = st.ExternalDescription })
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Order.Created),
OrderNumber = x.Key,
Cost = x.Sum(y => y.LineItem.UnitPrice).ToString(),
Status = x.Max(y => y.Description)
}
);
By replacing the Joins with Includes. I've searched around, and I've discovered that Joins and Includes are somewhat equivalent. But I can't figure out how to convert this query to one that uses includes instead of joins. Is it actually less code and simpler to use includes instead of joins here?
I strongly suggest you to use navigation properties instead of manual builded joins. It will be more efficent and controlable. Read this article.
If you would convert your query to navigation property form by using Include, it would seem like that;
var orders = db.Orders
.Include(x => x.Shipments)
.Include(x => x.Shipments.Select(y => y.LineItems))
.Include(x => x.Shipments.Select(y => y.StatusType))
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Created),
OrderNumber = x.Key,
Cost = x.LineItems.Sum(k => k.UnitPrice),
Status = x.Max(y => y.Description)
}
);
But, as I said, you should define the navigation properties for entities first.
var orders = db.Orders
.Include("Shipments")
.Include("Shipments.LineItems")
.Include("Shipments.StatusTypes")
.Where(x => x.Order.AccountID == accountId)
.GroupBy(x => x.Order.OrderNumber)
.ToList()
.Select(
x => new OrderStatusViewModel
{
Date = x.Max(y => y.Order.Created),
OrderNumber = x.Key,
Cost = x.Sum(y => y.LineItem.UnitPrice).ToString(),
Status = x.Max(y => y.Description)
}
);
i not tested above code ,just try
ObjectQuery.Include Method (String)
I have a Dictionary<Guid,IList<string>> which shows all the names an entity can have.
I want to convert this to see all the names mapped to all the entities.
so:
[["FFF" => "a", "b"],
["EEE" => "a", "c"]]
Becomes
[["a" => "FFF", "EEE"],
["b" => "FFF"],
["c" => "EEE"]]
I know this is easy to do with foreaches but I'm wondering if there is a way with LINQ / ToDictionary?
private static void Main(string[] args)
{
var source = new Dictionary<Guid, IList<string>>
{
{ Guid.NewGuid(), new List<string> { "a", "b" } },
{ Guid.NewGuid(), new List<string> { "b", "c" } },
};
var result = source
.SelectMany(x => x.Value, (x, y) => new { Key = y, Value = x.Key })
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(y => y.Value).ToList());
foreach (var item in result)
{
Console.WriteLine($"Key: {item.Key}, Values: {string.Join(", ", item.Value)}");
}
}
var dic = new Dictionary<string, List<string>>()
{
{"FFF", new List<string>(){"a", "b"}},
{"EEE", new List<string>(){"a", "c"}}
};
var res = dic.SelectMany(x => x.Value, (x,y) => new{Key = y, Value = x.Key})
.ToLookup(x => x.Key, x => x.Value);
Dictionary<int,IList<string>> d = new Dictionary<int ,IList<string>>(){
{1,new string[]{"a","b"}},
{2,new string[]{"a","d"}},
{3,new string[]{"b","c"}},
{4,new string[]{"x","y"}}};
d.SelectMany(kvp => kvp.Value.Select(element => new { kvp.Key, element}))
.GroupBy(g => g.element, g => g.Key)
.ToDictionary(g => g.Key, g => g.ToList());
I have a SQL Query
select Firma.Name as companyName,
Taetigkeit.Taetigkeit as skillName,
SUM(Zeit) as time
from Zeiterfassung
inner join Firma On ZEiterfassung.FirmenID = Firma.ID
inner join Taetigkeit on Zeiterfassung.TaetigkeitID = Taetigkeit.ID
group by Taetigkeit, Firma.Name
order by Firma.Name
And want to "translate" it to linq. Here is what I tried:
var query = db.Zeiterfassung
.Where(x => x.Firma.ID == x.FirmenID && x.TaetigkeitID == x.Taetigkeit.ID)
.GroupBy(x => x.Taetigkeit.Taetigkeit1)
.Select(x => new Evaluation() { skillName = x.Key, time = x.Sum(y => y.Zeit), //skillName = x.Sum(x => x.Zeit), })
.OrderBy(x => x.skillName);
I dont know who to solve this with joins and the group by because all the time when i do a groupBy i cant access the other members.
From data you provided, I think query should look like
from z in db.Zeiterfassung
join f in db.Firma on z.FirmenID equals f.ID
join t in db.Taetigkeit on z.TaetigkeitID equals t.ID
select new { f.Name, t.Taetigkeit, z.Zeit) into x
group x by new { x.Taetigkeit, f.Name } into g
select new {
CompanyName = g.Key.Name,
SkillName = g.Key.Taetigkeit,
Time = g.Sum(i => i.Zeit)
}
Or with navigation properties:
db.Zeiterfassung
.Select(z => new { z.Zeit, z.Taetigkeit.Taetigkeit1, z.Firma.Name })
.GroupBy(x => new { x.Taetigkeit1, x.Name })
.Select(g => new Evaluation {
companyName = g.Key.Name,
skillName = g.Key.Taetigkeit1,
time = g.Sum(y => y.Zeit)
});
I have this function:
/// <summary>
/// Return array of all badges for a users
/// </summary>
public static Badge[] getUserBadges(int UserID)
{
Badge[] ReturnBadges;
using (MainContext db = new MainContext())
{
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => c.BadgeID)
.Select(c => new { BadgeCount = c.Count(), TheBadge = c });
ReturnBadges = new Badge[q.Count()];
int i = 0;
foreach (var UserBadge in q)
{
ReturnBadges[i] = new Badge(UserBadge.TheBadge.Key);
ReturnBadges[i].Quantity = UserBadge.BadgeCount;
i++;
}
}
return ReturnBadges;
}
I wish to order by tblBadges.OrderID ascending but I can't seem to find out where to put it, can anyone help?
I've tried:
.OrderBy(c=> c.TheBadge.OrderID)
But it's not valid code. TheBadge.Key in the loop is a tblBadges type. It's confusing me a bit why intellisense wont let me do the order by anywhere!
TheBadge isn't a single badge, it's a group of badges... so I'd personally rename it if I were you. Now, which OrderId do you want to get? You've got multiple entities in the gruop. For example, you could do this:
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => c.BadgeID)
.Select(c => new { BadgeCount = c.Count(), TheBadge = c })
.OrderBy(x => x.TheBadge.First().OrderId);
That will order by some notional "first" element - although I don't know what the generated SQL will look like.
If you expect the OrderId to be the same for every badge with the same ID, you might use:
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => new { c.BadgeID, c.OrderID })
.OrderBy(group => group.Key.OrderID)
.Select(c => new { BadgeCount = c.Count(), TheBadge = c });
Try this:
var q = db.tblBadgeUsers
.Where(c => c.UserID == UserID)
.GroupBy(c => c.BadgeID)
.Select(c => new { BadgeCount = c.Count(), TheBadge = c.Key }) // *mod
.OrderBy(c=> c.TheBadge.OrderID); // * added
In the following line, TheBadge is a linq collection, not the badge itself. You want c.Key.
.Select(c => new { BadgeCount = c.Count(), TheBadge = c })