LINQ to Entities does not recognize the method 'Int32 Min(Int32, Int32)'? - linq

Im getting this error when I execute the following code, any Ideas how to fix it?
LINQ to Entities does not recognize the method 'Int32 Min(Int32, Int32)' method, and this method cannot be translated into a store expression.
result = items.ToList()
.Select(b => new BatchToWorkOnModel()
{
BatchID = b.Batch.ID,
SummaryNotes = b.Batch.Notes,
RowVersion = b.Batch.RowVersion,
Items = items
.Select(i => new ItemToWorkOnModel()
{
SupplierTitle = i.Title,
ItemID = i.ID,
BatchID = i.BatchID ?? 0,
ItemDate = i.PubDate,
// KB - Issue 276 - Return the correct Outlet name for each item
Outlet = i.Items_SupplierFields != null ? i.Items_SupplierFields.SupplierMediaChannel != null ? i.Items_SupplierFields.SupplierMediaChannel.Name : null : null,
Status = ((short)ItemStatus.Complete == i.StatusID ? "Done" : "Not done"),
NumberInBatch = i.NumInBatch,
Text = string.IsNullOrEmpty(i.Body) ? "" : i.Body.Substring(0, Math.Min(i.Body.Length, 50)) + (i.Body.Length < 50 ? "" : "..."),
IsRelevant = i.IsRelevant == 1,
PreviouslyCompleted = i.PreviouslyCompleted > 0 ? true : false
}).ToList()
})
.FirstOrDefault();

