Got a dictionary with the persons Id as a key. And each value is a List with a class that contains a datetime.
I want to get all contracts from the database where each date in the list is in between the contracts from and until -date. There could be multiple for each person.
I can't use .Any() function. If I do I'll get this error: " Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator."
This is an example with the .Any method that doesn't work with linq to sql. And I need an other way to manage this.
public class SimpleObject
{
public bool Test { get; set; }
public DateTime DateTime { get; set; }
}
private void Test(Dictionary<int, List<SimpleObject>> dataBaseObjDictionary)
{
using (var db = TpContext.Create())
{
var selectedObj = db.Contracts.Where(x => dataBaseObjDictionary.ContainsKey(x.PersonRef) && dataBaseObjDictionary.Values.Any(y => y.Any(a => x.FromDate <= a.DateTime) && y.Any(a=>a.DateTime >= x.UntilDate)));
}
}
I think this should do it. It looks like you were relying on y to be the same with the two y.anys. In addition, you were checking if a was greater than until date and greater than from date so I fixed those.
var selectedObj = db.Contracts.Where(x => dataBaseObjDictionary.ContainsKey(x.PersonRef)
&& dataBaseObjDictionary.Values.Any(
y => y.Any(a => x.FromDate <= a.DateTime
&& a.DateTime <= x.UntilDate)
)
);
Related
I need to search in sql server database table. I am using IQueryable to build a dynamic query like below
var searchTerm = "12/04";
var samuraisQueryable = _context.Samurais.Include(x => x.Quotes).AsQueryable();
samuraisQueryable = samuraisQueryable.Where(x => x.Name.Contains(searchTerm) ||
x.CreatedDate.HasValue && x.CreatedDate.Value.ToString()
.Contains(searchTerm)
var results = samuraisQueryable.ToList();
The above query is just an example, actual query in my code is different and more complicated.
Samurai.cs looks like
public class Samurai
{
public Samurai()
{
Quotes = new List<Quote>();
}
public int Id { get; set; }
public string Name { get; set; }
public DateTime? CreatedDate { get; set; }
public List<Quote> Quotes { get; set; }
}
The data in the table looks like
I don't see any results becasue the translated SQL from the above query converts the date in a different format (CONVERT(VARCHAR(100), [s].[CreatedDate])). I tried to specify the date format in the above query but then I get an error that The LINQ expression cannot be translated.
samuraisQueryable = samuraisQueryable.Where(x => x.Name.Contains(searchTerm) ||
x.CreatedDate.HasValue && x.CreatedDate.Value.ToString("dd/MM/yyyy")
.Contains(searchTerm)
If (comments) users will want to search partially on dates, then honestly: the best thing to do is for your code to inspect their input, and parse that into a range query. So; if you see "12/04", you might parse that into a day (in the current year, applying your choice of dd/mm vs mm/dd), and then do a range query on >= that day and < the next day. Similarly, if you see "2021", your code should do the same, but as a range query. Trying to do a naïve partial match is not only computationally expensive (and hard to write as a query): it also isn't very useful to the user. Searching just on "2" for example - just isn't meaningful as a "contains" query.
Then what you have is:
(var startInc, var endExc) = ParseRange(searchTerm);
samuraisQueryable = samuraisQueryable.Where(
x => x.CreatedDate >= startInc && x.CreationDate < endExc);
I know that Linq cannot handle ToString() and I've read a few work arounds, most seem to be doing the casting outside of the Linq query, but this is for the output where I am trying to shove it into a list and this is where it is blowing up.
As the code will show below I did some casting elsewhere already in order to make it fit in Linq, but the very last part has the tostring and this I need to rewrite too but I'm not sure how.
DateTime edate = DateTime.Parse(end);
DateTime sdate = DateTime.Parse(start);
var reading = (from rainfall in db.trend_data
join mid in db.measurements on rainfall.measurement_id equals mid.measurement_id
join siteid in db.sites on mid.site_id equals siteid.site_id
where siteid.site_name == insite && rainfall.trend_data_time >= sdate && rainfall.trend_data_time <= edate
select new GaugeData() { SiteID = siteid.site_name, Data_Time = rainfall.trend_data_time, Trend_Data = float.Parse(rainfall.trend_data_avg.ToString()) }).ToList();
Linq will handle it, however Linq2Entities will not since EF will want to relay that expression to the DbProvider which doesn't understand/translate all .Net methods.
When it comes to extracting data from entities, the path with the least pain is to have your entity definitions should use the compatible .Net types matching the SQL data types. Then when you want to load that data into view models / DTOs where you might want to perform formatting or data type translation, let the ViewModel/DTO handle that or pre-materialized your Linq2Entity query into an anonymous type list and then process the translations /w Linq2Object.
Without knowing the data type of your TrendDataAvg, an example with a value stored as a decimal, but you want to work with float:
Formatting in ViewModel example:
public class TrendData // Entity
{ // ...
public decimal trend_data_avg { get; set; }
// ...
}
public class GuageData // ViewModel
{
public decimal trend_data_avg { get; set; } // Raw value.
public float Trend_Data // Formatted value.
{
get { return Convert.ToSingle(trend_data_avg); }
}
}
var reading = (from rainfall in db.trend_data
join mid in db.measurements on rainfall.measurement_id equals mid.measurement_id
join siteid in db.sites on mid.site_id equals siteid.site_id
where siteid.site_name == insite && rainfall.trend_data_time >= sdate && rainfall.trend_data_time <= edate
select new GaugeData() { SiteID = siteid.site_name, Data_Time = rainfall.trend_data_time, trend_data_avg = rainfall.trend_data_avg }).ToList();
Anonymous Types Example:
public class GuageData // ViewModel
{
public float Trend_Data { get; set; }
}
var reading = (from rainfall in db.trend_data
join mid in db.measurements on rainfall.measurement_id equals mid.measurement_id
join siteid in db.sites on mid.site_id equals siteid.site_id
where siteid.site_name == insite && rainfall.trend_data_time >= sdate && rainfall.trend_data_time <= edate
select new
{
siteid.site_name,
rainfall.trend_data_time,
rainfall.trend_data_avg
}.ToList() // Materializes our Linq2Entity query to POCO anon type.
.Select( x=> new GaugeData
{
SiteID = site_name,
Data_Time = trend_data_time,
Trend_Data = Convert.ToSingle(trend_data_avg)
}).ToList();
Note: If you use the Anonymous Type method and want to utilize paging, additional filtering, etc. then be sure to do it before the initial .ToList() call so that it is processed by the Linq2EF. Otherwise you would be fetching a much larger set of data from EF than is necessary with potential performance and resource utilization issues.
Additionally, if you set up your navigation properties in your entities you can avoid all of the explicit join syntax. EF is designed to do the lifting when it comes to the relational DB, not just an alternate syntax to T-SQL.
// Given trend data has a single measurement referencing a single site.
var gaugeData = db.trend_data
.Where(x => x.trend_data_time >= sdate
&& x.trend_data_time <= edate
&& x.measurement.site.site_name == insite))
.Select(x => new
{
x.measurement.site.site_name,
x.trend_data_time,
x.trend_data_avg
}).ToList()
.Select( x=> new GaugeData
{
SiteID = site_name,
Data_Time = trend_data_time,
Trend_Data = Convert.ToSingle(trend_data_avg)
}).ToList();
You could use the Convert.ToSingle() method, float is an alias for system.single.
Trend_Data = Convert.ToSingle(rainfall.trend_data_avg)
My project is using MVC 4 C# LINQ to SQL.
For some reason the method used to get data for one of my properties is giving a "has no supported translation to SQL" error. The method to fetch this data is nearly identical to the method of another property in the same query except the one that works grabs a string where the one that doesn't gets a decimal.
Exact error code:
Method 'System.Decimal GetModelDifficulty(System.String)' has no supported translation to SQL.
I've tried numerous variations on the below code but I always get the same error as above:
public List<ProductionSchedule> GetBaseProductionSchedule(DateTime selectedDate)
{
var spaList = (from x in db.WO010032s
join y in db.MOP1042s on x.MANUFACTUREORDER_I equals y.MANUFACTUREORDER_I into x_y
where x.STRTDATE == selectedDate && (x.MANUFACTUREORDERST_I == 2 || x.MANUFACTUREORDERST_I == 3)
from y in x_y.DefaultIfEmpty()
select new ProductionSchedule()
{
MO = x.MANUFACTUREORDER_I,
BOMNAME = x.BOMNAME_I,
SpaModel = x.ITEMNMBR,
MoldType = GetMoldType(x.ITEMNMBR.Trim()),
SerialNumber = y.SERLNMBR,
Difficulty = GetModelDifficulty(x.ITEMNMBR.Trim())
}).OrderByDescending(x => x.Difficulty).ToList();
return spaList;
}
public string GetMoldType(string model)
{
return db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.MoldType).FirstOrDefault();
}
public decimal GetModelDifficulty(string model)
{
return (decimal)db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.Difficulty).FirstOrDefault();
}
Well I've tweaked the code around enough times to where I've stumbled on a variation that works:
public List<ProductionSchedule> GetBaseProductionSchedule(DateTime selectedDate)
{
var spaList = (from x in db.WO010032s
join y in db.MOP1042s on x.MANUFACTUREORDER_I equals y.MANUFACTUREORDER_I into x_y
where x.STRTDATE == selectedDate && (x.MANUFACTUREORDERST_I == 2 || x.MANUFACTUREORDERST_I == 3)
from y in x_y.DefaultIfEmpty()
select new ProductionSchedule()
{
MO = x.MANUFACTUREORDER_I,
BOMNAME = x.BOMNAME_I,
SpaModel = x.ITEMNMBR,
MoldType = GetMoldType(x.ITEMNMBR.Trim()),
SerialNumber = y.SERLNMBR,
Difficulty = GetModelDifficulty(x.ITEMNMBR)
}).ToList();
return spaList.OrderByDescending(x => x.Difficulty).ToList();
}
public string GetMoldType(string model)
{
return db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.MoldType).FirstOrDefault();
}
public decimal GetModelDifficulty(string model)
{
decimal difficulty = (String.IsNullOrEmpty(model)) ? 0M : Convert.ToDecimal(db.SkuModelDatas.Where(x => x.Value == model.Trim()).Select(x => x.Difficulty).FirstOrDefault());
return difficulty;
}
Why it worked when trapping for null string for x.ITEMNMBR (model parameter) in one method and not the other and needing to OrderByDescending outside of the main LINQ query, I have no idea.
Thanks for all the suggestions and help with this.
The problem is your query is calling code that LINQ cannot translate into SQL.
First try this, it may help. There may be a problem with your (decimal) cast. Modify your method GetModelDifficulty to the following:
public decimal GetModelDifficulty(string model)
{
return Convert.ToDecimal(db.SkuModelDatas.Where(x => x.Value == model).Select(x => x.Difficulty).FirstOrDefault());
}
If that doesn't work I'm afraid you'll have to break your query down further to narrow down the issue. Use the documentation provided here: Standard Query Operator Translation (LINQ to SQL) to be sure that any extension methods you are using have supported translations.
If you run into a piece of code that cannot be translated, you may need to declare a separate variable with the necessary value already stored inside of it, that you can then use inside of your query.
I think it's because your call to FirstOrDefault() can return a null value. When you assign to a typed object you can use ? operator to signify that it can be null:
decimal? myDec = <some code returning decimal value or null>
Then you can check if myDec is null or not by:
myDec.HasValue
I'm trying to filter by date based on two datetimes. MinTime and MaxTime are both nullable DateTimes in SQL, I want to filter based on these, but I'm getting an error:
This function can only be invoked from LINQ to Entities
public static IEnumerable<ExclusionRule> GetExclusionRules(AppointmentTypes? apptType, string resourceName, TimeSpan? minTime, TimeSpan? maxTime, DayOfWeek? day) {
using (var db = new BusinessObjectContainer()) {
db.ExclusionRules.MergeOption = MergeOption.NoTracking;
var items = db.ExclusionRules;
int intDay = day.HasValue ? (int) day.Value : -1,
intApptType = apptType.HasValue ? (int)apptType.Value : -1;
var filteredApptType = apptType.HasValue ? items.Where(i => !i.t_AppointmentType.HasValue|| i.t_AppointmentType == intApptType) : items;
var filteredResource = string.IsNullOrWhiteSpace(resourceName) ? filteredApptType : filteredApptType.Where(i => !string.IsNullOrEmpty(i.ResourceName) && resourceName.ToLower().Equals(i.ResourceName.ToLower()));
IEnumerable<ExclusionRule> filteredMinDate;
if (minTime.HasValue) {
filteredMinDate = filteredResource.Where(i => (!i.MinTime.HasValue) || (EntityFunctions.CreateTime(i.MinTime.Value.Hour, i.MinTime.Value.Minute, 0) >= minTime.Value));
} else {
filteredMinDate = filteredResource;
}
IEnumerable<ExclusionRule> filteredMaxDate;
if (maxTime.HasValue) {
// this throws the exception
filteredMaxDate = filteredMinDate.Where(i => (!i.MaxTime.HasValue) || EntityFunctions.CreateTime(i.MaxTime.Value.Hour, i.MaxTime.Value.Minute, 0) <= maxTime.Value);
} else {
filteredMaxDate = filteredMinDate;
}
var filteredWeekDay= day.HasValue ? filteredMaxDate.Where(i => !i.t_DayOfWeek.HasValue|| i.t_DayOfWeek == intDay) : filteredMaxDate;
return filteredWeekDay.ToList();
You are using EntityFunctions* in a linq statement on an IEnumerable<ExclusionRule>. But you can only use it on an IQueryable that has an Entity Framework query provider. So you should start with IQueryable<ExclusionRule> (from EF) or just create DateTimes in the regular.Net way.
*DbFunctions as of Entity Framework 6.
MinTime.Value.Hour etc. might have to be executed in a Query and it seems to me that you are excecuting them in .Net memory. That's why you get the error.
LINQ to entities:
http://msdn.microsoft.com/en-us/library/bb386964.aspx
This function can only be invoked from LINQ to Entities
EntityFunctions is applicable is LINQ to Entities queries only.
The EntityFunctions class contains methods that expose canonical functions to use in LINQ to Entities queries. http://msdn.microsoft.com/en-us/library/dd456873.aspx
I got around this by calling .ToList() before the DateTime filtering. This pulls everything into .NET so the querying can be done there.
As there are a relatively small number of items in my database this wasn't a problem.
I need to be able to build a query at runtime that uses OR statments. If I use the method below to build the query, everything is AND together. I really need each filter value to be OR in order for this query to work correctly.
public class IdAndRole
{
public string Id {get;set;}
public string Role {get;set;}
}
var idAndRoles = session.Query<IdAndRole, Roles_ById>();
foreach(var filter in filterValues)
{
idAndRoles = idAndRoles.Where(x => x.In(filter.Id) && x.In(filter.Role));
}
Pseudocode:
(filter[0].Id == value1 && filter[0].Role == role1) ||(filter[1].Id == value2 && filter[1].Role == role2)
You should be able to use a PredicateBuilder to construct the query.
var predicate = PredicateBuilder.False<IdAndRole>();
foreach (var filter in filterValues)
{
predicate = predicate.Or( x => x.In(filter.Id) && x.In(filter.Role) );
}
var idAndRoles = session.Query<IdAndRole,Roles_byId>()
.Where( predicate );
Phil,
You can drop down into the LuceneQuery, and that allows you fine grained control over your query.