我有一个引用对象,其中包含 QuoteAnswer 对象的集合。我还想要一个最新的QuoteAnswer的快捷方式。所以我建模了(为了简洁省略了不相关的代码):
public class Quote
{
public int Id { get; set; }
public ICollection<QuoteAnswer> Answers { get; set; }
public QuoteAnswer LatestAnswer { get; set; }
public int LatestAnswerId { get; set; }
}
public class QuoteAnswer
{
public int Id { get; set; }
public Quote Quote { get; set; }
public int QuoteId { get; set; }
/* Did not map this, not interested/not needed
* public Quote LastAnswerFor { get; set; }
* public int LastAnswerForId { get; set; }
*/
}
我希望能够做到这一点:
var quotes = context.Quotes
.Include(x => x.LatestAnswer)
.ToList();
而不是:
var quotes = context.Quotes
.Include(x => x.Answers)
.ToList();
foreach (var q in quotes)
{
var latestAnswer = q.Answers.OrderByDescending(x => x.Date).FirstOrDefault();
}
这显然会迫使我加载不必要的数据。
当我尝试映射这个数据库代码(添加迁移)时,我得到一个新属性,我不知道它来自哪里。
生成的迁移代码(为简洁起见省略了部分):
CreateTable(
"dbo.QuoteAnswer",
c => new
{
Id = c.Int(nullable: false),
QuoteId = c.Int(nullable: false),
QuoteId1 = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Quote", t => t.QuoteId)
.ForeignKey("dbo.Quote", t => t.QuoteId1)
.Index(t => t.QuoteId)
.Index(t => t.QuoteId1);
AddColumn("dbo.Quote", "LatestAnswerId", c => c.Int());
CreateIndex("dbo.Quote", "LatestAnswerId");
AddForeignKey("dbo.Quote", "LatestAnswerId", "dbo.QuoteAnswer", "Id");
QuoteId1 是什么东西?我得到 QuoteId ,但我不认识 QuoteId1 。
我如何实现此映射?这甚至在EF6中得到支持吗?
答案 0 :(得分:1)
首先,它是可能的。应删除显式FK属性:
public class Quote
{
public int Id { get; set; }
public string Data { get; set; }
public ICollection<QuoteAnswer> Answers { get; set; }
public QuoteAnswer LatestAnswer { get; set; }
}
并且应该使用流畅的API映射新关系:
modelBuilder.Entity<Quote>()
.HasOptional(e => e.LatestAnswer)
.WithOptionalDependent()
.Map(m => m.MapKey("LatestAnswerId"));
但我不建议你这样做,因为它会引入很多维护问题 - 除了明显需要保持最新,它会创建循环FK依赖,所以基本上所有的CUD操作都是有问题(如果工作的话)。
我认为你试图解决“加载不必要的数据”问题是一种错误的方法。您可以使用简单的投影来实现相同的目标:var quotesWithLatestAnswer = context.Quotes
.Select(q => new { Quote = q, LatestAnswer = q.Answers.OrderByDescending(a => a.Date).FirstOrDefault() })
.ToList();
请注意,Select
中的代码将被转换为SQL并在数据库中执行,只返回所需的数据。
要将最新答案作为实体的一部分返回,您可以将其标记为未映射:
public class Quote
{
public int Id { get; set; }
public string Data { get; set; }
public ICollection<QuoteAnswer> Answers { get; set; }
[NotMapped]
public QuoteAnswer LatestAnswer { get; set; }
}
并使用LiNQ与实体(SQL)和LINQ to Objects查询的组合:
var quotes = context.Quotes
.Select(q => new { Quote = q, LatestAnswer = q.Answers.OrderByDescending(a => a.Date).FirstOrDefault() })
.AsEnumerable() // end of db query
.Select(qi => { qi.Quote.LatestAnswer = qi.LatestAnswer; return qi.Quote; })
.ToList();
通过这种方式,您可以轻松,易于维护关系数据库模型,并有效地检索所需数据。