ReSharper by JetBrains is a great tool for static analysis, detecting easily-missed bugs, and generally fixing some wonkiness with Visual Studio. However, using it for code correction must be done carefully, as its output is not always correct.
Case in point is this situation I just ran into yesterday.
Here’s the original code, reduced for test case simplicity:
List<Messages> messages = new List<Messages>(); var messagePosts = msgPosts.Where(mp => mp.fromUserId == sessionData.UserID) .OrderByDescending(mp => mp.createDate).Skip(page * count).Take(count); foreach (var messagePost in messagePosts) { var message = Mapper.Map<Messages>(messagePost); messages.Add(message); }
ReSharper will immediately give you a prompt: Loop can be converted into LINQ-expression
.
However, what it generates is incorrect:
var messagePosts = msgPosts.Where(mp => mp.toUserId == sessionData.UserID && mp.status == 1) .OrderByDescending(mp => mp.createDate).Skip(page * count).Take(count); List<Messages> messages = messagePosts.Select(messagePost => Mapper.Map<Messages>(messagePost)).ToList();
Do you see the error? By moving the foreach
loop into an Enumerable projection, we now have code that Entity Framework will try to execute in SQL. This in turn will compile cleanly but at runtime will blow up:
LINQ to Entities does not recognize the method ‘Models.Messages Map[Messages](System.Object)’ method, and this method cannot be translated into a store expression.
The way to fix it is to use AsEnumerable()
to force evaluation with LINQ, like so:
List<Messages> messages = messagePosts.AsEnumerable().Select(messagePost => Mapper.Map<Messages>(messagePost)).ToList();
(You could also use ToList()
, but using AsEnumerable()
is more efficient as you’re just changing the compile-time typing as opposed to forcing evaluation.)
And then from there, you can simplify to a method group, like so:
List<Messages> messages = messagePosts.AsEnumerable().Select(Mapper.Map<Messages>).ToList();
And you could terse it up even more by conversion to implicit typing if you so desired.
This behavior takes place in both ReSharper 7.x and 8.x branches; I don’t really expect it to be corrected though, considering that ReSharper doesn’t know if the underlying enumeration is going to be taking place at SQL or not.