实体框架6最简单的方法来反规范化列,以避免频繁的连接
本文关键字:连接 规范化 框架 最简单 方法 实体 | 更新日期: 2023-09-27 18:05:09
让我们假设,我有两个实体
class Author{
public int Id{get;set;}
public string Name{get;set;}
//.....
}
class Article{
public int Id{get;set;}
public int AuthorId{get;set;}
public string Text{get;set;}
}
现在我想添加到文章AuthorName
属性加倍现有的作者。名称以简化结果linq查询和执行时间。我确信我的数据库只会被一个Asp使用。Net MVC项目。使用EF(不使用数据库触发器)实现这种列的常见方法是什么?
还有一个更困难的情况。假设我想在Author
实体中有TotalWordCountInAllArticles
列,该实体由Article的Text属性计算。
您可以将AuthorName
属性添加到Article
,并通过确保创建Articles
或更新Author.Name
的任何代码也更新所有Articles
来手动保持完整性。TotalWordCount
也是如此,Article.Text
改变的时候,重新计算其他Articles
的所有计数。
有一些模式可以使这更自动化,比如域事件模式(https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/),但它绝对不是即插即用的。这取决于这是几件事还是经常发生。
如果您经常为了性能而对数据进行非规范化处理,您可能需要考虑更多的架构,其中有一个规范化的数据库,然后有一个单独的进程,该进程生成数据的非规范化视图并将其放入文档存储中。
注意:这可能不会回答您问题的EF部分,但它确实为您的问题提供了另一种解决方案。
不知道你的项目开发到什么程度了,但你可能想考虑一下Drapper,它可以使这个简单,快速,并提供许多其他好处。
让我们假设对Article模型做了一个小的修改,以包含Author模型。
public class Article
{
public int ArticleId { get; set; }
public string Text { get; set; }
// using Author model
public Author Author { get; set; }
}
假设您期望执行的SQL在概念上类似于:
select article.[Id]
,article.[Text]
,article.[AuthorId]
,author.Name
from [Article] article
join [Author] author on author.AuthorId = article.AuthorId;
用Drapper实现一个存储库来检索它们真的很简单。它可能看起来像:
public class ArticleRepository : IArticleRepository
{
// IDbCommander is a Drapper construct
private readonly IDbCommander _commander;
/// <summary>
/// Initializes a new instance of the <see cref="ArticleRepository"/> class,
/// injecting an instance of the IDbCommander using your IoC framework of
/// choice.
/// </summary>
public ArticleRepository(IDbCommander commander)
{
_commander = commander;
}
/// <summary>
/// Retrieves all article instances.
/// </summary>
public IEnumerable<Article> RetrieveAll()
{
// pass the query method a reference to a
// mapping function (Func<T1, T2, TResult>)
// although you *could* pass the predicate
// in right here, the code is more readable
// when it's separated out.
return _commander.Query(Map.AuthorToArticle);
}
private static class Map
{
// simple mapping function which allows you
// to map out exactly what you want, exactly
// how you want it. no hoop jumping!
internal static Func<Article, Author, Article>
AuthorToArticle = (article, author) =>
{
article.Author = author;
return article;
};
}
}
您将使用Drapper可用的配置将SQL连接到存储库。它支持json和xml配置文件,如果你愿意,你也可以在代码中配置它。
我在Github上为您提供了一个快速示例。
为什么要考虑这个?这样做有很多好处:
- 您指出了性能问题(执行时间)。Drapper是建立在高性能微orm之王Dapper之上的抽象层。
- 你可以显式地控制对象的映射——没有奇怪的语义或框架怪癖(就像你面临的那样)。
- 没有自动生成SQL。你决定将执行什么SQL。
- 你的SQL和你的c#是分开的——如果你的模式改变了(也许是为了提高性能),没有需要重新编译你的项目,改变你的实体映射或改变任何你的域代码或存储库逻辑。您只需更新配置中的SQL代码。
- 同样,您可以将服务/存储库层设计得更加域友好,而不必担心数据访问问题会污染您的服务层(反之亦然)。
- 完全可测试-您可以轻松地模拟来自IDbCommander的结果。
- 更少的编码-不需要实体和dto(除非你想要它们),不重写
OnModelCreating
方法或从DbContext派生,POCO上没有特殊属性。
这只是冰山一角。