使用集合EntityFramework连接表
也许它是重复但我找不到正确的方法正确遵循。
通常我想从Employee表中检索与List相关的所有数据。 类型MyEmployee包含EntitySourceID,我用它来映射EmployeeID。 所以,我想检索所有与List集合中的EntitySourceID匹配EmployeeID的Employe。
类型MyEmployee看起来像:
public class MyEmployee { public long PersonID { get; set; } public string ConnectionString { get; set; } public long EntitySourceID { get; set; } public int EntitySourceTypeID { get; set; } }
我的查询看起来像:
internal IEnumerable GetPersons(List myEmployees) { return (from p in _context.Employee join pList in myEmployees on p.EmployeeID equals pList.EntitySourceID select new Person { PersonID = pList.PersonID, FirstName = p.FirstName, LastName = p.LastName, Name = p.Name, Suffix = p.Suffix, Title = p.Title }).ToList(); }
您可以在查询中看到,当我创建新的Person对象时,我使用List myEmployees集合中的pList.PersonID来填充Person。
所以,我的问题是我如何高效地从Employee表中检索与List集合匹配的数据,还使用pList.PersonID(来自集合)来创建返回结果?
我使用EF 6,数据库第一种方法。
另外,我没有提到。 此查询产生以下exception:无法创建“MyEmployee”类型的常量值。 在此上下文中仅支持原始类型或枚举类型。
IQueryable vs IEnumerable
解决一些更深层次问题的良好开端是花一些时间来发现它们之间的差异
-
IQueryable
和IEnumerable
也许还介于两者之间
- “常规代理”,如
Func
和“表达式”,如Expression
因为虽然他们有相似的forms,但他们的目的和行为不同。
现在回到你的问题
首先让我们举几个例子:
- 让我们调用RAM托管的
MyEmployee
实例集合列表 - 让我们调用数据库表(最有可能称为“Employee(s)”) 表
遗憾的是,在撰写问题时,您没有指出一些非常重要的细节。 这导致我提出4个不同的答案。 答案将根据以下两个问题的真值进行分类:
- 列表是巨大的吗?
- 桌子很大吗?
我们有4个非常不同的案例:
- 不,不
- 不,是的
- 是的,不
- 是的是的
你可以想象,现在第四个可能是最丑陋的。
当列表不是很大
在案例1和案例2中,您可以从不同的角度思考您的问题:
假设您需要根据作为ID的Precisely 1参数从数据库中获取一个 (或零)记录。 你应该加入吗?
答案是:绝对不是 。 看看这段代码:
var query = from employee in _context.Employee where employee.EmployeeId == 23 select employee; var found = query.FirstOrDefault();
如果我想获取与Precisely 2参数相关的记录,该怎么办? 我可以用类似的方式实现这一点:
var query = from employee in _context.Employee where employee.EmployeeId == 23 || employee.EmployeeId == 24 select employee; var results = query.ToArray(); if (results.Length == 0) // didn't find anyone of the presumably existing records else if (results.Length == 1) { if (results[0].EmployeeId == 23) // then we found the 23 else // the other one } else if (results.Length == 2) // found both, look inside to see which is which
我故意以愚蠢的方式写出算法的最后润色( if
部分)以避免额外的混淆。
对于最后的润色,这将是一种更人性化的方法:
... var results = ... got them (see above) var map = results.ToDictionary(keySelector: x => x.EmployeeId); var count = map.Count; // this gives you the number of results, same as results.Length var have23 = map.ContainsKey(23); // this tells you whether you managed to fetch a certain id var record23 = map[23]; // this actually gives you the record foreach (var key in map.Keys) { .. } // will iterate over the fetched ids foreach (var record in map.Values) { .. } // will iterate over the fetched values
不要担心ToDictionary
扩展方法。 它与EntityFramework无关(通过点击它查找)。
现在..回到我们的故事:如果你想带来与15个ID相关的记录怎么办? 停止。 这是怎么回事? 我是否要求您为每个可能的ID数量硬编码不同的查询?
当然不是。 只要id的数量“相对较小”(意味着你被某人允许,或者你自己用该请求量级轰炸数据库),你就可以很好地使用“列IN列表的params”SQL构造。
如何在SQL端指示LINQ to SQL或EF转换为“x IN y”操作而不是“x = y”操作?
通过使用相应类型的原始数组和Contains
方法。 换句话说,得到一个负载:
var query = from employee in _context.Employee where listOfIds.Contains( employee.EmployeeId ) select employee; var results = query.ToArray();
但是你需要一个“ID列表”而不是“MyEmployee实例列表”。 你可以很容易地把它拉下来:
List originalList = new List (); // ... say you populate this somehow, or you've received it from elsewhere int[] listOfIds = (from employee in originalList select employee.EntityId).ToArray(); // .. and then carry on with the EF query
请注意 ,对集合的查询显示为IEnumerable
实例,而不是IQueryable
实例,与EF或LINQ to SQL或任何其他DB或外部数据服务无关。
如果桌子不是巨大的
然后你可以避免将EF实际用于复杂查询,仅将其用于“全表提取”,将结果临时存储在.NET进程中,并使用常规LINQ,但是你喜欢。
这个故事的关键是从一开始就获取整个表格。 在你的问题中你写道:
return (from p in _context.Employee join pList in myEmployees on p.EmployeeID equals pList.EntitySourceID select new Person { PersonID = pList.PersonID, FirstName = p.FirstName ... etc
只需增加:
var entityList = _context.Employee.ToArray(); return (from p in entityList // PLEASE NOTE THIS CHANGE ALSO join pList in myEmployees on p.EmployeeID equals pList.EntitySourceID select ...
把它包起来
你可以:
- 指示数据库执行工作,但在这种情况下,您无法在此过程中向它发送花哨的.NET实例
- 在.NET上自己在楼上做这项工作
一方或另一方(数据库或.NET进程)需要拥有所有卡(需要克隆另一方)才能执行JOIN 。
所以这只是一场妥协游戏。
如何关于剩余案例
如果THE TABLE和THE LIST都是巨大的,那么你就是**** d。 不 – 我只是在开玩笑。
没有人听说过有人要求其他人在他们无法完成时做奇迹。
如果是这种情况,那么您必须将问题简化为大量较小的问题。 我建议转换成一个表巨大的 + 列表而不是那么大的问题乘以N.
那么你怎么做呢?
List original = ... // you take your list // and you split it in sections of .. say 50 (which in my book is not huge for a database // although be careful - the pressure on the database will be almost that of 50 selects running in parallel for each select) // how do you split it? // you could try this public static IEnumerable> Split(List source, int sectionLength) { List buffer = new List (); foreach (var employee in source) { buffer.Add(employee); if (buffer.Count == sectionLength) { yield return buffer.ToList(); // MAKE SURE YOU .ToList() the buffer in order to clone it buffer.Clear(); // or otherwise all resulting sections will actually point to the same instance which gets cleared and refilled over and over again } } if (buffer.Count > 0) // and if you have a remainder you need that too yield return buffer; // except for the last time when you don't really need to clone it } List> sections = Split(original, 50).ToList(); // and now you can use the sections // as if you're in CASE 2 (the list is not huge but the table is) // inside a foreach loop List results = new List (); // prepare to accumulate results foreach (var section in sections) { int[] ids = (from x in section select x.EntityID).ToArray(); var query = from employee in _context.Employee where ids.Contains(employee.EmployeeId) ... etc; var currentBatch = query.ToArray(); results.AddRange(currentBatch); }
现在你可以说这只是一种愚弄数据库的方式,相信它几乎没有什么工作要做,实际上我们仍然充斥着大量的工作, 也许让其他并发客户的生活变得艰难。
嗯,是的,但至少你可以减速。 您可以在各部分之间使用Thread.Sleep
…您可以使用iterators
(查找它们)并且实际上不会使RAM记录需要很长时间才能处理,而是“流式传输”。
你可以更好地控制局面。
祝好运!
我使用以下方法:
- 从myEmployees列表中提取ID;
- 检索该ID的数据并放入var查询;
- 加入mayEmployees和查询结果。
如下例所示:
long[] myEmployeesIDs = myEmployees.Select(p => p.EntitySourceID).ToArray(); var query = (from e in _context.Employees where myEmployeesIDs.Contains(e.EmployeID) select new Person { PersonID = e.EmployeeID }).ToList(); return (from m in myEmployees join q in query on m.EntitySourceID equals q.PersonID select new Person { PersonID = i.PersonID, ... }).ToList();