It seems Math.Min is not implemented by the EF query provider. You should be able to fix it by simply applying AsEnumerable on your items collection to do the expression using Linq to Objects instead;
Items = items.AsEnumerable().Select(i => new ItemToWorkOnModel()...
If you add a where condition to the item selection (seems a little strange to take all items in the whole table), you'll want to add it before AsEnumerable() to allow EF to do the filtering in the database.
Also, you only want the first result from the query, but you're fetching all of them using ToList() before cutting the list down to a single item. You may want to remove the ToList() so that EF/the underlying database can return only a single result;
result = items.Select(b => new BatchToWorkOnModel()...

You do not need Math.Min.
The line in question is:
Text = string.IsNullOrEmpty(i.Body)
? "" : i.Body.Substring(0, Math.Min(i.Body.Length, 50)) + (i.Body.Length < 50 ? "" : "...")
So what does this line return?
If i.Body is null or empty it returns an empty string. If it is 50 or more characters long it returns a substring of 50 characters and appends "...".
If the length is less than 50 it takes a substring with the length of the string and appends an empty string. But that's just the original string.
Text = string.IsNullOrEmpty(i.Body)
? "" : (i.Body.Length < 50 ? i.Body : i.Body.Substring(0, 50) + "...")

Related

Func<T, TResult> with OrderBy resulting in error with null fields

I've inherited some code, and also a bug.
The app is a C# MVC website using EF. The VIEW presents clients in a table (taken straight from a CLIENTS table in a SQL Server database).
List<Client> _clients = db.Client.ToList();
IEnumerable<Client> filteredClients;
The problem arises when the user clicks a header to sort by. The args passed to the controller indicate an index of the field to sort by (sortColumnIndex).
The original dev created a func to handle the translation from the index to the field.
Func<Client, string> orderingFunction = (c => sortColumnIndex == 1 ? c.Name.ToString()
: sortColumnIndex == 2 ? c.AccountExec.ToString()
: sortColumnIndex == 3 ? c.SalesforceLink.ToString()
: sortColumnIndex == 4 ? c.Location.ToString()
: sortColumnIndex == 5 ? c.PrimaryContact.ToString()
: sortColumnIndex == 6 ? c.AccountId.ToString()
: sortColumnIndex == 6 ? c.MongoClientId.ToString()
: ""); // index 0 is the hidden ClientId
The results of this are used in the OrderBy() clause.
filteredClients = filteredClients.OrderBy(orderingFunction);
When the field being sorted on has complete data (i.e. no NULL values), it works fine. As soon as a column has a NULL value, however, the resulting OrderBy throws a "Object reference not set to an instance of an object" error.
I'm afraid I'm not completely up to the task of deciphering the solution here; we still need to sort on the field selected by the user, even if all but one of the records have a NULL value. Is there any way to achieve this with the existing code structure, or is this better served by refactoring?
EDIT: full code up to the point of exception:
"param" is the argument that contains all of the filters and such.
List<Client> _clients = db.Client.ToList();
IEnumerable<Client> filteredClients;
//Check for filters. This is a search, and we can say it's empty for this purpose
if (!string.IsNullOrEmpty(param.sSearch))
{
var nameFilter = Convert.ToString(Request["bSearch_1"]); // Search string
var isNameSearchable = Convert.ToBoolean(Request["bSearchable_1"]); // Is column searchable? Optional
filteredClients = _clients.Where(x => x.Name.ToLower().Contains(param.sSearch.ToLower()));
}
else
{
filteredClients = _clients.OrderBy(x => x.Name);
}
// Sort Column
var isNameSortable = Convert.ToBoolean(Request["bSortable_1"]);
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
Func<Client, string> orderingFunction = (c => sortColumnIndex == 1 ? c.Name.ToString()
: sortColumnIndex == 2 ? c.AccountExec.ToString()
: sortColumnIndex == 3 ? c.SalesforceLink.ToString()
: sortColumnIndex == 4 ? c.Location.ToString()
: sortColumnIndex == 5 ? c.PrimaryContact.ToString()
: sortColumnIndex == 6 ? c.AccountId.ToString()
: ""); // index 0 is the hidden ClientId
// Sort Direction
var sortDirection = Request["sSortDir_0"]; // asc or desc
if (sortDirection == "asc")
{
//Results of this line generate exception
filteredClients = filteredClients.OrderBy(orderingFunction);
}
else
{
filteredClients = filteredClients.OrderByDescending(orderingFunction);
}
Well! Thanks to johnny 5's wondering why the c.AccountId.ToString() was in use, I pulled the .ToString() from the func<>, and that seemed to do the trick.
Thanks, johnny 5! Sometimes you just need another set of eyes!

Set Null if no Results Returned EntityFramework

I get a Word from the database and printout its "Text" using ViewBag in my ASP.NET MVC3 EntityFramework project.
ViewBag.ManagementSystems = db.Words.Where(w => w.WordBaseID == 1 && w.LanguageID == lang).FirstOrDefault().Text;
However, if no results are returned, I got null exception and program crashes. What is the simplest and best way of printing nothing if no results are returned?
Solutions I know of:
1- Surround with if's or try-catch blocks
2- Use
var query = "SELECT Text FROM Words WHERE WordBaseID = {0} AND LanguageID = {1}";
ViewBag.ManagementSystems= db.Database.SqlQuery<string>(query, 1, lang).FirstOrDefault();
I propose:
ViewBag.ManagementSystems = db.Words.Where(w => w.WordBaseID == 1 && w.LanguageID == lang)
.Select(x => x.Text).FirstOrDefault();

LINQ query returning null results

I have the following code
nodes = data.Descendants(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Results")).Nodes();
System.Collections.Generic.IEnumerable<Result> res = new List<Result>();
if (nodes.Count() > 0)
{
var results = from uris in nodes
select new Result
{
URL =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Url")).Value,
Title =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Title")).Value,
Description =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Description")).Value,
DateTime =
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}DateTime")).Value,
};
res = results;
}
Where Results is a object who has those URL, Title, Description, and DateTime variables defined.
This all works fine normally, but when a 'node' in nodes doesnt contain a Description element (or at least I think thats whats throwing it) the program hits the "res = results;"
line of code and throws a 'object reference not set to...' error and highlights the whole section right after "select new Results"..
How do I fix this?
The simplest way is to cast to string instead of using the Value property. That way you'll end up with a null reference for the Description instead.
However, your code can also be made a lot nicer:
XNamespace ns = "http://schemas.microsoft.com/LiveSearch/2008/04/XML/web";
var results = data.Descendants(ns + "Results")
.Elements()
.Select(x => new Result
{
URL = (string) x.Element(ns + "Url"),
Title = (string) x.Element(ns + "Title"),
Description = (string) x.Element(ns + "Description"),
DateTime = (string) x.Element(ns + "DateTime")
})
.ToList();
See how much simpler that is? Techiques used:
Calling ToList() on an empty sequence gives you a list anyway
This way you'll only ever perform the query once; before you were calling Count() which would potentially have iterated over each node. In general, use Any() instead of Count() > 0) - but this time just making the list unconditional is simpler.
Use the Elements() method to get child elements, rather than casting multiple times. (Your previous code would have thrown an exception if it had encountered any non-element nodes)
Use the implicit conversion from string to XNamespace
Use the +(XNamespace, string) operator to get an XName
If the Description element is not included you should test if this
((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Description"))
is not null before using Value. Try this code:
var results = from uris in nodes let des = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Description"))
select new Result
{
URL = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Url")).Value,
Title = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}Title")).Value,
Description = (des != null) ? des.Value : string.Empty,
DateTime = ((XElement)uris).Element(XName.Get("{http://schemas.microsoft.com/LiveSearch/2008/04/XML/web}DateTime")).Value,
};

How to make a linq Sum return null if the summed values are all null

I have a LINQ query that looks like this...
var duration = Level3Data.AsQueryable().Sum(d => d.DurationMonths);
If all the d.DurationMonths values are null the Sum returns 0. How can I make the Sum return null if all the d.DurationMonths are null? Or do I need to run a separate query first to eliminate this situation before performing the sum?
Along with the previous suggestion for an extension method - you could use a ternary operator...
var duration = Level3Data.AsQueryable().Any(d => d.DurationMonths.HasValue)
? Level3Data.AsQueryable().Sum(d => d.DurationMonths)
: null;
You can use Aggregate to provide custom aggregation code :
var items = Level3Data.AsQueryable();
var duration = items.Aggregate<D,int?>(null, (s, d) => (s == null) ? d.DurationMonths : s + (d.DurationMonths ?? 0));
(assuming the items in Level3Data are of type D)
var outputIndicatorSum = (from OutputIndicatorTable in objDataBaseContext.Output_Indicators
where OutputIndicatorTable.Output_Id == outputId
select (int?)OutputIndicatorTable.Status).Sum();
int outputIndicatorSumReturn = Convert.ToInt32(outputIndicatorSum);
return outputIndicatorSumReturn;
You can explicitly type cast non-nullable varaible into nullable type.
i.e, select (int?)OutputIndicatorTable.Status).Sum();
Using Sum alone, this is impossible. As you indicated in your question, you will need to check for this situation before you call Sum:
var q = Level3Data.AsQueryable();
var duration = q.All(d => d.DurationMonths == null)
? null
: q.Sum(d => d.DurationMonths);
If you would like the result without two queries try:
var duration = Level3Data.AsQueryable().Sum(d => (double?)d.DurationMonths);
If you want zero instead of null as the result of this query use:
var duration = Level3Data.AsQueryable().Sum(d => (double?)d.DurationMonths) ?? 0;

Linq Error: InvalidOperationException: Could not translate expression

Get value out of DateTime column
if null to return String.Empty
else
DateTime.ToShortDateString
What am I doing wrong => query produced below:
var queryable = from p in Products
select new {
selldate = p.SellEndDate == null
? string.Empty
: p.SellEndDate.Value.ToShortDateString() };
Error: InvalidOperationException: Could not translate expression 'Table(Product).Select(p => new <>f__AnonymousType01(selldate = IIF((p.SellEndDate = null), Invoke(value(System.Func1[System.String])), p.SellEndDate.Value.ToShortDateString())))' into SQL and could not treat it as a local expression.
Basically what's happening here is that LINQ to SQL is taking your entire query and trying to convert it into something that SQL Server can understand. The problem, though, is that SQL Server has no concept of DateTime.ToShortDateString, so the conversion to SQL fails.
You'll have to change your query so that it just selects SellEndDate (which will get it as a Nullable<DateTime>) and then when you use the results of that query you can do the conversion to string. For example:
var list = (from p in Products
select p.SellEndDate).ToList();
// calling ToList() above means we have the entire resultset in memory and
// no longer have to pass the query back to SQL Server
var stuff = from p in list select new
{
selldate = p.SellEndDate == null ?
string.Empty :
p.SellEndDate.Value.ToShortDateString()
};
ToShortDateString doesn't seem to have equivalent SQL translation.
Use ToString instead.
If the date time field allows nulls:
from order in repository.Order
select order.OrdShipDate == null ? "" : order.OrdShipDate.GetValueOrDefault(DateTime.Now).Month.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Day.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Year.ToString();
If the date time field doesn't allow nulls:
from order in repository.Order
select order.OrdShipDate.Month.ToString() + "/" + order.OrdShipDate.Day.ToString() + "/" + order.OrdShipDate.Year.ToString();

Resources