Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Find() with Includes Entity Framework

I currently have a complete generic repository but I'm missing one feature and that is to use Include() and Find() together.

So now I have:

public E FindById<E>(int id) where E : class
{
    return DataContext.Set<E>().Find(id);
}

called using

var person = PersonRepo.FindById<Person>(personId);

I would like to have something similar to:

var person = PersonRepo.FindByIdWithIncludes<Person>(personId,new[]{"State.Address"});

So, something along this lines (this is only a test):

public E FindByIdWithIncludes<E>(int id, string[] includes) where E : class
{
    var entitySet = DataContext.Set<E>();
    DbQuery<E> entityQuery;

    foreach (var include in includes)
    {
        entityQuery = entitySet.Include(include);
    }

    return entityQuery.Find(id); //this is were it breaks
}

Is it possible?

like image 560
Matija Grcic Avatar asked Dec 27 '22 20:12

Matija Grcic


2 Answers

You cannot use Find directly - Find doesn't work with includes. You must use SingleOrDefault.

First you need to define interface for your entities to expose their key.

public interface IEntityWithId 
{
    public int Id { get; set; }
}

Next you can write simple method with constrain to get access to the key:

public E FindByIdWithIncludes<E>(int id, string[] includes) 
    where E : class, IEntityWithId
{          

    IQueryable<E> entityQuery = DataContext.Set<E>();

    foreach (var include in includes)
    {
            entityQuery = entityQuery.Include(include);
    }

    return entityQuery.SingleOrDefault(e => e.Id == id); 
}

Btw. you can use strongly typed includes - here is an example.

like image 168
Ladislav Mrnka Avatar answered Dec 29 '22 10:12

Ladislav Mrnka


You could go also the other way around, to use Find, but in combination with Load. In some cases might be better performing than Include-SingleOrDefault, but it really depends on the scenario.

Non-generic example:

 private User GetByUID(int uID, bool includeDetails = false, bool includeAddresses = false)
 {
    var result = context.Users.Find(uID);
    if (includeDetails)
    {
       // load user-details (1:1 relation)
       context.Entry(result)
              .Reference<UserDetails>(us => us.UserDetails)
              .Load();
    }
    if (includeAddresses) 
    {
       // load user-addresses (1:m relation)
       context.Entry(result)
              .Collection(us => us.Addresses)
              .Load();    
    }
    return result;
 }

Should not be difficult to make it generic to your needs.

like image 31
Learner Avatar answered Dec 29 '22 10:12

Learner