简体   繁体   中英

Strongly Typed Repository Return Types

So I've been doing a lot of programming in Entity Framework 6 recently, and I'm rather annoyed with a certain behavior regarding eager loading.

Take this:

var customers = _db.Customers.Where(c => c.IsActive);
var customersWithOrders = _db.Customers.Include(c => c.Orders).Where(c => c.IsActive);

customers.SelectMany(c => c.Orders).DoSomething(...); // NRE
customers.SelectMany(c => c.Orders).DoSomething(...); // Works

I understand I can actually enable lazy loading in EF, but that's something I'd like to avoid doing as it would merely turn this into a Select N+1 problem.

What I don't like about this is that both queries are actually returning different types of data even though their signatures are essentially IEnumerable<Customer> If I were to write my own repository on top of EF, I'd prefer to do something like this:

public class CustomerRepo {
    IEnumerable<T> GetCertainCustomers<T>(...);
    IEnumerable<T> DifferentCustomerQuery<T>(...);
    IEnumerable<T> YetAnotherQuery<T>(...);
}

public class Customer {...}
public class CustomerWithOrders : Customer { public IEnumerable<Order> Orders { get; set;} }
public class CustomerWithPaymentMethods : Customer { public IEnumerable<Order> Orders { get; set;} }

Something like the above would actually work fine, the repository conducts the correct amount of eager loading given the type parameter T.

My problem with this is that the number of types is going to explode if you have more than 2-3 different eager loading options, not to mention what happens when you want to eager load more than one relationship!

FYI all of the above code is just for illustration, it's not real.

If you're concerned about null reference exceptions in particular , you can simply give your Orders an empty collection by default.

For example:

public class Customer
{
    public virtual List<Order> Orders { get; set; }
    public Customer()
    {
        Orders = new List<Order>();
    }
}

Loading a customer without .Include(c => c.Order) means the collection will stay empty but not null . This means that your examples:

customers.SelectMany(c => c.Orders).DoSomething(...);
customersWithOrders.SelectMany(c => c.Orders).DoSomething(...);

Will both work (the first one would simply do nothing, as the collection is empty).

In any case, you should be doing null checks where appropriate. If your method requires customers with orders, it should check that . Having a class defined which merely describes what information is or is not null in the object is a bad idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM