在 .net Project MVC 中实现数据访问层最佳实践

Posted

技术标签:

【中文标题】在 .net Project MVC 中实现数据访问层最佳实践【英文标题】:Implement data access layer best practices in .net Project MVC 【发布时间】:2017-02-26 22:33:48 【问题描述】:

我想通过在访问数据库时添加另一层来改进我的 .NET 项目。这是我的代码:

namespace Company.Models

public static class AgencyBean

[WebMethod]
    [ScriptMethod(UseHttpGet = true)]
    public static String createGUID(string name)
    
       DataAccess dataAccess = new DataAccess();
       bool exists = dataAccess.checkIfExists(Id);
       if(exist)
       
           dataAccess.delete(Id);
       
       retur "ok";
    
 

我将 DataAccess 类放在一个名为“Helpers”的单独文件夹中,其中包含我的大部分查询:

public class DataAccess
     
public bool checkIfExists(String Id)
    
        try
        
            SqlConnection cnn = new SqlConnection(dataConnection);
            cnn.Open();
            SqlCommand check_Id = new SqlCommand("SELECT COUNT(*) FROM TABLE_GUID WHERE ([USER_ID] = @Id)", cnn);
            check_Id.Parameters.AddWithValue("@Id", Id);
            int UserExist = (int)check_Id.ExecuteScalar();

            if (UserExist > 0)
            
                return true;
            
            else
            
                return false;
            
        
        catch (SqlException ex)
        
            Debug.WriteLine("SQL Exception " + ex);
            DisplaySqlErrors(ex);
            throw ex;
        
    
 

public class AgentBeanController : Controller

    // GET: AgentBean
    public ActionResult Index(string name)
    
        return View();
    

    [AllowAnonymous]
    [WebMethod]              
    public string AgentURL()  //here we create Agent URL and return it to the view
    
        string var = Models.AgentBean.createGUID("TODO");            
        return var;           
    



我以非常直接的方式访问数据库。使用更好的技术会如何,因此这种访问可以更安全,例如通过服务层访问? 我正在连接到某个服务器中的现有 sql 数据库,并在我的项目中使用 MVC 架构。

【问题讨论】:

你在正确的轨道上。如果您在 Google 上搜索“数据存储库”和“依赖注入”一词,可能会更进一步。存储库的一般概念是,您在模型层中有单独的类来访问数据(就像您现在所做的那样),但它们符合接口。当您实例化您的 AgencyBean 类时,您将接受一个实现该接口的对象作为参数。好处是您可以拥有不接触仅用于单元测试的数据库的接口的第二个实现。像 Ninject 这样的 DI 工具使这更容易编码。 现在我理解得更好了,我也想实现单元测试,我发现我需要 DI。感谢您的建议! 【参考方案1】:

这就是我过去所做的。

首先,这是您的“模型”命名空间...模型永远不应该具有数据库连接性。相反,您有一个单独的类,例如控制器,它可以为某些模型提供水合。

其次,我有一个“服务”类,它连接到一个“存储库”类。存储库类实现了一个接口来识别您正在使用的数据库的确切“类型”。但如果这不是您的要求的一部分,您可能不需要走那么远。

第三,查找依赖注入(又名 DI)。那里有几个框架。我个人使用过 Autofac,但也有其他工具可以更轻松地完成工作。

第四,在您的“控制器”、“服务”和“存储库”类上,实现依赖注入,以及形成合约所需的任何接口。

第五,我将使用一个实际的控制器命名空间,而不是使用你的模型命名空间来推送 http 调用带......相反,在你的控制器类中创建一个动作,并实例化你的“ agentBean”,用数据对其进行水合,然后将该模型返回到您的视图中。

基本上,在这样的场景中,您会尝试让每个组件都按照指定的方式执行...将职责分解为更小的部分并专注于此。您的控制器应该只“获取”您的模型,并可能根据需要或任何其他业务类型逻辑对其进行一些转换。 您的服务应该处理您的控制器和数据库层之间的通信。 您的数据访问层(即,在这种情况下,一些“存储库”类...)将完成所有这些新数据连接和/或设置对存储过程或查询的调用。

以这种方式做事有很多好处。其中一些重要的是可维护性、可读性、代码重用。当然,就文件放在任何地方而言,这会使您的项目变得更加复杂……但这可能是一件好事。这比把所有东西都塞进一个班级然后让它做所有事情要好得多:)

但是,仅供参考,这是我过去完成的一个实现...我确信有更好的方法,但这种设置对我和我的团队来说效果很好。

这是一个使用您发布的一些代码的小示例。我没有检查这个是否有错别字,它不会编译,但应该有助于大致了解我在说什么......

namespace Company.Models

    public class AgencyBean
    
        public AgencyNameget;set;
        public AgencyIdget;set;
        // other properties...
    





namespace Company.Controllers

    public class MyController : Controller
    
        private readonly IMyService myService;


        public MyController(IMyService myService) // <-- this is your dependency injection here...
        
            this.myService = myService; 
        

        [WebMethod]
        [ScriptMethod(UseHttpGet = true)]
        public static String createGUID(string name)
        
            var model = new AgencyBean();
            model.AgencyId = 1;
            model = myService.getAgency(agencyBean);            
            return model;
        
    





namespace Company.Services

    public class MyService
    
        private readonly IMyRepository myRepository;


        public MyService(IMyRepository myRepository) // <-- this is your dependency injection here...
        
            this.myRepository = myRepository; 
        

        public AgencyBean getAgency(AgencyBean model)
            var dataTable = myRepository.getAgencyData(model.AgencyId);
            // fill other properties of your model you have...
            // ...
            // ...
            return model;
        
    




namespace Company.Repositories

    public class MyRepository : IDatabaseCommon  // <-- some interface you would use to ensure that all repo type objects get connection strings or run other necessary database-like setup methods...
     
        private readonly String connectionStringget;set;

        public MyRepository()
        
            this.connectionString = //get your connection string from web.config or somewhere else...; 
        

        public DataTable getAgencyData(int id)
            var dataTable = new DataTable();

            // perform data access and fill up a data table

            return dataTable;
        
    

【讨论】:

我在某处读到我的业务逻辑应该在模型中,并且控制器应该尽可能简单,这就是我开始以这种方式编码的原因。然后我发现了一个类似的帖子,人们建议有一个你指出我的服务层。因此,在您的示例中,IMyservice 是位于包含我的查询的新文件中的接口,对吗?我只是有点困惑在哪里可以找到某些文件,因为我不想“破坏”这个 MVC 模式...... IMyService 是您在另一个文件中创建的接口是的。但这不是你写查询的地方,什么不是。如果你想写一堆 SQL 命令,我推荐另一个“模型”类,比如“QueryModel”,它会是一堆返回你的 sql 字符串的常量。但实际上,我个人不会那样做。相反,我在数据库级别使用存储过程/函数,并从应用程序代码中调用它们。 db 存储的过程/函数将包含您的所有 SQL。 现在我不明白你的意思,我将模型作为参数发送,使用带有 DI 的 myRepository 将其填充到 MyService 中,然后返回模型。还有一个问题,我从 Nugget 安装了 Unity DI,但出现无参数错误,我应该手动添加一些东西到我的 Application_Start() 还是应该由安装程序添加? 通常对于那些 DI 框架,你需要为你的项目添加一个新的引用......至少我知道你是为 Autofac 做的。我从未亲自使用过 Unity DI,但我确信他们有某种“入门”指南。我相信这是微软提供的“内置”di 框架。这是使用 Unity 的一个很好的演练。 geekswithblogs.net/danielggarcia/archive/2014/01/23/…

以上是关于在 .net Project MVC 中实现数据访问层最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

您将如何在 asp.net mvc 中实现面包屑助手?

在 ASP.NET MVC 2 中实现 DropDownList 的最佳方式?

如何在 .Net Core MVC 中实现 IAuthorization Filter 时获取连接字符串?

如何在 .NET MVC 2 中实现正确的 HTTP 错误处理?

如何使用 ASP.NET MVC 在 Kendo UI Grid 中实现 N 级嵌套层次结构

csharp 在asp.net mvc中实现工作单元模式