Strange exception in Linq-to-SQL - c#

I developed the extension method:
public static IQueryable<T> Get<T>(this DataContext dataContext) where T : class
{
return dataContext.GetTable<T>();
}
Then I tried to use it:
dataContext.Get<XDbCurrency>().
Where(c => c.Id == XCurrencyId.USD).
SelectMany(
c =>
dataContext.Get<XDbCurrencyRate>().
Where(cr =>
cr.CurrencyId == c.Id &&
cr.StartOn <= DateTime.UtcNow &&
cr.EndOn > DateTime.UtcNow).
Take(1),
(c, cr) => cr).
ToArray();
I get the exception Member access 'XCurrencyId CurrencyId' of 'X.XDbCurrencyRate' not legal on type 'System.Data.Linq.Table``1[X.XDbCurrencyRate]. When I change Get back to GetTable - everything works! When I change the return type of Get from IQueryable<T> to Table<T> - it crashes.
UPDATE. I created XDataContextBase class, moved Get method there, inherited DBML-generated XDataContext from XDataContextBase - unfortunately it does not help. Magic!!
UPDATE. this.GetTable<XDbCurrencyRate> works, this.XDbCurrencyRates works, this.Get<XDbCurrencyRate> not works either Get is extension method or method in base class. By the way, internally this.XDbCurrencyRates is implemented absolutely like my Get. Magic!!
UPDATE. Looks like Linq-to-SQL supports only two things: either direct call of DataContext.GetTable<> or call of any-named property that returns System.Data.Linq.Table<XDbCurrencyRate>. If I create System.Data.Linq.Table<XDbCurrencyRate> Foo - it works, but when I create IQueryable<XDbCurrencyRate> Foo - it crashes with Queries with local collections are not supported.. Magic!!
Any ideas?

