Home > Blockchain >  EntityFramework Generic Service with Generic Includes
EntityFramework Generic Service with Generic Includes

Time:04-07

I have a generic service, which is defined like this:

public interface IGenericService<T> : IDisposable
where T : class, IModel
{
    Task<bool> Add(T entity);

    Task<bool> AddOrUpdate(T entity);

    Task<bool> Update(T entity);

    Task<T?> Get(int id);

    Task<bool> Exists(int id);

    Task<IEnumerable<T>> GetAll();

    Task<bool> Delete(T entity);

    Task<IEnumerable<T>> GetAll(int numberOfElements, int page);

    Task<int> NumberOfEntries();

    Task<T?> Refresh(T entity);
}

which is implemented in GenericService.cs. This works pretty well, even with child classes, e.g. "MyService.cs". I am currently cleaning up the code and I found one issue, I am not happy with.

If MyService with the template type MyClass has a relationship to MyOtherClass and I want to use my generic Get-Method, I do not receive the object of MyOtherClass. Therefore, I need to create a new Get-Method in MyService with the corresponding include, which looks something like this:

public new async Task<MyClass?> Get(int id)
{
    try
    {
        return await this.Context.Set<MyClass>().Include(c => c.MyOtherClass).FirstOrDefaultAsync(d => d.Id == id);
    }
    catch (Exception)
    {
        return null;
    }
}

This is not a problem per se, but I started to figure out a way to generically load the includes for all the child classes. This means, that I only have one implementation of the Get-Method and not a separate one for nearly all of my services.

My idea was, that in GenericService, I add an abstract method:

protected abstract IEnumerable<string> GetIncludes();

which is then called in the Get-Method of the GenericService:

public async Task<TModel?> Get(int id)
{
    try
    {
        var query = this.Context.Set<TModel>();
        foreach (var include in this.GetIncludes())
        {
            query.Include(include);
        }

        return await query.FindAsync(id);
    }
    catch (Exception)
    {
        return null;
    }
}

In my child classes I then simply implement the abstract method like this:

protected override IEnumerable<string> GetIncludes()
{
    return new List<string>() { "MyOtherClass" };
}

The problem is, that this approach does not seem to work. If I debug the Get-Method of my GenericService, I get the correct List of Includes and the for-loop is executed correctly, but MyOtherClass is always null. (It obviously works, when I override the Get Method and directly write this.Context.Set<MyClass>().Include(c => c.MyOtherClass))

CodePudding user response:

You have forgotten to assign modified query:

query = query.Include(include);
public async Task<TModel?> Get(int id)
{
    try
    {
        var query = this.Context.Set<TModel>().AsQueryable();
        foreach (var include in this.GetIncludes())
        {
            query = query.Include(include);
        }

        return await query.FirstOrDefaultAsync(m => m.Id == id);
    }
    catch (Exception)
    {
        return null;
    }
}
  • Related