NotSupportedException thrown after call to .AsEnumerable() - linq

I have some simple code that retrieves recorded ELMAH exceptions from a db:
HealthMonitoringEntities context = new HealthMonitoringEntities();
IQueryable<ELMAH_Error> exceptions = context.ELMAH_Error;
if (filter.ToDate != null)
exceptions = exceptions.Where(e => e.TimeUtc <= filter.ToDate.Value.AddHours(-4));
return exceptions.OrderByDescending(e => e.TimeUtc)
.Take(filter.Size)
.AsEnumerable()
.Select(e => new ElmahException()
{
ErrorId = e.ErrorId,
Application = e.Application,
Host = e.Host,
Type = e.Type,
Source = e.Source,
Error = e.Message,
User = e.User,
Code = e.StatusCode,
TimeStamp = e.TimeUtc.AddHours(-4).ToString()
}).ToList();
}
I get an exception on this line:
TimeStamp = e.TimeUtc.AddHours(-4).ToString()
The exception is:
LINQ to Entities does not recognize the method 'System.DateTime AddHours(Double)' method, and this method cannot be translated into a store expression.
When I call .AsEnumerable() before projecting with Select(), my sequence is enumerated and I project from a sequence that implements IEnumerable<ELMAH_Error>. Given that, why am I not working with the Linq-To-Objects API in my projection, which understands AddHours(), instead of still working with the Linq-To-Entities API?
UPDATE
There is a post on this topic by Jon Skeet here:
http://msmvps.com/blogs/jon_skeet/archive/2011/01/14/reimplementing-linq-to-objects-part-36-asenumerable.aspx
He has this query:
var query = db.Context
.Customers
.Where(c => some filter for SQL)
.OrderBy(c => some ordering for SQL)
.Select(c => some projection for SQL)
.AsEnumerable() // Switch to "in-process" for rest of query
.Where(c => some extra LINQ to Objects filtering)
.Select(c => some extra LINQ to Objects projection);
Note that after his call to AsEnumerable(), he indicated he is switching over to Linq-To-Objects. I am doing something similar in my function, but I am receiving a Linq-To-Entities exception where I had thought I would be executing against the Linq-To-Objects API.
Further Update
From Jim Wooley's blog: http://linqinaction.net/blogs/jwooley/archive/2009/01/21/linq-supported-data-types-and-functions.aspx
"As an example the following methods are shown as having translations for DateTime values: Add, Equals, CompareTo, Date, Day, Month, Year. In contrast methods like ToShortDateString, IsLeapYear, ToUniversalTime are not supported.
If you need to use one of the unsupported methods, you need to force the results to the client and evaulate them using LINQ to Objects at that point. You can do that using the .AsEnumerable extension method at any point in the query comprehension."
Is that not what I'm doing?

You have forgotten to protect your first call
if (filter.ToDate != null)
exceptions = exceptions.AsEnumerable()
.Where(e => e.TimeUtc <= filter.ToDate.Value.AddHours(-4));
Edit
Remember, IQueryables are not translated to sql until they are enumerated, so your debugger will execute that line but the error won't occur until you return. If filter.ToDate is null your code is equivalent to this:
if(filter.ToDate == null)
return exceptions
.Where(e => e.TimeUtc <= filter.ToDate
.Value
.AddHours(-4)) //uh-oh won't work in Linq-to-Entities
.OrderByDescending(e => e.TimeUtc)
.Take(filter.Size)
.AsEnumerable() //too late to protect you!
.Select(e => new ElmahException()
{
ErrorId = e.ErrorId,
Application = e.Application,
Host = e.Host,
Type = e.Type,
Source = e.Source,
Error = e.Message,
User = e.User,
Code = e.StatusCode,
TimeStamp = e.TimeUtc.AddHours(-4).ToString() //if we get this far we're OK
}).ToList();

Related

String extension method in Linq query