This is because the initial Get call is not part of the query tree. Just a normal method call. The second call is embedded in the tree and LINQ to SQL has no idea what that method means.
I know that EF would give up here immediately. I believe L2S is basically capable of running such a method locally and trying to inline the query that it returns. Here, that seems to now work out (or I'm wrong about that).
What I did was to create a custom LINQ provider that rewrites expression trees to execute such methods locally, inline their results into the query tree and then forward to LINQ to SQL. This is not trivial.
Maybe you can make AsExpandable from the so called "LINQ kit" (approximate spelling) work.

Related

Is Converting between a subtype interface and its base type interface and back again, a bad thing to do?

I have an example where I want a IQueryable to convert to an IEnumerable (in order to use a multi-statement lambda), and then I immediately want to convert back to a IQueryable to use an extension method that requires IQueryable.
This is within a LINQ query if that matters?
My question is whether this can be time or space intensive? Or, more plainly, should I avoid it for any reason?
Here is the use case of this scenario:
var messageTimelines = ctx.UserMessageTimelines.AsEnumerable()
.Where(w =>
{
var intersectCount = w.MessageTimelineParticipants.Intersect(timelineParticipants).Count();
return ((intersectCount == w.MessageTimelineParticipants.Count()) && (intersectCount == timelineParticipants.Count()))
? true : false;
}).AsQueryable().ProjectTo<MessageTimeline>(mapper.ConfigurationProvider);
As you can see I started off as a IQueryable and converted to 'AsEnumerable()' in order to utilize a multi-statement lambda. I then converted back to 'AsQueryable()' in order to use an AutoMapper extension method called 'ProjectTo', which requires 'IQueryable'.
To Note:
This post almost answers my question, but doesn't speak of converting back to the sub-type IQueryable: does converting IQueryable to IEnumerable execute the query again?

Reflection in Linq

This line of code:
db.Set<T>().Max(d => d.GetType().GetProperty("DateTimePropertyName").GetValue(d))
causes this exception:
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method
How do I make it work?
Your trying to run c# code that's suppose to get translated into SQL somehow and can't.
This is a good guide to start with.
You have two options:
1.Fetch the data from the DB and go through it with LINQ to Objects - Probally the easiest and not the best way to do things since some queries can return large collections.
2.Try to find a better way to do what you are doing. Why would you want this reflection code to run? What's the purpose? Is the DateTimePropertyName non public? If so why? Otherwise something like this should work:
db.Set<T>().Max(d => d.DateTimePropertyName);
By default it assumes that you pass Expression<Func<T,TResult>>, but what you need is to pass Func<T,TResult>:
Func<T, object> f = p => p.GetType().GetProperty("DateTimePropertyName").GetValue(p);
_context.Set<T>().Max(f);
It comes from IEnumerable, so it will hit performance if you table is large

Often used LINQ returned from method

I have 1 LINQ which used so much. I try create the method which return this LINQ like:
public static System.Linq.Expressions.Expression<Func<MyEntity, bool>> GetFilteredEntity() {
return x => true/*Some condition*/;
}
public static Func<MyEntity, bool> GetFilteredEntity() {
return x => true/*Some condition*/;
}
And use this like
db.MyEntities.Where(GetFilteredEntity());
is successfull, But! I need use it like
db.ParentEntities.Where(entity => entity.MyEntities.Where(GetFilteredEntity()));
This code compiled too, but every time when i use it, i got the Error:
System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
,even:
db.ParentEntities.Where(entity => entity.MyEntities.Where(GetFilteredEntity())).ToList();
throw this Exception too.
But,
db.ParentEntities.Where(entity => entity.MyEntities.Where(x => true/*Some condition*/))
still works fine!
So why it happend, and have some way to get round this?
Final working code
public static Expression<Func<MyEntity, bool>> GetFilteredEntity() {
return x => true/*Some condition*/;
}
and
var expression = GetFilteredEntity();
db.ParentEntities.Where(entity => entity.MyEntities.AsQueryable().Where(expression ));
Also .AsQueryable() thanks to Passing func as parameter in Linq to Entities and 'Internal .NET Framework Data Provider error 1025' error
In your first example the function is called and translated into an expression before it is even sent to the query provider. In the next two examples the function call is embedded within an expression that is sent to the query provider, and that query provider doesn't know what to do with that function call, so it just throws an exception. When you embed the actual expression in another expression, there is no function call to confuse the query provider.
As for a solution, just pull out the function call into a variable. The query provider is smart enough to see that you used a closed over variable, and will pull out its value. For a function call it's just not sure if it should evaluate it or try to translate it into something that should be done on the DB's end. Trying to do some of both would just be very confusing and hard to work with, both for the query provider, and for people using it. To simplify matters, function calls with expressions are never executed prior to sending the query. As for a closed over variable, there is no other way that it could be treated, so there isn't any other behavior to confuse it with.
var expression = GetFilteredEntity();
db.ParentEntities.Where(entity => entity.MyEntities.Where(expression ));
Looks like LazyLoading might be the culprit, have you tried popping the ToList() on the parameters?
db.ParentEntities.Where(entity => entity.MyEntities.Where(GetFilteredEntity()).ToList());

DbSortClause expressions must have a type that is order comparable parameter Name :Key

I am using Linq to entity and have the following query
IQueryable<DomainModel.User> userResult =
userResult.OrderBy(u => u.UserClientRoles.OrderBy(r => r.Role.RoleName));
But I am getting this error
DbSortClause expressions must have a type that is order comparable
parameter Name :Key
and it returns an empty collection.
Any idea what's going on?
.OrderBy(), when working with databases, is supposed to take in a delegate that returns only a single property that represents a column in your database. I'm not sure what you're trying to do, but it looks like
u.UserClientRoles.OrderBy(r => r.Role.RoleName)
Will return an enumeration of values, which can't be ordered.
I had the same problem, I solved it using this:
your code:
IQueryable<DomainModel.User> userResult = userResult.OrderBy(u => u.UserClientRoles.OrderBy(r => r.Role.RoleName));
my code:
List<Membership> results = new List<Membership>();
results.AddRange(memberships.OrderBy(m => m.Roles));
memberships = results.AsQueryable();
coincidences:
*.OrderBy(m => m.Roles)
solution:
*.OrderBy(m => m.Roles.Select(r => r.RoleId).FirstOrDefault())
possible problem's reason:
Maybe, you did what I did, and cause that 1 user/member could have more than 1 role in the same membership. That made a conflict with/to OrderBy() because the application can just "order" a single element at the time, when she call the Role (which is an ICollection of elements) the instead receive more than 1 element with no kind of priority's levels (even when we could assume that the application will take the role's index as priority's base level, actually its don't).
solution's explaination:
When you add the *.Select(r => r.RoleId), you are specifying to the application which element will be used to OrderBy(). But, as you shall see when you maybe reached at this point, just by using the *.Select(r => r.RoleId) could be not enough, because the application is still receiving multiple results with the same priority's level. Adding *.Select(r => r.RoleId).FirstOrDefault() you are basically saying: "...I don't care how many results you received from that element, just the focus on the first result, or order them by its default..." (default normally means EMPTY or NULL).
additional information:
I used non-official's simple concepts/meanings to explain a complex solution with simple words, which means that you could maybe have problems to find similar posts in the web by using the words/concepts used in this "answer". Otherwise, the code itself works and you shouldn't not have any problem by applying it and/or modifying it by yourself. GOOD LUCK!!! (^_^)
In my case, I was accidentally trying to order by an object instead of ordering by one of it's properties.
You should you use
var query = from Foo in Bar
orderby Foo.PropertyName
select Foo;
Instead of
var query = from Foo in Bar
orderby Foo
select Foo;
Note: you will get the same behaviour event if there is an override on Foo's ToString() method.

Why does this code generate a NotSupportedException?

Why does this throw System.NotSupportedException?
string foo(string f) { return f; }
string bar = "";
var item = (from f in myEntities.Beer
where f.BeerName == foo(bar)
select f).FirstOrDefault();
Edit: Here's an MSDN reference that (kind of) explains things...
Any method calls in a LINQ to Entities
query that are not explicitly mapped
to a canonical function will result in
a runtime NotSupportedException
exception being thrown. For a list of
CLR methods that are mapped to
canonical functions, see CLR Method to
Canonical Function Mapping.
See also http://mosesofegypt.net/post/LINQ-to-Entities-what-is-not-supported.aspx
EDIT: Okay, the code blows up because it doesn't know what to do with the call to foo(). The query is built up as an expression tree which is then converted to SQL.
The expression tree translator knows about various things - such as string equality, and various other methods (e.g. string.StartsWith) but it doesn't know what your foo method does - foo() is a black box as far as it's concerned. It therefore can't translate it into SQL.
The second version will fail as soon as you try to iterate over it. You can't use a locally defined method in an IQueryable<> where clause (of course, you can, but it will fail when the LINQ provider tries to translate it into SQL).
Because in the 2nd query no actual query is executed. Try adding ToList() where SingleOrDefault() is.
It's probably because the SQL-generation functionality isn't able to determine what to do with your foo() function, so can't generate output for it.

Categories

Resources