Cannot find the right method to call in a deferred LINQ statement - c#

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.

Related

Cannot implicitly convert type 'System.Linq.IQueryable<AuthenticationApp.Models.User>' to 'AuthenticationApp.Models.User'

My linq is this:
User us = from s in entities.Users
where s.Username.Equals(username)
select s;
Any idea why I am getting the above error?
Use FirstOrDefault:-
User us = (from s in entities.Users
where s.Username.Equals(username)
select s).FirstOrDefault();
Your query is returning multiple results but you are trying to store that in AuthenticationApp.Models.User which can hold just one object thus the conversion error.
Or better:-
User us = entities.Users.FirstOrDefault(x => x.Username.Equals(username));
Update:
If you are sure it will return just 1 object back, you can use SingleOrDefault too. Check differences between both here.
You are getting error because projection returns IEnumerable (IQueryable). To get User by its name you can use the following:
try
{
var user = entities.Users.SingleOrDefault(u => u.Username.Equals(username));
}
catch
{
// handle the case when there are more than
// one user with given name in DB
}

Custom Method in LINQ 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

How to access NH QueryOver aliases from other functions and classes?

I want to be able to use one particular query in several other functions, I have a class that just creates a specialized QueryOver object for a particular domain.
But that function uses alias objects to create the joins. How can I access those aliases from another function?
For example say I have Course entities that each have a collection of students.
And I always want to only get Active ( a bool value) courses
public class QueryHelperClass
{
public QueryOver<Course, Course> GetQuery()
{
Address studentAlias = null;
QueryOver<Course, Course> query = QueryOver.Of<Course>(() => courseAlias)
.JoinAlias(x => cus.Student, () => studentAlias)
.Where(x => courseAlias.IsActive);
return query;
}
}
That works fine if all I need to do is GetExecutableQuery and return the results, but what do I do if I need to modify the query by accessing studentAlias?
Example:
public class SomeOtherClass
{
public List<Course> GetActiveCourseSummary(QueryOver<Course, Course> queryOver)
{
var query = queryOver.Where(a=> studentAlias.Name = "Bob");
...
}
}
From the SomeOtherClass.GetActiveCourseSummary I want to modify the query to only get courses where "Bob" is enrolled. But I can't access the studentAlias because it was defined in another function.
What can I do here, or am I setting this up all completely hard-core incorrectly?
In fact, we can re-declare the same variable in SomeOtherClass.
public List<Course> GetActiveCourseSummary(QueryOver<Course, Course> queryOver)
{
Address studentAlias = null;
var query = queryOver.Where(() => studentAlias.Name == "Bob");
...
}
The point is, that the name studentAlias (of the local variable Address) is the same as in the method GetQuery().
This will work, because what we pass in the .Where() method is the Expression. It is parsed and its string part "studentAlias" is used the same way as before, in GetQuery().
BUT
I would say, that this is not the way I would use. It is not clear what is passed into SomeOtherClass, how the query was built. There already could be an alias, but also it could be just a simple QueryOver<Course, Course> queryOver.
My approach is to do it different way. Collect all restrictions all the way down. Once there is e.g. set of restrictions IList<ICriterion>, call the DAO method, create query and append these restrictions at one place. But it is different story
If we would like to get some more checks into SomeOtherClass: we can use the Criteria API. Down side is that we have to usestring representation of properties "Student" and "Code" (not so clean as QueryOver API)
public List<Course> GetActiveCourseSummary(QueryOver<Course, Course> queryOver)
{
var criteria = query.UnderlyingCriteria;
var rootAlias = criteria.Alias; // will return "courseAlias"
var path = rootAlias + ".Student"; // the path
var student = criteria.GetCriteriaByPath(path)
?? criteria.CreateCriteria(path, path);
var studentAlias = student.Alias; // finally we do have existing alias
queryOver.And(Restrictions.Eq(studentAlias + ".Name ", "Bob"));
...

Linq query to get the properties from object B which have the same name and type of object A

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);

Method cannot be translated into a store expression

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)

Categories