The method Distinct is not supported - entity-framework-4

I am using Linq to Entities and am getting this error
The method Distinct is not supported
On this line of code
var typeIds = _context.AttributeValues.Select(av => av.AttributeTypeId).Distinct();
Why is this?

As per MSDN few LINQ methods are not supported which using OData service. Below is a partial list of unsupported methods.
All
Any
Concat
DefaultIfEmpty
Distinct
Except
Intersect
Union
Zip
For a full list of unsupported operations see here
However, as a workaround following statement should work in this case.
var typeIds = _context.AttributeValues
.Select(av => av.AttributeTypeId)
.AsEnumerable()
.Distinct();

A solution is to define a WCF Data Service using (server-side) the QueryByCube method provided by my product AdaptiveLINQ (www.adaptivelinq.com). This method transforms a projection (expressed by the select operator) in an aggregation.
You have just to define a cube with one dimension : AttributeTypeId
av => av.AttributeTypeId
Define a Data Service providing the queryable collection:
yourDbContext.AttributeValues.QueryByCube(yourCube);
Now you can query your service using the OData protocol:
http://.../myService.svc/AttributeValues?$select=AttributeTypeId
Or, if your using a C# client:
serviceContext.AttributeValues.Select(x => x.AttributeTypeId);
The advantage of this solution relative to the workaround proposed A J Qarshi is that the distinction is made on the server side.

Related

Using navigation property in orderby clause of breeze query

I am using breeze to call a web api method which is:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => sa.Appeal).Include("Case");
My breeze query looks like:
var query = EntityQuery.from('GetEditShipmentAppeals')
.withParameters({ shipmentID: shipmentID, caseID: caseID })
.orderByDesc("Case.ID")
GetEditShipmentAppeals is a web api method that contains the first query. In spite of using .Include("Case") in the query I am not able to use "Case.ID" in the order by clause of breeze query.
var query = EntityQuery.from('Appeals')
.expand("Case,Patient")
.orderByDesc("Case.ID").inlineCount();
Even if I use navigation property on a breeze query that does not involve a EF query in web api, it does not work. In above query Case is a navigation property in Appeal table.
If I understand your example correctly, then I think that this is an Entity Framework issue. My understanding is that Entity Framework does not support "Includes" on a projection. See http://connect.microsoft.com/VisualStudio/feedback/details/347543/entity-framework-eager-loading-not-working-in-some-projection-scenarios.
To confirm this, I would try executing your EF query in isolation and see if the "Include" is actually doing anything. My guess is that it isnt.
However, you can still accomplish what you want with a slightly different server side projection. ( I'm not sure what object 'Case' is a property of and the syntax may be a bit off but...) Something like:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => new
{ Appeal: sa.Appeal, Case: sa.Appeal.Case, CaseId: sa.Appeal.Case.Id });
Note that Breeze will return a collection of 'anonymous' javascript objects from this query, but each of the 'entities' within each of these objects (i.e. Appeals and Cases) will be full Breeze entities and will be part of the EntityManager cache.

LINQ custom function in .where

can I, and if I can, how, write LINQ statement like this:
public IQueryable<Advert> SearchSimilarAdverst(string query)
{
Levenshtein compute = new Levenshtein();
return _db.Adverts.Where(a => a.IsActive &&
(compute.FindSimilarity(a.Name, query) <= 2));
}
Thanks
EDIT
I've tired solution Jeffery suggested and it worked but when I've tried this line of code I got EntityCommandExecutionException, does anybody know why ?
adverts.Where(a => a.WhoAmILookingForTags.Any
(t => compute.FindSimilarity(t.Name,query) <= 2));
Tags and Adverts are connected with many to many relation, and WhoAmILookingForTags is list of tags
EF will not be able to translate compute.FindSimilarity(a.Name, query) to SQL, so you're going to have to take the performance hit and do the following.
public IEnumerable<Advert> SearchSimilarAdverst(string query)
{
Levenshtein compute = new Levenshtein();
var adverts = _db.Adverts.Where(a => a.IsActive).ToList();
return adverts.Where(a => compute.FindSimilarity(a.Name, query) <= 2));
}
Note the return type of the method also needed to be changed to reflect the return type
As commented below, the query should be forced to run before filtering using FindSimilarity because of delayed execution. query.ToList() is one of many options.
In short, no - because EF needs to be able to convert your Linq predicate into T-SQL (or whatever dialect of SQL your RDBMS uses). Only a subset of .NET BCL functions are supported (such as String.Contains and custom user code is right-out).
For complicated predicates I recommend writing your own SQL by hand - you'll also get considerably better performance, EF can be slow at generating SQL.
I hope I'm wrong on this, but I do not believe that you would be allowed to that in a Linq-to-Entities query. In those kinds of LINQ queries it has to translate everything in the Where function into a SQL statement to be run on the underlying database. Since your method is not something that is on the SQL side of things, it will cause an exception when you try this.
For a regular LINQ query (i.e. after everything has already been put into memory), there should be no problem with accomplishing this.
You could also try it and see if it does or does not work...

