Royar Royar - 9 days ago 5
C# Question

LINQ to Entities does not recognize the method - Entity Framework and DDD

I started using Entity Framework 6 and learning Domain Driven Design.

To my understanding, according to DDD principles, business logic should reside in the domain layer, in entities and value objects.

In my

ApplicationUser
class, I want to have a method that says whether or not the user is currently active in the chat:

public bool IsActiveInChat()
{
return this.ConnectedToChat &&
(DateTime.Now - this.LastChatActivity).TotalMinutes < 10;
}


The purpose of the method is to filter which users to display in the chat members list.

This is the query in
ApplicationUserManager
, which is the
Repository
:

public List<ApplicationUser> GetUsersConnectedToChat()
{
List<ApplicationUser> users = Users
.Where(u => u.IsActiveInChat())
.ToList();

return users;
}


When I run this code, I get the error:


LINQ to Entities does not recognize the method 'Boolean
IsActiveInChat()' method, and this method cannot be translated into a
store expression.


I can easily make this work by transferring the logic into the
Repository
, but from what I read about DDD, the business logic should belong in the entities and not in the repositories.

This is a working version of the same query (without the method in
ApplicationUser
):

public List<ApplicationUser> GetUsersConnectedToChat()
{
List<ApplicationUser> users = Users
.Where(u => u.ConnectedToChat &&
DbFunctions.DiffSeconds(DateTime.Now, u.LastChatActivity) < 10)
.ToList();

return users;
}


So, my question: is this a known issue with Entity Framework?

Am I misunderstanding something?

Is there a way to bypass this issue and keep the business logic inside the
User
entity?

P.S. - The question brought up by marc_s doesn't answer my question. Firstly, the answer given there suggests using
AsEnumerable
, which fetches the entire table into memory and is bad performance wise. It also doesn't answer my question of what is the Domain Driven Design approach to this issue, which appears to be quite common and something I would encounter a lot in my project.

Answer

This is a common problem.

If your filtering logic is stored in C# functions, then the sql server does not know anything about it.

You have three options.

  1. Pull all the data down from SQL and perform your filtering locally (which,as you point out, can lead to bad performance).

  2. Move your filtering logic to SQL

  3. Attempt to write your filtering logic in a manner that can be passed to and understood by sql.

By 3, I mean you can re-write your IsActiveInChat function like

Expression<Func<ApplicationUser, bool>> IsActiveInChat = r => 
     (r.ConnectedToChat.Value && (DateTime.Now - r.LastChatActivity).TotalMinutes < 10);

or possibly

 Expression<Func<ApplicationUser, bool>> IsActiveInChat = r => 
      (r.ConnectedToChat &&
        DbFunctions.DiffSeconds(DateTime.Now, r.LastChatActivity) < 10)

Then you can use this function as

public List<ApplicationUser> GetUsersConnectedToChat()
{
    List<ApplicationUser> users = Users
        .Where(IsActiveInChat)
        .ToList();

    return users;
}

The problem with this method is that you are limited to functions that SQL can handle. For example, you will get a NotSupportedException if you try

Expression<Func<ApplicationUser, bool>> isJan = r => 
     (r.LastChatActivity.ToSting("MMM")  == "JAN";
Comments