Внедрение лучших практик уровня доступа к данным в .net Project MVC

#c# #mysql #asp.net-mvc #dao

#c# #mysql #asp.net-mvc #dao

Вопрос:

Я хотел бы улучшить свой проект .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 в отдельную папку под названием «Помощники», и в ней содержится большинство моих запросов:

 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 в своем проекте.

Комментарии:

1. Вы на правильном пути. Если вы Загуглите термин «Хранилища данных» и «Внедрение зависимостей», это может продвинуть вас на шаг дальше. Общая идея репозитория заключается в том, что у вас есть отдельные классы на уровне модели для доступа к данным (как вы делаете сейчас), но они соответствуют интерфейсам. Когда вы создаете экземпляр своего класса AgencyBean, вы должны принять объект, который реализует этот интерфейс, в качестве аргумента. Преимущество заключается в том, что у вас может быть вторая реализация интерфейса, которая не затрагивает базу данных, используемую только для модульного тестирования. Инструмент DI, такой как Ninject, упрощает кодирование.

2. Теперь я понимаю это лучше, я также хотел внедрить модульное тестирование, и я вижу, что мне нужен DI. спасибо за совет!

Ответ №1:

Итак, вот что я делал в прошлом.

Во-первых, это ваше пространство имен «models»… модели никогда не должны иметь подключения к базе данных. Вместо этого у вас есть отдельный класс, такой как контроллер, который увлажняет некоторую модель.

Во-вторых, у меня был класс «service», который подключается к классу «repository». Класс репозитория реализует интерфейс для определения точного «типа» используемой вами базы данных.. но если это не входит в ваши требования, вам, вероятно, не нужно заходить так далеко.

В-третьих, найдите внедрение зависимостей (иначе DI). Существует несколько фреймворков. Лично я использовал Autofac, но существуют и другие, чтобы упростить работу.

В-четвертых, в ваших классах «контроллеры», «службы» и «ответственные» реализуйте внедрение зависимостей, а также любые интерфейсы, необходимые для формирования контракта.

В-пятых, я бы использовал фактическое пространство имен контроллера и не работал бы над вашим пространством имен моделей, чтобы продвигать http-вызовы band и далее…. Вместо этого создайте действие в своем классе контроллера и создайте экземпляр вашего «agencyBean», наполните его данными и верните эту модель в ваше представление.

По сути, в подобном сценарии вы пытаетесь заставить каждый компонент выполнять то, для чего он предназначен … разбивая обязанности на более мелкие части и сосредотачиваясь на этом. Ваш контроллер должен просто «извлекать» вашу модель и, возможно, выполнять некоторые преобразования по мере необходимости или любую другую логику бизнес-типа.
Ваша служба должна обрабатывать связь между вашим контроллером и уровнем базы данных.
Ваш уровень доступа к данным (т.Е., В данном случае, Некоторый класс «репозитория» …) будет выполнять все эти новые подключения к данным и / или настраивать вызовы хранимых процедур или запросов.

Выполнение этого способа имеет много преимуществ. Некоторые из основных — это удобство обслуживания, удобочитаемость, повторное использование кода. Конечно, это немного усложняет ваш проект с точки зрения размещения файлов где угодно… но это может быть и хорошо. Это намного лучше, чем объединять все в один класс и заставлять его делать все 🙂

Но, к вашему сведению, это из реализации, которую я делал в прошлом… Я уверен, что есть лучшие способы, но эта настройка сработала достаточно хорошо для моей команды и меня.

Вот небольшой пример, использующий часть вашего кода, который вы опубликовали. Я НЕ проверял это на наличие опечаток, и оно не будет компилироваться, но должно помочь дать общее представление о том, о чем я говорю….

 namespace Company.Models
{
    public class AgencyBean
    {
        public AgencyName{get;set;}
        public AgencyId{get;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 connectionString{get;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;
        }
    }
}
  

Комментарии:

1. Я где-то читал, что моя бизнес-логика должна быть в модели, а контроллер должен быть как можно более простым, поэтому я начал кодировать таким образом .. затем я нашел похожий пост, где люди рекомендовали иметь уровень обслуживания, как вы мне указали. Итак, в вашем примере IMyService — это интерфейс, расположенный в новом файле, который содержит мои запросы, верно? Я просто немного запутался в том, где искать определенные файлы, поскольку я не хочу «ломать» этот шаблон MVC…

2. IMyService — это интерфейс, который вы создаете в другом файле, да. Но это НЕ то место, где вы бы писали свои запросы, а что нет. Если вы хотите написать кучу SQL-команд, я бы порекомендовал другой класс «model», такой как «QueryModel», и это будет набор констант, которые возвращают ваши строки sql. Но на самом деле, я лично этого не делаю. Скорее я использую хранимые процедуры / функции на уровне базы данных и вызываю их из кода приложения. Хранимые в БД процедуры / функции будут содержать весь ваш SQL.

3. Теперь я понимаю вашу точку зрения, я отправляю модель в качестве параметра, заполняю ее в MyService, используя MyRepository с DI, а затем возвращаю модель. Еще один вопрос, я установил Unity DI из Nugget, но получаю ошибку без параметров, должен ли я добавить что-то вручную в свой Application_Start() или это то, что должно быть добавлено установщиком?

4. Обычно для этих DI-фреймворков вам нужно добавить новую ссылку на ваш проект… по крайней мере, я знаю, что вы делаете для Autofac. Я лично никогда не использовал Unity DI, но я уверен, что у них есть какое-то руководство по началу работы. Я считаю, что это «встроенная» платформа di, которую предоставляет Microsoft. Вот хорошее пошаговое руководство по использованию Unity. geekswithblogs.net/danielggarcia/archive/2014/01/23 /…