C# EntityFramework 6反规范化列以避免频繁联接的最简单方法

C# EntityFramework 6反规范化列以避免频繁联接的最简单方法,c#,entity-framework,C#,Entity Framework,让我们假设,我有两个实体 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;} } 现在我想在文章AuthorNameproperty中添加现有Autho

让我们假设,我有两个实体

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
property中添加现有Author.Name,以简化生成的linq查询和执行时间。我确信我的数据库将只被一个Asp.NETMVC项目使用。使用EF(无数据库触发器)实现此类列的常见方法是什么


这里还有一个更难的例子。假设我想在由文章的文本属性计算的
作者
实体中有
TotalWordCountInAllArticles
列。

您可以将
AuthorName
属性添加到
文章
中,并通过确保创建
文章
或更新
Author.Name
还更新了所有
文章
。与
TotalWordCount
相同,当
文章的时间发生变化时,重新计算其他
文章的所有计数

有几种模式可以让这变得更加自动化,比如域事件模式(),但它肯定不仅仅是即插即用。这取决于这些是否只是几个项目,或者这是否会经常发生


如果经常对数据进行非规范化处理以提高性能,您可能希望更多地了解一个体系结构,其中有一个规范化的DB,然后是一个单独的进程,该进程会生成数据的非规范化视图并将其放入文档存储。

您可以将
AuthorName
属性添加到
文章
中,并通过确保创建
文章
或更新
作者。姓名
也会更新所有
文章
。与
TotalWordCount
相同,当
文章的时间发生变化时,重新计算其他
文章的所有计数

有几种模式可以让这变得更加自动化,比如域事件模式(),但它肯定不仅仅是即插即用。这取决于这些是否只是几个项目,或者这是否会经常发生


如果经常对数据进行非规范化处理以提高性能,您可能希望了解更多的体系结构,其中有一个规范化的DB,然后是一个单独的进程,该进程生成数据的非规范化视图并放入文档存储。

注意:这可能无法回答您问题的EF部分,但它确实为您的问题提供了另一种解决方案

不确定你在开发项目的过程中有多远,但你可能想考虑一下让它变得琐碎、快速,并提供一些其他的好处。 让我们假设对您的文章模型进行了一个小的更改,以包括作者模型

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;
        };
    }
}
公共类ArticleRepository:IArticleRepository
{
//IDbCommander是一个Drapper构造
私人只读IDBcommanderu commander;
/// 
///初始化类的新实例,
///使用IoC的框架注入IDbCommander的实例
///选择。
/// 
公共物品储存库(IDB指挥官)
{
_指挥官=指挥官;
}
/// 
///检索所有项目实例。
/// 
公共数字

你为什么要考虑这个问题? 走这条路线有很多好处:

  • 您指出了性能问题(执行时间)。Drapper是构建在Dapper之上的抽象层,Dapper是高性能micro ORM之王
  • 您可以显式地控制对象的映射-没有奇怪的语义或框架怪癖(就像您面临的那个)
  • 没有自动生成的SQL。您可以决定将执行什么样的SQL
  • SQL与C#分离-如果模式发生更改(可能是为了提高性能),则无需重新编译项目、更改实体映射或更改任何域代码或存储库逻辑。只需在配置中更新SQL代码即可
  • 同样,您可以将服务/存储库层设计为更加领域友好,而不必担心数据访问问题会污染服务层(反之亦然)
  • 完全可测试-您可以轻松模拟IDB命令的结果
  • 更少的编码—不需要实体和dto(除非您需要),不需要重写模型创建方法或从DbContext派生的方法,也不需要POCO上的特殊属性

这只是冰山一角。

注意:这可能无法回答您问题的EF部分,但它确实为您的问题提供了另一种解决方案

不确定你在开发项目的过程中有多远,但你可能想考虑一下让它变得琐碎、快速,并提供一些其他的好处。 让我们假设对您的文章模型进行了一个小的更改,以包括作者模型

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;
        };
    }
}
公共类ArticleRepository:IArticleRepository
{
//IDbCommander是一个Drapper构造
私人只读IDBcommanderu commander;
/// 
///初始化类的新实例,
///使用IoC的框架注入IDbCommander的实例
///选择。
/// 
公共物品储存库(IDB指挥官)
{
_指挥官=指挥官;
}
/// 
///