-->

Passing multiple Include statements into a reposit

2019-03-22 18:41发布

问题:

I am trying to figure out a way to pass a collection of include statements into my repository so that I can have it include specific entities. Below is some sample code from my repository.

   public TEntity GetById(Guid id)
        {
            return id != Guid.Empty ? GetSet().Find(id) : null;
        }
   private IDbSet<TEntity> GetSet()
            {
                return _unitOfWork.CreateSet<TEntity>();
            }

The GetByID method calls the GetSet to return the entity set. I was thinking, if I could somehow pass in a collection of entities to include (via an expression) as part of my GetById, this way I wouldn't have to expose the GetSet to my services. So, something like this:

var entity = _repository.GetById(theId, e => {e.Prop1, e.Prop2, e.Prop3});

I could then pass that expression into my GetSet method and pass it into an include statement. Thoughts?

回答1:

I have done something like this in my code recently. Would the following work for you?

public TEntity GetById(Guid id, params Expression<Func<TEntity, object>>[] includeProperties)
    {
        if (id == Guid.Empty) return null;

        var set = _unitOfWork.CreateSet<TEntity>();
        foreach(var includeProperty in includeProperties)
        {
             set.Include(includeProperty);
        }
        return set.First(i => i.Id == id);
    }

Then you would call it like this...

var entity = _repository.GetById(theId, e => e.Prop1, e=> e.Prop2, e=> e.Prop3);

I know this doesn't exactly follow your pattern, but I think you could refactor it as required.



回答2:

I don't think Paige Cook's code will work quite as shown.

I've included a modified version of the code that should work instead:

public TEntity GetById(Guid id, params Expression<Func<TEntity, object>>[] includeProperties)
{
    if (id == Guid.Empty) return null;

    IQueryable<TEntity> set = _unitOfWork.CreateSet<TEntity>();

    foreach(var includeProperty in includeProperties)
    {
         set = set.Include(includeProperty);
    }
    return set.First(i => i.Id == id);
}

I only spotted this by tracing the SQL generated by Entity Framework, and realised the original code was only giving the illusion of working, by using lazy-loading to populate the entities specified for inclusion.

There's actually a more terse syntax for applying the Include statements using the LINQ Aggregate method, which is in the blog post linked to. My post also improves the method slightly by having a fall-back to the Find method, when no includes are needed and also shows an example of how to implement a "GetAll" method, using similar syntax.



回答3:

The include method can be strung together in your linq query like so:

var result = (from i in dbContext.TableName.Include("RelationProperty")
                                           .Include("RelationProperty")
                                           .Include("RelationProperty")
                select i);


回答4:

It's bad idea to store context in non-local space, for many reasons.

I modify Steve's code and get this for my ASP.NET MVC projects:

public aspnet_User FirstElement(Func<aspnet_User, bool> predicate = null, params Expression<Func<aspnet_User, object>>[] includes)
    {
        aspnet_User result;
        using (var context = new DataContext())
        {
            try
            {
                var set = context.Users.AsQueryable();

                for (int i = 0; i < includes.Count(); i++ )
                    set = set.Include(includes[i]);

                if (predicate != null)
                    result = set.ToList().FirstOrDefault(predicate);
                else
                    result = set.ToList().FirstOrDefault();
            }
            catch
            {
                result = null;
            }
        }

        return result;
    }