How to use string extension method in linq query:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name.ToFriendlyUrl() ==
name.ToFriendlyUrl());
return newsType;
}
Above query x.Name.ToFriendlyUrl() is not allowed at the minute. Is anyone know how to achieve with it.
Extension methods are indeed allowed in LINQ queries, moreover the LINQ methods themselves are implemented as extension methods.
It's quite another issue however, to use extension methods (or most other methods) in LINQ-to-SQL or LINQ-to-Entities queries. Those queries are not actually run in the C# code, but they are treated like expressions that are translated to SQL. I.e.
db.News.Where(x => x.Published).Select(x => x.Name)
is translated to the SQL Statement
Select Name
From News
Where Published = 1
and it's results are returned to the C# code.
Since there is not way to transfer the ToFriendlyUrl() method to SQL, your code throws an error.
You have basically, two solutions/workarounds. One is to transform the call to a form could be translated into SQL, e.g. if the ToFriendlyUrl() method was just:
public static string ToFriendlyURL(this string value)
{
return value.ToLower();
}
you can inline that code in the LINQ call, and that would work. If however, the methods is more complex, than your only solution is to just fetch the data from the base and then process it on the C# side:
var newsTypeQuery = db.NewsTypes.Where(x => // other conditions, if any);
var newsTypes = newsTypes.ToList(); //forces execution of the query
// the result is now a C# list
var newsType = newsTypes.FirstOrDefault(x =>
x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
Assuming the NewsTypes is an IQueryable this is a result of Entity Framework not being able to convert you extension method into SQL (how should it?). Unless you can rewrite your predicate into something that Entity Framework can translate into SQL you will have to perform the query client side:
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.AsEnumerable().FirstOrDefault(x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
return newsType;
}
Notice how AsEnumerable() has been added before FirstOrDefault. Unfortunately this may pull all the rows returned by NewsTypes from the server to client and thus may be quite costly.
This
var newsType = db.NewsTypes.FirstOrDefault(
x => x.Name.ToFriendlyUrl() == name.ToFriendlyUrl());
can't be done in Entity Framework. ToFriendlyUrl is an extension method. It's something that is in the "client" computer. The query will be executed on the SQL server. The SQL server doesn't have a ToFriendlyUrl function.
The "standard" solution is to save in a second column named FriendlyName a precalculated version of the ToFriendlyUrl(), so your query becomes:
var friendlyName = name.ToFriendlyUrl();
var newsType = db.NewsTypes.FirstOrDefault(
x => x.FriendlyName == friendlyName);
Instead try like this
public NewsType GetNewsType(string name)
{
var newsType = db.NewsTypes.FirstOrDefault(x => x.Name == name).ToFriendlyUrl();
return newsType;
}

Enumeration yielded no result?

I am trying to filter some objects using linq to enitites and I get an error telling me "Enumeration yielded no results".
on the client side I get a message like this:
The operation cannot be completed because the DbContext has been
disposed
I know that these filter values should return some results but it just doesnt work, so Im guessing my query is wrong, can you help please.
var mediaChannels =
NeptuneUnitOfWork.MediaChannels
.FindWhere(m => m.CountryID == CountryID &&
m.SonarMediaTypeID == MediaTypeID &&
m.SonarMediaTypes.SonarMediaGroupID == MediaGroupID &&
m.Name.Contains(search))
.Select(m => new MediaChannelModel() {
ID = m.ID,
Name = m.Name,
MediaType = m.MediaType.Name,
Country = m.Countries.Name,
SubRegion = m.Countries.Lookup_SubRegions.Name,
Region = m.Countries.Lookup_SubRegions.Lookup_Regions.Name
});
My guess is that this runs just fine, then you dispose you context, then you try to access mediaChannels. The problem is that Linq uses deferred execution. Therefore, you query doesn't really execute until you enumerate mediaChannels, which is after you context is disposed.
If you don't want to use deferred execution, then add a .ToList() to the end of your query to force it to load right there.
If you want to use deferred execution, then you can't dispose of your context until a later point.
The operation cannot be completed because the DbContext has been disposed is often seen if you send data to the client without saving the data to memory. This can be easily fixed by .ToList()-ing your query before sending it to the page
var mediaChannels = NeptuneUnitOfWork.MediaChannels
.Where(m => m.CountryID == CountryID
&& m.SonarMediaTypeID == MediaTypeID &&
&& m.SonarMediaTypes.SonarMediaGroupID == MediaGroupID
&& m.Name.Contains(search))
.Select(m => new MediaChannelModel() {
ID = m.ID,
Name = m.Name,
MediaType = m.MediaType.Name,
Country = m.Countries.Name,
SubRegion = m.Countries.Lookup_SubRegions.Name,
Region = m.Countries.Lookup_SubRegions.Lookup_Regions.Name
}).ToList(); // <<-- NOTE this additional method

could not resolve property (complex properties)

I have a asp.net mvc application with NHibernate and I do not know how to resolve a problem to query some data. I have this query:
// create query
var query = session.QueryOVer<Laudo>().Fetch(x => x.Equipament).Eager;
// add some filters
if (idEquipament.HasValue)
query = query.And(x => x.Equipament.Id == idEquipament.Value);
//I got the error here...
if (idCompany.HasValue)
query = query.And(x => x.Equipament.Company.Id == idCompany.Value);
When I try to execute this query, I've got an exception with this message:
"could not resolve property: Equipament.Company.Id of: DomainModel.Laudo"
what can I do to fix this problem?
Thanks
You cannot use another entity property like that. NHibernate expects expression that can be evaluated to property of the current entity. You need to use JoinQueryOver or JoinAlias to join another entity, and perform where after that.
With JoinQueryOver:
// ...
query = query.JoinQueryOver(x => x.Equipment)
.JoinQueryOver(x => x.Company)
.Where(c => c.Id == idCompany.Value);
With JoinAlias:
Equipment equipment = null;
Company company = null;
// ...
query = query.JoinAlias(x => x.Equipment, () => equipment)
.JoinAlias(() => equipment.Company, () => company)
.Where(() => company.Id == idCompany.Value);
Some more info:
What is the difference between JoinQueryOver and JoinAlias?
What can be used as a NHibernate QueryOver alias?
Complex nHibernate QueryOver expression
The tags chosen for your question make me think you didn't want to use QueryOver, but LINQ.
This is achieved by using the extension method Query, in the NHibernate.Linq namespace:
var query = session.Query<Laudo>().Fetch(x => x.Equipament);
if (idEquipament.HasValue)
query = query.Where(x => x.Equipament.Id == idEquipament.Value);
if (idCompany.HasValue)
query = query.Where(x => x.Equipament.Company.Id == idCompany.Value);

LINQ to Entities complex query

Is it possible ...??? I have 4 DropDownLists on my main page and the
user may select from any, all or some of
the DropDownLists. I am capturing their selection (or non-selection) using a SESSION
variable. What I would like to be able to do is pass the session
variable values to my Data Access Layer and build a WHERE clause
(maybe using StringBuilder) and then place that variable SOMEHOW into
my query expression. Is that possible??? Sorry, I'm a newbie. Thanks ~susan~
public class DLgetRestaurants
{
FVTCEntities db = new FVTCEntities();
public List<RESTAURANT> getRestaurants(string cuisineName, string priceName, string cityName)
[Build a string based on the values passed to the function]
{
var cuisineID = db.CUISINEs.First(s => s.CUISINE_NAME == cuisineName).CUISINE_ID;
List<RESTAURANT> result = (from RESTAURANT in db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price")
where **[USE STRINGBUIDER EXPRSSION HERE]**
select RESTAURANT).ToList();
return result;
}
}
You can compose Where conditions which are linked by a logical AND relatively easy in LINQ extension method syntax:
var query = db.RESTAURANTs.Include("CITY").Include("CUISINE").Include("Price");
if (userHasSelectedInDDL1)
query = query.Where(r => r.PropertyForDDL1 == ValueFromDDL1);
if (userHasSelectedInDDL2)
query = query.Where(r => r.PropertyForDDL2 == ValueFromDDL2);
if (userHasSelectedInDDL3)
query = query.Where(r => r.PropertyForDDL3 == ValueFromDDL3);
if (userHasSelectedInDDL4)
query = query.Where(r => r.PropertyForDDL4 == ValueFromDDL4);
List<RESTAURANT> result = query.ToList();
For a much more flexible solution to build queries dynamically the Dynamic LINQ Library recommended by boca is probably the better choice.
I have done this in the past using the Dynamic Linq Library.

NH Linq with FetchMany and ToFutureValue in NH 3.0.0beta

I'm trying to use the ToFuture with the new NH Linq 3.0 provider.
This works fine:
var result = ses.Query<Parent>()
.Where(x => x.Id == id)
.ToFutureValue();
but when I use a Fetch/FetchMany like this:
var result = ses.Query<Parent>()
.Where(x => x.Id == id)
.Fetch(x => X.Child)
.ToFutureValue();
This exception occurs:
NotSupportedException with: You can
also use the AsFuture() method on
NhQueryable
Any suggestions?
It's probably not implemented yet (LINQ Futures is one of the most recently added features, just a few weeks ago).
You can enter an issue at http://jira.nhforge.org/
.Fetch(x => X.Child)
return IQueryable<>, not NhQueryable<>
that's why we got exception
Take a look:
http://www.symbolsource.org/Public/Metadata/Default/Project/NHibernate/3.0.0.GA/Release/All/NHibernate/NHibernate/Linq/LinqExtensionMethods.cs

Resources