【问题标题】:Relationship between EF-Generated classes and model?EF 生成的类和模型之间的关系?
【发布时间】:2014-08-06 02:11:05
【问题描述】:

我正在为一个项目使用 ASP .NET MVC (C#) 和 EntityFramework(数据库优先)。

假设我在“电影详细信息”页面上,该页面显示了我数据库中一部电影的详细信息。我可以点击每部电影并进行编辑。 因此,我有一个 Movie 类,以及一个使用 EF 生成的 Database.Movie 类。

我的索引操作看起来像:

    public ActionResult MovieDetail(int id)
    {
        Movie movie = Movie.GetInstance(id);
        return View("MovieDetail", movie);    
    }

GetInstance 方法应该返回一个 Movie 类的实例,目前看起来像这样:

    public static Movie GetInstance(int dbId)
    {
        using (var db = new MoviesEntities())
        {
            Database.Movie dbObject = db.Movies.SingleOrDefault(r => r.Id == dbId);
            if (dbObject != null)
            {
                Movie m = new Movie(dbObject.Id, dbObject.Name, dbObject.Description);
                return m;
            }
            return null;
        }
    }

它工作正常,但这是实现它的好方法吗?还有其他更简洁的方法来获取我的 Movie 类实例吗? 谢谢

【问题讨论】:

    标签: c# asp.net-mvc entity-framework architecture


    【解决方案1】:

    is this a good way to implement it?

    这是一个非常主观的问题。它是有效的,而且这个实现在技术上没有任何问题。对于我的小型家庭项目,我使用过类似的东西。

    但对于业务应用程序,最好让您的实体与您的 MVC 应用程序无关。这意味着你的数据上下文 + EF + 生成的实体应该保存在一个单独的项目中(我们称之为“数据”项目),实际数据以 DTO 的形式传递。

    所以如果你的实体类似于这样:

    public class Person {
           public int Id { get; set; }
           public string Name { get; set; }
    }
    

    您希望有一个等效的 DTO 类能够传递该数据:

    public class PersonDTO {
           public int Id { get; set; }
           public string Name { get; set; }
    }
    

    这意味着您的“数据”项目只回复 DTO 类,而不是实体

    public static MovieDTO GetInstance(int dbId)
    {
        ...
    }
    

    最有意义的是,您的 DTO 也在一个单独的项目中。所有这些抽象的原因是,当您必须更改您的数据上下文(例如,应用程序将开始使用不同的数据源)时,您只需要确保新数据项目也与相同的 DTO。它在内部如何工作,以及它使用哪些实体,只与项目内部相关。从外部(例如,从您的 MVC 应用程序),您如何获取数据并不重要,只要您以您的 MVC 项目已经理解的形式(DTO 类)传递它。

    您的所有 MVC 控制器逻辑都不必更改,因为 DTO 对象没有更改。这可以节省您的时间。如果您将实体链接到您的控制器和视图,如果您突然决定更改实体,则必须重写两者。

    如果您担心将实体转换为 DTO(反之亦然)需要编写大量代码,您可以查看 Automapper 之类的工具。

    主要问题:这需要吗?

    这又是一个非常主观的问题。它与项目的范围有关,也与应用程序的预期生命周期有关。如果它应该使用很长时间,那么让所有东西都可以互换可能是值得的。如果这是一个小规模、短生命周期的项目,那么实施它所增加的时间可能不值得。

    我不能给你一个明确的答案。评估您希望应用程序适应变化的程度,以及应用程序在未来发生变化的可能性。

    免责声明:我们在我工作的公司就是这样做的。这不是此类问题的唯一解决方案,但它是我熟悉的解决方案。就个人而言,我不喜欢抽象,除非有功能上的原因。

    【讨论】:

      【解决方案2】:

      一些事情:

      1. 您使用的命名有点尴尬和混乱。通常,您永远不希望项目中的两个类具有相同的名称,即使它们位于不同的命名空间中。它在技术上没有任何问题,但它会造成混乱。我在这里需要哪个Movie?如果我正在处理Movie 实例,是Movie 还是Database.Movie?如果您坚持使用 MovieMovieDTOMovieMovieViewModel 之类的名称,则类名清楚地表明了用途(缺少后缀表示数据库支持的实体)。

      2. 特别是如果您来自另一个 MVC 框架,如 Rails 或 Django,ASP.NET 的特殊 MVC 风格可能会有点让人迷惑。大多数其他 MVC 框架都有一个真正的模型,一个单一的类,它充当所有业务逻辑的容器,也充当存储库(在某种意义上可以被认为是业务逻辑)。 ASP.NET MVC 不能那样工作。您的实体(表示数据库表的类)是并且应该是愚蠢的。它们只是 Entity Framework 用来填充从数据库中提取的数据的地方。您的模型(MVC 中的 M)实际上更像是您的视图模型和您的服务/DAL 层的组合。另一方面,您的Movie 类(不要与Database.Movie 混淆...看看为什么命名位很重要)正在尝试执行三重职责,充当实体、视图模型和存储库。这简直是​​太多了。你的班级应该做一件事并且把它做好。

      3. 同样,如果您有一个类将充当服务或存储库,那么它应该是一个实际的服务或存储库,具有这些模式所暗示的一切。即使这样,您也不应该在方法中实例化您的上下文。处理它的最简单正确方法是简单地将您的上下文作为类实例变量。比如:

        public class MovieRepository
        {
            private readonly MovieEntities context;
        
            public MovieRepository()
            {
                this.context = new MovieEntities();
            }
        }
        

        更好的是,使用控制反转并在上下文中传递:

        public class MovieRepository
        {
            private readonly MovieEntities context;
        
            public MovieRepository(MovieEntities context)
            {
                this.context = context;
            }
        }
        

        然后,只要您需要MovieRepository 的实例,您就可以使用依赖注入框架,如 Ninject 或 Unity 来满足您的依赖(最好使用请求范围的对象)。不过,如果您刚刚起步,这有点高水平,所以如果您暂时推迟整英里,这是可以理解的。但是,您仍然应该牢记这一点来设计您的课程。控制反转的要点是抽象依赖关系(例如需要从数据库中提取实体的类的上下文),以便您可以在需要时切换这些依赖关系(例如,如果有时间您将从 Web API 而不是通过实体框架检索实体,或者即使您只是决定切换到不同的 ORM,例如 NHibernate)。在您代码的当前迭代中,您将不得不触及每个方法(并且通常对您的类进行更改,违反了开闭原则)。

      【讨论】:

        【解决方案3】:

        实体模型永远不应该充当视图模型。向视图提供数据是视图模型的重要角色。 view-model 很容易被识别,因为它除了持有数据和仅对该数据起作用的业务规则之外没有任何其他角色或职责。因此,它具有任何其他纯模型的所有优点,例如单元可测试性。

        可以在 Dino Esposito 的 The Three Models of ASP.NET MVC Apps. 中找到一个很好的解释

        【讨论】:

          【解决方案4】:

          您可以使用 AutoMapper

          什么是 AutoMapper?

          AutoMapper 是一个简单的小库,旨在解决看似复杂的问题 - 摆脱将一个对象映射到另一个对象的代码。这种类型的代码写起来比较枯燥乏味,为什么不发明一个工具来帮我们做呢?

          我该如何开始?

          查看getting started guide

          在哪里可以买到?

          首先,安装 NuGet。然后,从包管理器控制台安装 AutoMapper:

          PM> Install-Package AutoMapper
          

          【讨论】:

            猜你喜欢
            • 2021-05-08
            • 1970-01-01
            • 2013-03-27
            • 1970-01-01
            • 2011-06-01
            • 2021-06-15
            • 1970-01-01
            • 2014-12-02
            • 2012-09-12
            相关资源
            最近更新 更多