Reputation: 97
basically i have some data table similar to this
| ItemId | RuleId | IsActive |
------------------------------
| ItemA | RuleA | FALSE |
------------------------------
| NULL | RuleA | TRUE |
------------------------------
| ItemC | RuleA | TRUE |
------------------------------
| ItemA | RuleB | FALSE |
------------------------------
| NULL | RuleB | TRUE |
------------------------------
and i want to get result like this
for item a
| ItemId | RuleId | IsActive |
------------------------------
| ItemA | RuleA | FALSE |
------------------------------
| ItemA | RuleB | FALSE |
------------------------------
for item b
| ItemId | RuleId | IsActive |
------------------------------
| NULL | RuleA | TRUE |
------------------------------
| NULL | RuleB | TRUE |
------------------------------
for item c
| ItemId | RuleId | IsActive |
------------------------------
| ItemC | RuleA | TRUE |
------------------------------
| NULL | RuleB | TRUE |
------------------------------
basically if there's specific rule, then use the specified, otherwise use the default rule setting with null as itemId
how to achieve this in linq?
Thanks in advance
EDIT: thanks for styling, @lazyberezovsky. @dasblinkenlight I don't have the query for sql either. I do need in linq way, but it's also of great help if anyone can give me some advice how to get the result in sql way.
Upvotes: 1
Views: 481
Reputation: 236248
You can group all items by RuleId
, then select first item from each group, which has specified itemId
. If there is no such item, then create and return default item:
from i in items
group i by i.RuleId into g
select g.FirstOrDefault(x => x.ItemId == itemId) ??
new Item { ItemId = null, RuleId = g.Key, IsActive = true }
This query will work with in-memory items. But you can't create new default item on server side - you will receive an error
The entity or complex type 'Item' cannot be constructed in a LINQ to Entities query.
So, return anonymous types instead of items, and construct items in memory:
(from i in context.Items
group i by i.RuleId into g
let item = g.FirstOrDefault(x => x.ItemId == itemId)
select new
{
ItemId = item == null ? null : itemId, // default if not found
RuleId = g.Key,
IsActive = item == null ? true : item.IsActive
})
.AsEnumerable() // move to memory
.Select(i => new Item {
ItemId = i.ItemId,
RuleId = i.RuleId,
IsActive = i.IsActive
});
That works with EF.
Upvotes: 0
Reputation: 21664
This isn't the prettiest LINQ query, but here's a complete code sample that works
//the sample data as provided by the OP
var rules = new []
{
new { ItemId = "ItemA", RuleId = "RuleA", IsActive = false },
new { ItemId = (string)null, RuleId = "RuleA", IsActive = true },
new { ItemId = "ItemC", RuleId = "RuleA", IsActive = true },
new { ItemId = "ItemA", RuleId = "RuleB", IsActive = false },
new { ItemId = (string)null, RuleId = "RuleB", IsActive = true },
};
//the items that we want to get the rules for
var items = new [] { "ItemA", "ItemB", "ItemC", };
//get the rules for all of the items
var query = from i in items
let itemRules = from r in rules
//we need to prefer rules that match our id
orderby r.ItemId ?? "" descending
//match on id or null
where (r.ItemId ?? i) == i
group r by r.RuleId into grouping
//take the best match
select grouping.First()
select new
{
i,
itemRules,
};
and here's the output
I don't think this will work with LINQ to SQL but it certainly works with LINQ to Objects. I'll see if I can get a version working with LINQ to SQL when I've got some time to build a test DB.
Upvotes: 1
Reputation: 39777
You can use any language construct in LINQ (it's not called Language Integrated Query for nothing). For example if you use VB.NET - you can use CHOOSE
function directly inside of LINQ query
Upvotes: 0