我正在尝试在Linq to EntityFramework查询中编写OrderBy子句。我的问题是我正在查看的实体表存储了一个ID,它与不同数据库中的表有关,我无法调整数据库。
MainDatabase.EntityToOrder
ID
Name
OtherID
SecondDatabase.OtherEntity
ID
Name
我的C#EntityToOrder模型看起来像这样,我需要能够通过" OtherName"
订购EntityToOrder.cs
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
public string OtherName { get; set; }
}
所以,我想通过" OtherName"来订购EntityToOrder。以最有效的方式。我现有的查询看起来像这样。
var entities = mainContext.EntityToOrder.OrderBy(e => e.Name).Skip(startIndex).Take(pageSize).ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
如何通过&#34; OtherName&#34;编写最有效的查询,最好避免将整个EntityToOrder表选入内存。
更新
为清楚起见,这里有一些实现OrderBy的代码,但需要将整个EntityToOrder表检索到内存中。我希望能以更有效的方式实现这一目标。此外,OtherEntity可以属于许多EntityToOrder行。
var entities = mainContext.EntityToOrder.ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
return entities.OrderBy(e => e.OtherName).Skip(startIndex).Take(pageSize).ToList();
答案 0 :(得分:1)
非常具有挑战性的任务。我最初只想在OtherEntity表上切换角色并执行分页(OrderBy / Skip / Take),但不幸的是,由于一对多的关系,它不起作用。所以我最终在OtherEntity的内存中做了一些预分页。但是,为了做到这一点,我需要EnityToOrder中匹配项的计数,所以这是通过额外的db查询检索的,这使得该解决方案涉及3个db查询和一些内存处理。这是
var countByOtherId = db.EntityToOrder
.GroupBy(e => e.OtherId)
.Select(g => new { ID = g.Key, Count = g.Count() })
.ToDictionary(e => e.ID, e => e.Count);
var other = new Dictionary<long, string>();
int skipCount = startIndex, useCount = 0;
foreach (var e in db.OtherEntity.OrderBy(e => e.Name))
{
int count;
if (!countByOtherId.TryGetValue(e.ID, out count)) continue;
if (skipCount > 0 && other.Count == 0)
{
if (skipCount >= count) { skipCount -= count; continue; }
count -= skipCount;
}
other.Add(e.ID, e.Name);
if ((useCount += count) >= pageSize) break;
}
var entities = db.EntityToOrder
.Where(e => other.Keys.Contains(e.OtherId))
.AsEnumerable()
.Select(e => new EntityToOrder { ID = e.ID, Name = e.Name,
OtherId = e.OtherId, OtherName = other[e.OtherId] })
.OrderBy(e => e.OtherName).ThenBy(e => e.Name)
.Skip(skipCount).Take(pageSize)
.ToList();
现在,我不太确定你现在做的事情是否更好,但值得尝试。
答案 1 :(得分:0)
如果您可以更改模型,则可以尝试以下操作:
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
[ForeignKey("OtherId")]
public OtherEntity OtherEntity{ get; set; }
}
然后,您应该能够执行此查询:
using System.Data.Entity;
var entities = mainContext
.EntityToOrder
.Include(x => x.OtherEntity)
.OrderBy(e => e.OtherEntity.Name)
.Skip(startIndex)
.Take(pageSize)
.ToList();
修改: 对不起,我错过了你有两个数据库....
答案 2 :(得分:0)
我发现了一个替代方案,我想我会发布以防任何人都有用。我使用.Join()将OtherEntity的字典合并到我的查询中。这仍然选择IEnumerable,所以我认为它不是更有效。
var entities = mainContext.EntityToOrder;
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
Func<EntityToOrder, KeyValuePair<long, string>, EntityToOrder> joinFunc = ((a, b) => {
a.OtherName= b.Value;
return a;
});
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.Skip(startIndex)
.Take(pageSize)
.ToList();
有关包含的说明
应用Join时,您选择进入IEnumerable,因此无法访问链接表中的属性。要解决此问题,您需要在应用.Join()之前为需要访问的任何链接表添加.Include()。 E.g。
var entities = mainContext.EntityToOrder
.Include("LinkedEntity");
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.ThenBy(e => e.LinkedEntity.Name) //reference to linked table
.ToList();