I have the following Entity Framework query:
IQueryable<Unit> units = context.Units;
Product product = new Product {
Conversions = model.Conversions.Select(y => new Conversion {
UnitId = units
.Where(z => z.Name == y.Unit)
.Select(z => z.Unit.Id)
.FirstOrDefault(),
Value = y.Value
}).ToList(),
Name = model.Name
}
I tried to use await before units.Where( ... as:
Product product = new Product {
Conversions = model.Conversions.Select(y => new Conversion {
UnitId = await units
.Where(z => z.Name == y.Unit)
.Select(z => z.Unit.Id)
.FirstOrDefaultAsync(),
Value = y.Value
}).ToList(),
Name = model.Name
}
This is not allowed as it is inside new Conversion ...
Shouldn't I use await? How can I use it in this query?
You can use this approach :
unitId = await Task.Factory.Start<int>(()=>{
return units.Where(z => z.Name == y.Unit)
.Select(z => z.Unit.Id)
.FirstOrDefault();
})
change int to your return type.
Related
I'm creating a linq query in which I need to return minimum and maximum values of the years and prices of cars.
Linqpad:
var result = (from c in Cars
where c.IsShowed == true
c.CarCod == carCod
group c by c.CarCod into cg
select new {
MinPrice = cg.Min(cv => cv.Price) ,
MaxPrice = cg.Max(cv => cv.Price),
MinYear = cg.Min(cv => cv.Year),
MaxYear = cg.Max(cv => cv.Year)
})
.SingleOrDefault();
result.Dump();
How would I return a default value, example 0, for all properties MinPrice, MaxPrice, MinYear, MaxYear if the query is empty.
You should declare intermediate Result class for that:
var result = (from c in Cars
where c.IsShowed && c.CarCod == carCod
group c by c.CarCod into cg
select new Result {
MinPrice = cg.Min(cv => cv.Price) ,
MaxPrice = cg.Max(cv => cv.Price),
MinYear = cg.Min(cv => cv.Year),
MaxYear = cg.Max(cv => cv.Year)
})
.SingleOrDefault() ?? new Result();
Before I asked Prevent sort result of union in entity framework
I got my answer but now I have new problem with this issue. I have this code:
var productExactlyTitle = products.Where(x => x.Title == keyword);
var productStartWithPhrase = products.Where(x => x.Title.StartsWith(keyword));
var productStartWithWord = products.Where(x => x.Title.StartsWith(keyword + " "));
var productContainsWord = products.Where(x => x.Title.Contains(" " + keyword + " "));
var productContainsPhrase = products.Where(x => x.Title.Contains(keyword)
|| x.Title.Contains(keyword)
|| x.SubTitle.Contains(keyword)
|| x.OtherName.Contains(keyword));
var splitWords = keyword.Split(' ');
var productSplitWordSearch = splitWords.Aggregate(products, (current, word) => current.Where(x => x.Title.Contains(word.Trim())));
var p1 = productExactlyTitle.Select(x => new { Item = x, Order = 1 });
var p2 = productStartWithWord.Select(x => new { Item = x, Order = 2 });
var p3 = productStartWithPhrase.Select(x => new { Item = x, Order = 3 });
var p4 = productContainsWord.Select(x => new { Item = x, Order = 4 });
var p5 = productContainsPhrase.Select(x => new { Item = x, Order = 5 });
var p6 = productSplitWordSearch.Select(x => new { Item = x, Order = 6 });
var productList = p1
.Union(p2)
.Union(p3)
.Union(p4)
.Union(p5)
.Union(p6)
.OrderBy(x => x.Order)
.Take(21)
.AsEnumerable()
.Select(x => new ProductItemViewModel()
{
Id = x.Item.Id,
Title = x.Item.Title,
Price = DiscountController.ApplyDiscountToPrice(x.Item).ToPrice(),
Image = x.Item.Images.FirstOrDefault(y => y.IsCoverPhoto)?.ImageUrl
});
Result of above code have duplicate records and I have to use select and distinct to remove duplicate records. so I change my code like this:
var productList = p1
.Union(p2)
.Union(p3)
.Union(p4)
.Union(p5)
.Union(p6)
.OrderBy(x => x.Order)
.Select(x => x.Item)
.Distinct()
.Take(21)
.AsEnumerable()
.Select(x => new ProductItemViewModel()
{
Id = x.Id,
Title = x.Title,
Price = DiscountController.ApplyDiscountToPrice(x).ToPrice(),
Image = x.Images.FirstOrDefault(y => y.IsCoverPhoto)?.ImageUrl
});
But after that my result is sorted with Id column again.
How can I solved this?
First, since adding Order field to each query makes the record unique, using Union (which is supposed to remove duplicates) doesn't make sense, so simply use Concat instead.
Second, to remove duplicates and not lose the Order field needed for later ordering, you need to group by Item and take the minimum Order for each group. The rest is the same as in the original solution.
var productList = p1
.Concat(p2)
.Concat(p3)
.Concat(p4)
.Concat(p5)
.Concat(p6)
.GroupBy(e => e.Item)
.Select(g => new { Item = g.Key, Order = g.Min(e => e.Order) })
.OrderBy(e => e.Order)
.Select(e => e.Item)
.Take(21)
.AsEnumerable()
...
I'm currently defeated in my attempts to map the following sql to an EF lambda style query:
sql:
SELECT ResellerId, Name, Month, SUM(MonthTotal) AS MonthTotal
FROM dbo.View_ResellerYearMonthBase
WHERE (Year = 2015) AND (EventId > 0) AND (ValidationResponse IS NOT NULL)
GROUP BY ResellerId, Month, Name
I've tried
public JsonResult GetResellerAnnualReportData(int year, bool includeUnValidated, bool includeUnbooked)
{
var qry = _reportsDal.View_ResellerYearMonthBase.AsQueryable();
qry = qry.Where(x => x.Year == year);
if (!includeUnValidated) { qry = qry.Where(x => x.ValidationResponse.Length > 0); }
if (!includeUnbooked) { qry = qry.Where(x => x.EventId > 0); }
qry = qry.GroupBy(x => new { x.ResellerId, x.Month, x.Name }).Select(y => new ResellerAnnualReportDto
{
ResellerId = y.Key.ResellerId,
Month = y.Key.Month.Value,
Name = y.Key.Name,
SumMonthTotal = y.Sum(z => z.MonthTotal.Value)
});
throw new NotImplementedException();//keep the compiler happy for now
}
How should I go about achieving the SQL Query with the function parameters (year, includeUnValidated etc)
.GroupBy(key => new { key.ResellerId, key.Month, key.Name},
el => el.MonthTotal,
(key, el) => new ResellerAnnualReportDto
{
ResellerId = key.ResellerId,
Month = key.Month,
Name = key.Name,
MonthTotal = el.Sum(s => s.MonthTotal)
}).ToList();
This uses the overload with keyselector, elementselector and resultselector. This way you avoid making the IGrouping<key,value> and get the results you want immediately. Couldn't test though.
Here is how to do this:
var result = qry.GroupBy(x => new { x.ResellerId, x.Month, x.Name }).
Select(y => new {
y.Key.ResellerId,
y.Key.Month,
y.Key.Name,
SumMonthTotal = y.Sum(z => z.MonthTotal)
}).ToList();
I have a Banner with multiple Packs. Each pack has multiple files.
I have the following query:
List<BannerModel> models = context.Banners
.Select(x => x.Packs
.SelectMany(p => p.Files, (p, f) => new {
Id = p.Id,
Flag = p.Flag,
File = new { Id = f.Id, Flag = f.Flag, Key = f.Key, Mime = f.Mime }
})
.Where(a => a.File.Flag == "Img_200")
.Select(a => new BannerModel { PackId = a.Id, ImageKey = a.File.Key })
).ToList();
1) I get the error on "ToList()".
Cannot implicitly convert type 'System.Collections.Generic.List>' to 'System.Collections.Generic.List'
2) Then I removed the ToList and added "var models = ..."
I know there are 10 records where 5 of them satisfy the criteria:
.Where(a => a.File.Flag == "Img_200")
What is strange is that I get 10 items, 5 with data and 5 with no data.
Where I should only get a list of 5 items. The one that satisfy the criteria.
Could someone help me solving this problem?
Thank you,
Miguel
Should this be:
List<BannerModel> models = context.Banners
.SelectMany(x => x.Packs
.SelectMany(p => p.Files, (p, f) => new {
Id = p.Id,
Flag = p.Flag,
File = new { Id = f.Id, Flag = f.Flag, Key = f.Key, Mime = f.Mime }
})
.Where(a => a.File.Flag == "Img_200")
.Select(a => new BannerModel { PackId = a.Id, ImageKey = a.File.Key })
).ToList();
Task: use different where clause in one query
Here is example (it is not real query, just to illustrate the problem)
var events = ctx.Events; // ctx - EntityFramework context
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// how to get this clause from variable
.Where(ee => ee.State == gg.Key)
.Take(2)
})
.ToList();
Next code did not work, the problem is that where expression use parameter from query gg.Key
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// 1
// how to get this clause from variable
//.Where(ee => ee.State == gg.Key)
// 2
// try to take out where expression from query
.Where(_buildExpression(gg.Key))
.Take(2)
})
.ToList();
// method
static Expression<Func<Event, bool>> _buildExpression(string state)
{
return ee => ee.State == state;
}
// exeption
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.SqlServer.dll
Additional information: variable 'gg' of type 'System.Linq.IGrouping`2[System.String,Entities.Event]' referenced from scope '', but it is not defined
Example of getting where expression from variable, but does not depend on gg.Key (wrong)
Expression<Func<Event, bool>> whereClause = (ee) => (ee.State == "test");
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
// 1
// how to get this clause from variable
//.Where(ee => ee.State == gg.Key)
// 2
// try to take out where expression from query
//.Where(_buildExpression(gg.Key))
// 3
// whereClause from variable, but does not depend on gg.Key
.Where(whereClause)
.Take(2)
})
.ToList();
How to take where сlause from variable with depend on gg.Key?
p.s. the query is just example of the problem. The code below does not solve the problem of real query:
var events = ctx.Events;
var res = events
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = gg.Take(2)
})
.ToList();
Solution by OP.
Thanks to Ivan Stoev comment.
Expression<Func<Event, string, bool>> whereClause = (ee, state) => (ee.State == state);
var events = ctx.Events;
var res = events
.AsExpandable() // <= add this
.GroupBy(ee => ee.State)
.Select(gg => new
{
State = gg.Key,
FirstTwo = events
.Where(ee => whereClause.Invoke(ee, gg.Key)) // <= Invoke expression
.Take(2)
})
.ToList();
This was made possible by LinqKit