Search code examples
c#entity-frameworkservice-layer

"safe" queryable service layer design?


Imagine you're using EntityFramework as your ORM, all wrapped up in a separated DAL class library.

You have the following POCO object in another "common" class library, which is nicely shared between your DAL,SL and Presentation Layer:

public class User
{
   public int Id { get; set;}
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string Email { get; set; }
   public int Age { get; set; }
   public Gender Gender { get; set; }
}

You then implement the following in the SL:

public interface IUserService
{
   User GetById(int u);
   List<User> GetByLastName(string s);
}

public class UserService : IUserService
{
   private MyContext _myContext;
   public UserService(MyContext myContext = null)
   {
      _myContext = myContext ?? new MyContext();
   }
   public User GetById(int userId)
   {
      return _myContext.Users.FirstOrDefault(u=>u.Id == userId);
   }

   public List<User> GetByLastName(string lastName)
   {
      return _myContext.Users.Where(u=>u.LastName == lastName).ToList();
   }
}

And all works hunky-dory.
.
But then you need to add a new method to the service to handle a different query (for example, users who fall within an age-range).
And then another.
And another...
.
Before long, you start to think

Wouldn't it be nice if you could provide any query you can think of through to the service layer, and it would get the relevant data and return it for you, without having to explicitly define each possibly query as a distinct method, much in the same way the SL is already doing with the DAL?

So the question is:

Is this possible to achieve SAFELY within a SL, whilst still maintaining loose coupling? .
I've read that using IQueryable can lead to disaster with things like:

q.Where(x=>{Console.WriteLine("fail");return true;});

But I'm also fairly new to using ORMs and service layers, so naturally am looking for the "best practices" and "known pitfalls", whilst also wanting to keep my code clean.


Solution

  • It sounds like you're leaking business layer logic into your presentation layer.

    As mentioned in the comments, determining the exact dataset that should be displayed by the presentation layer is actually business logic. You may have fields on your UI that give the user the ability to select a particular age range to display, which is perfectly valid, but the presentation layer should be responsible for just pushing those values up to the service layer and providing the data it returns to the actual UI in a friendly/expected fashion.

    The actual searching / fitlering of data based on those values should be done within the service layer / business layer.