想象一下这三个 EF 类
class Instrument
{
public long InstrumentId { get; set; }
public string Model { get; set; }
public dynamic AsJson()
{
return new
{
instrumentId = this.InstrumentId,
model = this.Model
}
}
}
class Musician
{
public long MusicianId { get; set; }
public virtual Instrument Instrument { get; set; } // notice navigation
public string Name { get; set; }
public dynamic AsJson()
{
return new
{
musicianId = this.MusicianId,
name = this.Name,
instrument = this.Instrument.AsJson()
}
}
}
class MusicBand
{
public long MusicBandId { get; set; }
public string Name { get; set; }
public virtual List<Musician> Members { get; set; }
}
现在想象一下,我们需要多个操作,所有操作都彼此相似,并且返回 JSON。
我们称之为方法 (A)
// ajax/Bands/Members
//
public JsonResult Members(long musicBandId)
{
MusicBand g = db.MusicBands.SingleOrDefault(g => g.MusicBandId == musicBandId);
if (g == null)
return null;
return Json(new
{
error = false,
message = "",
members = from p in g.Members.ToList() select p.AsJson()
}, JsonRequestBehavior.AllowGet);
}
问题是要使用 AsJson()
方法需要 ToList()
...所以需要做大量的工作内存
在下面的方法中,我们称之为方法(B),这不是问题。在内存中完成的工作是最少的,大部分工作是在 SQL 中完成的。事实上,它是一个大型 SQL 查询,包含所需的一切。
// ajax/Bands/Members
//
public JsonResult Members(long musicBandId)
{
MusicBand g = db.MusicBands.SingleOrDefault(g => g.MusicBandId == musicBandId);
if (g == null)
return null;
return Json(new
{
error = false,
message = "",
persons = from p in g.Members select new
{
musicianId = p.MusicianId,
name = p.Name,
instrument = select new
{
instrumentId = instrument.InstrumentId,
model = instrument.Model
}
}
}, JsonRequestBehavior.AllowGet);
}
方法A。
优点:代码简洁,我可以在其他操作中重用代码,减少耦合
缺点:性能问题(工作在内存中完成!)
方法B:
优点:代码丑陋,如果其他操作需要类似的东西,我最终会复制粘贴代码!这会带来耦合(多次修改类似的代码)
缺点:没有性能问题(在 SQL 中完成工作!)
最后一个问题: 我正在寻找另一种方法(C),它具有(A)和(B)的优点, 可重用且无性能问题
我想听听具有许多导航属性的大型系统如何实现这一目标。
当需要不同的 JSONS(共享子部分)时,他们如何设法减少耦合
还有一个子问题:
在方法(A)中,以下内容会产生影响吗?
db.MusicBands.Include(g => g.Members.Select(m => m.Instrument)).SingleOrDefault(g => g.MusicBandId == musicBandId)
还是不会? (其余代码保持不变)
最佳答案
您的问题的起点是实体类中的这些 AsJson()
方法。但这是我一开始就不会做的事情。首先,它在您的领域中引入了传输概念,其次,实体的表示可能取决于用例:也许在其他情况下您只想展示没有乐器的音乐家。
如果您想将实体序列化为 JSON,您通常需要禁用代理生成,这会禁用延迟加载并具体化原始实体类型...
db.ProxyCreationEnabled = false;
...并急切地加载您想要返回的所有内容...
MusicBand g = db.MusicBands
.Include(mb => mb.Members.Select(m => m.Instrument))
.SingleOrDefault(mb => mb.MusicBandId == musicBandId);
...并以 JSON 形式返回 g
。
这在一个 SQL 查询中完成了这项工作。在您的替代方案中,始终至少有两个查询,因为您首先获取 MusicBand
,然后通过延迟加载获取成员(和乐器)。
如果您想序列化实体的其他表示形式,则应该映射到 DTO(或 View 模型)对象。这时像 AutoMapper 这样的工具就派上用场了。
关于c# - 避免 Linq-To-Objects 但允许重用代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23284680/