DBContext Find with Includes - where lambda with Primary keys

纵然是瞬间 提交于 2020-01-13 17:09:30

问题


I am writing a generic repository to interface with EF using DBContext.

I have a generic Get() method which receives a primary key value and returns the entity:

public class DALRepository<DALEntity> : IDisposable, IGenericRepository<DALEntity> where DALEntity : class
{
private IDbSet<DALEntity> dbSet;
private NWEntities context;

public DALRepository()
{
  context = new NWEntities();
  context.Configuration.LazyLoadingEnabled = false;
  dbSet = context.Set<DALEntity>();
}

Here's a simple get method - just works on the PK - exactly what I want.

public DALEntity Get(string ID)
{
   return dbSet.Find(ID);
}

I now want to change this to allow the consumer to pass in a list of includes - so as well as returning just a customer they can request to return the orders as well. Here's where I'm running into trouble. If I do this:

public DALEntity Get(string ID, IEnumerable<string> IncludeEntities = null)
{
      IQueryable<DALEntity> query = dbSet;
      query = IncludeEntities.Aggregate(query, (current, includePath) => current.Include(includePath));
}

I can't use find with the IQueryable. And I can't Find() directly because I can't pass includes to it. If I use a where lambda instead on the IQueryable, how do I tell it to use the PK of the entity? I guess I could have a generic constraint that insists the generic type must implement some IPkey interface with a well-defined primary column name such as "ID", but then I can't use the entities generated by DBContext as they woudl have to implement this interface. I can change the T4 to do this if I need - and I've already changed it to emit XML comments so not too averse to that - but does anyone have a simpler way? I suppose what I need is an overloaded find() which accepts a list of includes.

So my question is either how to use Find with includes, or how to write the lambda where it knows the PK? I can't receive such a lambda as a parameter as this will ultimately be consumed by a WCF service.


回答1:


Kind of wierd answering your own question but in case anyone else has this issue here's what I did. I used the dynamic LINQ stuff and used the string overloaded version of .Where(). I used one of the links mentioned to figure out how to grab the primary key (and mine is from a DBContext as well), and the method now looks like this:

public DALEntity Get(string ID, IEnumerable<string> IncludeEntities = null)
{
  var set = ((IObjectContextAdapter)context).ObjectContext.CreateObjectSet<DALEntity>();
  var entitySet = set.EntitySet;
  string[] keyNames = entitySet.ElementType.KeyMembers.Select(k => k.Name).ToArray();
  Debug.Assert(keyNames.Length == 1, "DAL does not work with composite primary keys or tables without primary keys");

  IQueryable<DALEntity> query = dbSet;
  query = IncludeEntities.Aggregate(query, (current, includePath) => current.Include(includePath));

  query = query.Where(keyNames[0] + "= @0", ID);
  return query.FirstOrDefault();
}



回答2:


You could get your Key dinamycally the way it is described here : https://stackoverflow.com/a/10796471/971693

That being said, I can't see a way without using some reflection :

public IEnumerable<DALEntity> Get(params string IDs)
{
   var objectSet = objectContext.CreateObjectSet<YourEntityType>();
   var keyNames = objectSet.EntitySet.ElementType.KeyMembers.First(k => k.Name);

   return dbSet.Where(m => ID.Contains((string)m.GetType().GetProperty(keyNames ).GetValue(m, null));
}

First of, params string IDs will let you pass 1 or more ID and will result in an array of string. The first part of the function is to dynamically get the name of your primary key.

The second part creates a query to return all elements from your set where the primary key value (obtained through reflection) is contained within the array of IDs received in parameter.




回答3:


Use Linq's Single or First methods, which allow you to search on IQueryable objects.

public DALEntity Get(string ID, IEnumerable<string> IncludeEntities = null)
{
      IQueryable<DALEntity> query = dbSet;
      query = IncludeEntities.Aggregate(query, (current, includePath) => current.Include(includePath));
      query = query.Single(x=>x.Id == ID);
}


来源:https://stackoverflow.com/questions/13059548/dbcontext-find-with-includes-where-lambda-with-primary-keys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!