I saw this code work with LINQ to SQL but when I use Entity Framework, it throws this error:
LINQ to Entities does not recognize the method 'System.Linq.IQueryable'1[MyProject.Models.CommunityFeatures] GetCommunityFeatures()' method, and this method cannot be translated into a store expression.`
The repository code is this:
public IQueryable<Models.Estate> GetEstates()
{
return from e in entity.Estates
let AllCommFeat = GetCommunityFeatures()
let AllHomeFeat = GetHomeFeatures()
select new Models.Estate
{
EstateId = e.EstateId,
AllHomeFeatures = new LazyList<HomeFeatures>(AllHomeFeat),
AllCommunityFeatures = new LazyList<CommunityFeatures>(AllCommFeat)
};
}
public IQueryable<Models.CommunityFeatures> GetCommunityFeatures()
{
return from f in entity.CommunityFeatures
select new CommunityFeatures
{
Name = f.CommunityFeature1,
CommunityFeatureId = f.CommunityFeatureId
};
}
public IQueryable<Models.HomeFeatures> GetHomeFeatures()
{
return from f in entity.HomeFeatures
select new HomeFeatures()
{
Name = f.HomeFeature1,
HomeFeatureId = f.HomeFeatureId
};
}
LazyList is a List that extends the power of IQueryable.
Could someone explain why this error occurs?
Reason:
By design, LINQ to Entities requires the whole LINQ query expression to be translated to a server query. Only a few uncorrelated subexpressions (expressions in the query that do not depend on the results from the server) are evaluated on the client before the query is translated. Arbitrary method invocations that do not have a known translation, like GetHomeFeatures() in this case, are not supported.
To be more specific, LINQ to Entities only support Parameterless constructors and Initializers.
Solution:
Therefore, to get over this exception you need to merge your sub query into the main one for GetCommunityFeatures() and GetHomeFeatures() instead of directly invoking methods from within the LINQ query. Also, there is an issue on the lines that you were trying to instantiate a new instance of LazyList using its parameterized constructors, just as you might have been doing in LINQ to SQL. For that the solution would be to switch to client evaluation of LINQ queries (LINQ to Objects). This will require you to invoke the AsEnumerable method for your LINQ to Entities queries prior to calling the LazyList constructor.
Something like this should work:
public IQueryable<Models.Estate> GetEstates()
{
return from e in entity.Estates.AsEnumerable()
let AllCommFeat = from f in entity.CommunityFeatures
select new CommunityFeatures {
Name = f.CommunityFeature1,
CommunityFeatureId = f.CommunityFeatureId
},
let AllHomeFeat = from f in entity.HomeFeatures
select new HomeFeatures() {
Name = f.HomeFeature1,
HomeFeatureId = f.HomeFeatureId
},
select new Models.Estate {
EstateId = e.EstateId,
AllHomeFeatures = new LazyList<HomeFeatures>(AllHomeFeat),
AllCommunityFeatures = new LazyList<CommunityFeatures>(AllCommFeat)
};
}
More Info: Please take a look at LINQ to Entities, what is not supported? for more info.
Also check out LINQ to Entities, Workarounds on what is not supported for a detailed discussion on the possible solutions.
(Both links are the cached versions because the original website is down)
Related
I have a Table-Valued function which I would like to use as an IQueryable in a LINQ statement. I have created the following in my DbContext class:
[DbFunction("dbo","MyTableValuedFunction")]
public virtual IQueryable<MyClass> MyFunction(string keyword)
{
var keywordsParam = new System.Data.Entity.Core.Objects.ObjectParameter("keywords", typeof(string))
{
Value = keyword
};
return (this as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext
.CreateQuery<MyClass>("dbo.MyTableValuedFunction(#keywords)", keywordsParam);
}
The result of dbo.MyTableValuedFunction(#keywords) match an existing class "MyClass". An example of how I'd like to use this function:
MyClass example = (from a in dbContext.MyClass
join b in dbContext.MyFunction("exampleKeyword")
on a.Id equals b.Id
join c in dbContext.MyOtherClass
on a.SomeId equals c.Id
select a);
...but enumerating this IEnumerable throws an exception:
'dbo.MyTableValuedFunction' cannot be resolved into a valid type or function.
I have tried reducing the function to just one column, and changing the type to <int>, but this doesn't work, so I am not sure what is happening here; it's like the TVF is not being correctly found/recognised/used?
I have also tried following https://weblogs.asp.net/Dixin/EntityFramework.Functions#Table-valued_function and on the off-chance there's some subtle difference, I create the function with:
[Function(FunctionType.TableValuedFunction, "MyTableValuedFunction", Schema = "dbo")]
...but I run into exactly the same problem.
First if you used this extension, you should write following code according to your code:
[DbFunction("dbo","MyTableValuedFunction")]
public virtual IQueryable<MyClass> MyFunction(string keyword)
{
var keywordsParam = new System.Data.Entity.Core.Objects.ObjectParameter("keywords", typeof(string))
{
Value = keyword
};
return (this as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext
.CreateQuery<MyClass>("[Your DbContext Name].MyFunction(#keywords)", keywordsParam);
}
According to the extension codes, the EF uses conventions to translate the C# syntax into sql syntax. for this reason, you should register your functions in dbContext first as the link mentioned.
I have problem with creating complex query with entity framework. I would like to fetch additional data into my linq entity based on parameters given during construction of such query. Here is example with where:
if (featureEnabled)
{
query = query.Where(n => *condition*);
}
I have complex object created like that:
n => new Entity{
Property = n.Something
\* ... *\
PropertyN = n.SomethingN,
}
and I want to load additional data into entity if feature is enabled (just like in where example):
public DoSomething(bool featureEnabled, feature2Enabled, etc.)
{
return n => new Entity{
Property = n.Something,
\* ... *\
PropertyN = n.SomethingN,
Feature = (featureEnabled) ? *fetch some data from navigation property* : 0,
Feature2 = (feature2Enabled) etc.
}
}
In above example parameters (featureNEnabled) will be translated into sql parameters. How to perform such operation at query construction time?
did you mean inside the initializer you want use the if condition?
if its so i will have its not possible. To use the if condition you have to put it outside the initializer
var a = new MyClass{
prop1 = n.prop1,
prop2 = n.prop2,
prop3 = n.prop3,
};
a.propN = boolCondition ? n.PropN : 0;
I finally found answer to my question on this blog
With this code you may call expression.Merge(expression2) and initialization lists of two objects will be merged into one query.
I sum myself to the hapless lot that fumbles with custom methods in LINQ to EF queries. I've skimmed the web trying to detect a pattern to what makes a custom method LINQ-friendly, and while every source says that the method must be translatable into a T-SQL query, the applications seem very diverse. So, I'll post my code here and hopefully a generous SO denizen can tell me what I'm doing wrong and why.
The Code
public IEnumerable<WordIndexModel> GetWordIndex(int transid)
{
return (from trindex in context.transIndexes
let trueWord = IsWord(trindex)
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
}
public string IsWord(transIndex trindex)
{
Match m = Regex.Match(trindex.word, #"^[a-z]+(\w*[-]*)*",
RegexOptions.IgnoreCase);
return m.Value;
}
With the above code I access a table, transIndex that is essentially a word index of culled from various user documents. The problem is that not all entries are actually words. Nubers, and even underscore lines, such as, ___________,, are saved as well.
The Problem
I'd like to keep only the words that my custom method IsWord returns (at the present time I have not actually developed the parsing mechanism). But as the IsWord function shows it will return a string.
So, using let I introduce my custom method into the query and use it as a grouping parameter, the is selectable into my object. Upon execution I get the omninous:
LINQ to Entities does not recognize the method
'System.String IsWord(transIndex)' method, and this
method cannot be translated into a store expression."
I also need to make sure that only records that match the IsWord condition are returned.
Any ideas?
It is saying it does not understand your IsWord method in terms of how to translate it to SQL.
Frankly it does not do much anyway, why not replace it with
return (from trindex in context.transIndexes
let trueWord = trindex.word
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
What methods can EF translate into SQL, i can't give you a list, but it can never translate a straight forward method you have written. But their are some built in ones that it understands, like MyArray.Contains(x) for example, it can turn this into something like
...
WHERE Field IN (ArrItem1,ArrItem2,ArrItem3)
If you want to write a linq compatible method then you need to create an expresion tree that EF can understand and turn into SQL.
This is where things star to bend my mind a little but this article may help http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx.
If the percentage of bad records in return is not large, you could consider enumerate the result set first, and then apply the processing / filtering?
var query = (from trindex in context.transIndexes
...
select new WordIndexModel
{
Word,
Instances = grouped.Select(test => test.trans).Distinct()
});
var result = query.ToList().Where(word => IsTrueWord(word));
return result;
If the number of records is too high to enumerate, consider doing the check in a view or stored procedure. That will help with speed and keep the code clean.
But of course, using stored procedures has disadvatages of reusability and maintainbility (because of no refactoring tools).
Also, check out another answer which seems to be similar to this one: https://stackoverflow.com/a/10485624/3481183
I'm on a interop scenario, and because of that I'm dealing with structures and classes used like structures in different assemblies - so a cast is not enough and have to do manually field-by-field which is very boring and error prone.
So I devised an function that does copy the great deal of simple fields/properties and I deal only with the troubled ones.
When I do this to properties only, it works ok. But I need now how to fix this LiNQ query to be able to get a list of fields from the source object and join them with the properties on the target object.
Code below:
var TypeOrig = pSource.GetType();
var TypeDest = pTarget.GetType();
var TypeString = typeof(System.String);
var PropOrig = TipoOrig.GetFields(); // if it is GetProperties instead
// of GetFields works OK
var PropDest = TipoDest.GetProperties();
var QryPropVT =
from
POrig in PropOrig
join PDest in PropDest
on new
{
POrig.Name,
POrig.FieldType
} equals new
{
PDest.Name,
PDest.PropertyType
}
where POrig.PropertyType.IsValueType || (POrig.PropertyType.Equals(TipoString))
select new
{
PropO = POrig,
PropD = PDest
};
Visual C# error: Error 2 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
EDIT: I saw value injector, but it's like using a Death Star to kill a mosquito...[/EDIT]
Your join statement appears to be creating 2 different anonymous types since one has a property called FieldType and one has a property called PropertyType. LINQ is unable to do the join unless both types have the exact same fields in the exact same order. There is a wonderful article on this found here.
In which case you'll need to do this for your join:
join PDest in PropDest
on new
{
Name = POrig.Name,
JoinType = POrig.FieldType
} equals new
{
Name = PDest.Name,
JoinType = PDest.PropertyType
}
I think you might be after AutoMapper. http://automapper.codeplex.com/ or Value Injector http://valueinjecter.codeplex.com/
Value Injector example:
myObject.InjectFrom(anyOtherObject);
//inject from multiple sources
a.InjectFrom(b,c,d,e);
//inject using your own injection
a.InjectFrom<MyInjection>(b);
Here's the code, with comments to explain what's happening
public IQueryable<ImportUserAttemptViewModel> GetReports ()
{
// used to retrieve information
var userInfoServices = new UserInfoServices();
// get IQueryable<BasicImprtReport>
var attempts = GetBasicReports(type);
// convert to view model
var formattedAttempts =
(from BasicImportReport attempt in attempts
orderby attempt.DateStarted descending
select new ImportUserAttemptViewModel
{
ID = attempt.ID,
When = attempt.DateStarted,
NumberFailed = attempt.NumberFailed,
NumberProcessed = attempt.NumberProcessed,
UserName = userInfoServices.GetUserName(attempt.UserID)
});
return formattedAttempts;
}
When I try to do GetReports.ToList(), such as:
var report = GetReports().ToList();
I get the error:
LINQ to Entities does not recognize the method 'System.String GetUserName(Int32)' method, and this method cannot be translated into
a store expression.
I suppose this is because userInfoServices is out of scope by the time of the deffered excution. Is there anyway to let LINQ to entities find the required method?
(I know there are other solutions, such as using a join to find the username, but I am interested if there is anything else that can be done)
You could create the instance of the userInfoService in the anonymous type definition inside your LINQ so you don't have a variable which can potentially go out of scope. But adding a join to your user table if possible.
You could do something like
select new {
attempt.ID,attempt.DateStarted,
attempt.NumberFailed,attempt.NumberProcessed,
}).AsEnumerable().Select(x => new ImportUserAttemptViewModel {
ID = x.ID,
When = x.DateStarted,
NumberFailed = x.NumberFailed,
NumberProcessed = x.NumberProcessed,
UserName = userInfoServices.GetUserName(x.UserID)
});
which uses an anonymous type as a holding type while switching (AsEnumerable()) into LINQ-to-Objects for the GetUserName - however, note that this is a potential N+1; Personally, I'd be looking for a GetUserNames(...) that takes more than one UserId, and I'd stitch them back together after the fact.