Exception while building a dictionary (Linq to SQL) - c#

Well I am trying to build a dictionary using linq to SQL. Not sure how to pick distinct values using the below query. The idea to is fetch the instances of a title between a date.
GolfitoDataContext db = new GolfitoDataContext();
var dic = db.GetTable<History>()
.Select(p => new { p.Title, p.Date }).Where(x => x.Date >= startDateFilter && x.Date <= endDateFilter)
.AsEnumerable()
.ToDictionary(k => k.Title, v => v.Date);
I get an exception that "An item with the same key has already been added."
I know its got to do with the "title" being repeated. But not sure how to apply the Distinct() method in the above condition to be able to build the dictionary. If I am doing something wrong, please correct me. Thanks!

This should work:
GolfitoDataContext db = new GolfitoDataContext();
var dic = db.GetTable<History>()
.Select(p => new { p.Title, p.Date }).Where(x => x.Date >= startDateFilter && x.Date <= endDateFilter)
.DistinctBy(p => p.Title)
.AsEnumerable()
.ToDictionary(k => k.Title, v => v.Date);

You can do this by using MoreLinQ
var dic = db.GetTable<History>()
.Select(p => new { p.Title, p.Date }).Where(x => x.Date >= startDateFilter && x.Date <= endDateFilter)
.DistinctBy(x=>x.Title)
.AsEnumerable()
.ToDictionary(k => k.Title, v => v.Date);

Related

How to iterate over a list to build a Linq query

I have the following working query:
posts.Where(post =>
post.Fields
.Where(x =>
x.RegionId == "RecipeArticleDetails" &&
(x.FieldId == "RecipePrepTime" || x.FieldId == "RecipeCookTime")
)
.GroupBy(x => x.PostId)
.Select(x => new { ID = x.Key, Value = x.Sum(y => Convert.ToInt32(y.Value)) })
.Where(x => x.Value > 10 && x.Value < 40)
.Any()
)
List<string> suppliedTimes = new List<string>(){
"10-60","0-10"
};
I would like to replace Where(x => x.Value > 10 && x.Value < 40) so it looks up from a list of ranges:
List<string> suppliedTimes = new List<string>(){
"10-60","0-10"
};
My understanding is I can use select to iterate over the items:
posts.Where(post =>
suppliedTimes.Select(x => new {low = Convert.ToInt32(x.Split("-",StringSplitOptions.RemoveEmptyEntries)[0]), high = Convert.ToInt32(x.Split("-",StringSplitOptions.RemoveEmptyEntries)[1]) })
.Any( a =>
post.Fields
.Where(x =>
x.RegionId == "RecipeArticleDetails" &&
(x.FieldId == "RecipePrepTime" || x.FieldId == "RecipeCookTime")
)
.GroupBy(x => x.PostId)
.Select(x => new { ID = x.Key, Value = x.Sum(y => Convert.ToInt32(y.Value)) })
.Where(x => x.Value > a.low && x.Value < a.high)
.Any()
)
)
However this code results in the error:
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Please can someone explain how I can achieve this and why what I have isn't working.
To make it work with EF Core I would suggest my extnsion FilterByItems and change the way how to retrieve records.
List<string> suppliedTimes = new List<string>(){
"10-60","0-10"
};
var ranges = suppliedTimes
.Select(x => x.Split("-", StringSplitOptions.RemoveEmptyEntries))
.Select(x => new {
low = Convert.ToInt32(x[0]),
high = Convert.ToInt32(x[1])
});
var fields = context.Fields
.Where(x =>
x.RegionId == "RecipeArticleDetails" &&
(x.FieldId == "RecipePrepTime" || x.FieldId == "RecipeCookTime")
)
.GroupBy(x => x.PostId)
.Select(x => new { ID = x.Key, Value = x.Sum(y => Convert.ToInt32(y.Value)) })
.FilterByItems(ranges, (e, r) => e.Value > r.low && e.Value < r.high, true);
var posts = posts
.Join(fields, p => p.Id, f => f.ID, (p, f) => p);

LINQ error: System.NotSupportedException on GroupBy Selection

When var items = q3.ToList(); executes from the code snippet below, it throws exception System.NotSupportedException. The aim is to get the list of items after the grouping.
Exception:
Unable to create a constant value of type 'AppDB.Stage.Rules'. Only primitive types or enumeration types are supported in this context.
var valuations = context.stage
.Where(q => q.stageID == stageID && !rules.ToList().Any(r => r.type1 == q.type1 || r.type2 == q.type2))
.GroupBy(q => q.stageKey)
.Select(g => g) ;
var q3 = valuations.Select(y => new StageType
{
TypeKey = y.Key,
TypeName= "UNKNOWN",
});
var items = q3.ToList(); //error here
Your database doesn't have any idea of what your in-memory rules actually is, and in-turn cant convert this statement to SQL
The simplest solution will be to leave it as an IQueryable and don't use ToList,
context.stage
.Where(q => q.stageID == stageID && !rules.Any(r => r.type1 == q.type1 || r.type2 == q.type2))
.GroupBy(q => q.stageKey)
.Select(g => g) ;
However, if it is already in memory, then you will have to send the values as a primitive list
var type1s = rules.Select(x => x.type1);
var type2s = rules.Select(x => x.type2);
context.stage
.Where(q => q.stageID == stageID && !type1s.Contains(q.type1) && !type2s.Contains(q.type2))
.GroupBy(q => q.stageKey)
.Select(g => g) ;
Because rules.ToList() makes results in memory, you can't use it inside an IQueryable that executes over SQL. You should first bring your data into memory and then narrow it by other in-memory object.
var valuations = context.stage.ToList()
.Where(q => q.stageID == stageID && !rules.ToList().Any(r => r.type1 == q.type1 || r.type2 == q.type2))
.GroupBy(q => q.stageKey)
.Select(g => g) ;
var q3 = valuations.Select(y => new StageType
{
TypeKey = y.Key,
TypeName= "UNKNOWN",
});
var items = q3.ToList();

