Переместить логику доступа к данным с бизнес-уровня на уровень доступа к данным

Я делаю приложение asp.net MVC с уровнем доступа к данным (DAL). Сделав 90% кода CRUD базы данных, я спросил себя, нужен ли мне бизнес-уровень.

Но что я должен положить туда? Все мои методы CRUD в DAL не являются одиночными. Например, для одной таблицы SQL. В большинстве случаев я делаю много объединений + агрегатные функции sql. Просто упомяну, что я использую ADO.NET, НЕ хранимые процедуры / триггеры.

Затем я снова спросил себя, будет ли такой метод принадлежать бизнес-уровню:

 /// <summary>
 /// Creates a testplan with all teststeps and their default values for a certain template
 /// </summary>
 /// <param name="testplan"></param>
 /// <returns>true if transaction was successfull else false</returns>
 public void CreateTestplan(Testplan testplan)
 {
            try
            {
                using (var con = new SqlConnection(_connectionString))
                using (var trans = new TransactionScope())
                {
                    con.Open();

                    _testplanDataProvider.AddTestplan(testplan,con);
                    _testplanDataProvider.CreateTeststepsForTestplan(testplan.Id, testplan.TemplateId,con);
                    trans.Complete();                   
                }
            }
            catch (SqlException ex)
            {
                ExceptionManager.HandleException(ex);
            }           
        }

Этот метод фактически вызывает ДВА других метода в DAL.

Теперь я спросил себя, зачем вводить дополнительный бизнес-уровень, когда я могу поместить метод CreateTestplan также в класс TestplanDataProvider со всем кодом из обоих методов AddTestplan + CreateTeststepsForTestplan.

Как вы думаете? Это хороший подход?

Я действительно спрашиваю об этом, потому что, по моему мнению, метод CreateTestplan содержит только логику доступа к данным.

ОБНОВЛЕНИЕ:

public void AddTestplan(Testplan testplan, SqlConnection con)
        {

            using (var cmd = new SqlCommand("INSERT INTO TESTPLAN (ReleaseId,TemplateId,CreatedAt,UserId,Name,Duration) VALUES (@ReleaseId,@TemplateId,@CreatedAt,@UserId,@Name,@Duration);Select Scope_Identity();", con))
            {
                var p1 = new SqlParameter("@ReleaseId", testplan.ReleaseId);
                var p2 = new SqlParameter("@TemplateId", testplan.TemplateId);
                var p3 = new SqlParameter("@CreatedAt", testplan.CreatedAt);
                var p4 = new SqlParameter("@UserId", testplan.UserId);
                var p5 = new SqlParameter("@Name", testplan.Name);
                var p6 = new SqlParameter("@Duration", testplan.Duration);

                cmd.Parameters.AddRange(new[] { p1, p2, p3, p4, p5, p6 });
                testplan.Id = Convert.ToInt32(cmd.ExecuteScalar());
            }
        }

 public void CreateTeststepsForTestplan(int testplanId, int templateId, SqlConnection con)
        {
            var teststeps = new List<Teststep>();

            using (var selectCMD = new SqlCommand("SELECT ts.TeststepId, MAX(ts.CreatedAt)FROM Teststep ts INNER JOIN Unit u ON ts.UnitId = u.UnitId Where u.TemplateId = @TemplateId Group by TeststepId", con))
            {
                var p = new SqlParameter("@TemplateId", templateId);
                selectCMD.Parameters.Add(p);

                using (var reader = selectCMD.ExecuteReader())
                {
                    Teststep teststep = null;
                    while (reader.Read())
                    {
                        teststep = new Teststep
                        {
                            Id = Convert.ToInt32(reader["TeststepId"]),
                            CreatedAt = Convert.ToDateTime(reader["CreatedAt"]),
                        };
                        teststeps.Add(teststep);
                    }
                }
            }

            using (var insertCMD = new SqlCommand("INSERT INTO TestplanTeststep (TestplanId,TeststepId,TestState,ErrorText) VALUES (@TestplanId, @TeststepId, @TestState, @ErrorText)", con))
            {
                var p1 = new SqlParameter("@TeststepId", SqlDbType.Int);
                var p2 = new SqlParameter("@CreatedAt", SqlDbType.DateTime);
                var p3 = new SqlParameter("@TestplanId", testplanId);
                var p4 = new SqlParameter("@ErrorText", DBNull.Value);
                var p5 = new SqlParameter("@ErrorScreenshot", DBNull.Value);
                var p6 = new SqlParameter("@TestState", (int)Teststep.TeststepTestState.Untested);

                insertCMD.Parameters.AddRange(new[] { p1, p2, p3, p4, p5 });

                foreach (Teststep step in teststeps)
                {
                    p1.Value = step.Id;
                    p2.Value = step.CreatedAt;
                    insertCMD.ExecuteNonQuery();
                }
            }
    }

2 ответа

Решение

Хорошая причина не допустить доступа к данным из вашего BLL - это возможность переключения баз данных или структур баз данных с минимальными изменениями в вашей бизнес-логике.

Например, если вы переместили весь свой связанный с ADO.NET код в DAL, а затем решили вместо этого использовать Entity Framework, изменится только ваш DAL, а не BLL.

Конечно, если вы склонны иметь очень мало бизнес-логики, и ваш BLL просто передает работу вашему DAL, вы можете не получить выгоды от наличия отдельных уровней. Это может иметь место для очень тривиальных приложений, но это также может указывать на то, что в вашем DAL скрыта бизнес-логика.

Я знаю, что общепринятая лучшая практика состоит в том, чтобы отделить ваш DAC от вашего BL, но я думаю, что если вы используете что-то вроде L2S или Entity Framework, у вас уже есть DAL, и ваша бизнес-логика может перейти к частичным определениям для них. классы. Нет необходимости добавлять еще один DAL поверх этого. Я бы даже посчитал ADO абстракцией, которая составляет DAL. Кстати, так как вы используете пролив ADO, вы можете взглянуть на Dapper. Это минимальный и быстрый DAL.

Другие вопросы по тегам