LINQ join in VB.NET return concrete type rather than anonymous

I have the following LINQ query in VB.NET
Using db As New ReablementLSQLDataContext
query = (From b In db.Visits
Join c In db.LinkStaffToVisits On b.ID Equals c.VisitID
Where c.StaffID = staffid And b.StartEpoch = newepochdatetime).ToList()
End Using
When I run this, it returns a list of type anonymous which means its pretty useless if I want to access any of the data in it. How do I run this join statement and return a list of a concrete type?
Anonymous types as query results aren't so "useless", since you can always foreach over them (locally). Still, if you want a concrete type, you can add a select statement at the end and project the anonymous result into your type (supposed that you made this type and know which fields to use where), like (C# syntax)
var newQuery = query.Select(anon_x => new YourType(anon_x.field1, anon_x.field2, ...))
You can use a generic version of ToList method. I am a C# developer and can provide syntax related to C# :
.ToList<YourType>();
For VB.Net version read : http://msdn.microsoft.com/en-us/library/bb342261.aspx#Y0

Entity SQL Sort Then LINQ to Entities GroupBy Doesn't Return IOrderedQueryable

Works: My UI sends up entity sql sort expressions from a jqGrid to my DAL and then applies some Where clauses that get returned to my service layer. My service layer will then create a PaginatedList object that applies the Skip()..Take() to the IQueryable.
Example:
var qry = ((IObjectContextAdapter)DbContext).ObjectContext
.CreateQuery<TEntity>(entityName)
.OrderBy(pEntitySQLSort.GetEntitySQL());
//GetEntitySQL() i.e. "it.WorksheetID ASC"
return qry.Where(p=> pStatus == "blah").Skip(5).Take(10);
Doesn't Work: Applying a GroupBy() then Select() that returns a list of the same type of entities (Worksheet).
Example:
var qry = ((IObjectContextAdapter)DbContext).ObjectContext
.CreateQuery<TEntity>(entityName)
.OrderBy(pEntitySQLSort.GetEntitySQL());
var qryGrouped = qry.GroupBy(pWorksheet => pWorksheet.ParticipantID)
.Select(pGroup => new {Group = pGroup, LatestWorksheetID = pGroup.Max(pWorksheet => pWorksheet.WorksheetID)})
.Select(p => p.Group.FirstOrDefault(pWorksheet => pWorksheet.WorksheetID == p.LatestWorksheetID));
return qryGrouped.Skip(5).Take(10); //throws exception.
Throws NotSupportedException: The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
It seems to me that the first snippet does return an IOrderedQueryable that applies the esql sorting expression but the second snippet does not? Or maybe does GroupBy() remove the ordering of a query/collection ? If this is is the case, and since esql must be applied BEFORE LINQ to Entities, how could I accomplish the sql sorting + LINQ GroupBy ?
Related:
When is ObjectQuery really an IOrderedQueryable?
Why can't we mix ESQL and LINQ TO Entities
GroupBy() returns an IGrouping<int, Worksheet>. The actual object is an ObjectQuery returned as IQueryable. You linked to my question (When is ObjectQuery really an IOrderedQueryable?), so you know that the fact that this ObjectQuery also implements IOrderedQueryable does not necessarily mean that it actually behaves as such.
In a more elementary, related, question, Jon Skeet made the distinction between actual type and compile-time type. The compile-time type (IQueryable) is what matters.
So, GroupBy effectively cancels the previous OrderBy. In a quick test on a similar case I could see that even the ordering is gone in the grouping. Conclusion: you'll have to re-apply an OrderBy for the Skip to be executed successfully.

ASP.NET MVC LINQ query

When I use the query
var NewMatchs = (from x in entity.matches select x).LastOrDefault();
I get error like this
LINQ to Entities does not recognize the method 'SocialSports.Models.match
LastOrDefaultmatch'
method, and this method cannot be
translated into a store expression.
What's wrong in my code ???
Thanks...
LastOrDefault is not supported in Linq to Entities. You can achieve the same using the following:
var lastmatch = (from x in entity.matches select x)
// Assuming you have some kind of timestamp
.OrderByDescending(s => s.Date)
.FirstOrDefault();
You can't use LastOrDefault to query EF entities, as it cannot be translated to T-SQL.
Not all the LINQ methods are supported by Linq to Entities.
List of ALL supported / not supported methods:
http://msdn.microsoft.com/en-us/library/bb738550.aspx
You could try
var NewMatchs = (from x in entity.matches select x).ToList().LastOrDefault();
but this will load ALL matches from the db and perform Linq to objects.
Or try sorting and call FirstOrDefault.
See The query operator 'LastOrDefault' is not supported

Resources