Linq query to get the list filtered by max value in one query

Could you help me to improve this code?
var orderNo = "1234";
var maxValue = MyTable.Where(x => x.OrderNo == orderNo )
.OrderByDescending(x => x.Filename)
.Select(x => x.Filename)
.FirstOrDefault();
var list = (from x in MyTable
where x.OrderNo == orderNo && x.Filename == maxValue
select x).Distinct();
list.Dump();
Is it possible to improve these 2 Linq queries into 1 and avoiding query 2 times my database. Something like:
var list = (from x in MyTable
where x.OrderNo == orderNo && MaxValue(x.Filename)
select x)
.Distinct();
You could use GroupBy:
var list = MyTable
.Where(x => x.OrderNo == "1234") // or var1
.GroupBy(x => x.Filename)
.OrderByDescending(g => g.Key)
.First()
.Distinct();
Try
var list = (
from x in MyTable
where x.OrderNo == var1 &&
x.Filename == MyTable.Where(x => (x.OrderNo == "1234")).Max(p => p.Filename)
select x
).Distinct();
Why not just:
MyTable.Where(x => x.OrderNo == "1234")
.Max(x => x.Filename);
You will get all the records that have the maximal filename.

Building a list in a dictionary (LINQ to SQL)

I am try to build a dictionary with a list of dates as values.
I am not sure what extension method that I need to use inorder to get to the solution. Tried doing a ToList() on the value field but its throwing an exception.
Below is the code that I am using.
GolfitoDataContext db = new GolfitoDataContext();
var dic = db.GetTable<History>()
.Select(p => new { p.Title, p.Date})
.Where(x => x.Date >= startDateFilter && x.Date <= endDateFilter)
.DistinctBy(x => x.Title)
.AsEnumerable()
.ToDictionary(k => k.Title, k => k.Date);
For example for the below data
Date Title
2013-07-18 22:51:45.000 QA
2013-07-18 22:52:30.000 Controller
2013-07-18 22:52:30.000 Controller
2013-07-18 22:58:00.000 Agent
2013-07-18 23:07:00.000 QA
2013-07-18 23:07:45.000 Controller
2013-07-18 23:08:30.000 Planning
I am trying to build a dictionary which will give me all the instances of individual titles(QA,Controller,etc.) and their occurrences (date on which the instances occurred). Basically building a Dictionary<string,List<DateTime>>
You should use GroupBy:
var dic = db.GetTable<History>()
.Select(p => new { p.Title, p.Date})
.Where(x => x.Date >= startDateFilter && x.Date <= endDateFilter)
.GroupBy(x => x.Title)
.ToDictionary(g => g.Key, g => g.Select(x => x.Date).ToList());
The ToLookup method encompasses that:
GolfitoDataContext db = new GolfitoDataContext();
var dic = db.GetTable<History>()
.Select(p => new { p.Title, p.Date})
.Where(x => x.Date >= startDateFilter && x.Date <= endDateFilter)
.ToLookup(k => k.Title, k => k.Date);
A lookup is basically the same as a multi-map, and can be used for example as:
foreach(var date in dic[title])
{
// ...
}
you need to build the list before you try to build the dictionary. You can do this by use of group by
var dic = (from x in db.GetTable<History>()
where x.Date >= startDateFilter && x.Date <= endDateFilter
group x.Date by x.Title)
.ToDictionary(grp => grp.Key, grp.ToList());

How can I check if the integer value of a string is less than something in LINQ?

I have the following:
var topRole = 25;
var menuItems = _contentRepository.GetPk()
.Where(m => m.Status <= topRole)
.OrderBy(m => m.Order)
.Select(m => new MenuItem
Status has values of "00", "05" or "10"
Is there some way I can convert m.Status to an integer and then compare to see if it is less than or equal to topRole?
var menuItems = _contentRepository.GetPk()
.Where(m => int.Parse(m.Status) <= topRole)
.OrderBy(m => m.Order)
.Select(m => new MenuItem);
If this query is for LINQ to SQL, you may need to use Convert.ToInt32 instead:
var menuItems = _contentRepository.GetPk()
.Where(m => Convert.ToInt32(m.Status) <= topRole)
.OrderBy(m => m.Order)
.Select(m => new MenuItem);
use int.Parse(m.Status)
var menuItems = _contentRepository.GetPk()
.Where(m => int.Parse(m.Status) <= topRole)
.OrderBy(m => m.Order)
.Select(m => new MenuItem)
EDIT: changed "parse" to "Parse".

Categories