5

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?

Jsinh
  • 2,539
  • 1
  • 19
  • 35
Matija Grcic
  • 12,963
  • 6
  • 62
  • 90

2 Answers2

10

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.

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • 1
    I'm interested in how would you expose entity key if there are not named Id, rather [TableName]Id? I've already implemented the strongly typed includes as i found your post while researching for my problem. Regards – Matija Grcic Jul 26 '12 at 10:06
  • 1
    I have previously answered similar questions for ObjectContext - you will just need to convert DbContext to ObjectContext: check [this one](http://stackoverflow.com/questions/5273416/entity-framework-simple-generic-getbyid-but-has-differents-pk-name/5278684#5278684) and [more complex one](http://stackoverflow.com/questions/5794902/generic-getbyid-for-complex-pk/5795480#5795480) but you will simply help yourselves a lot if you use the same key name and interface. – Ladislav Mrnka Jul 26 '12 at 10:29
  • Thank you. I use the same key name but the DB i'm currently working on is messed up. Regards – Matija Grcic Jul 26 '12 at 13:02
1

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.

Learner
  • 3,297
  • 4
  • 37
  • 62
  • 2
    Correct me if I'm wrong but this will cause multiple queries to be sent to the server. `Include` OTOH would allow fetching all the data with a simple query. So although this solution works, it's important to understand it may not always be a suitable replacement to `Include` extension. – Crono Jun 02 '16 at 12:07