将数据访问逻辑从业务层移动到数据访问层



我正在做一个具有数据访问层(DAL)的 asp.net mvc应用程序。在完成了90%的数据库CRUD代码后,我问自己是否需要业务层。

但是我应该放什么呢?例如,我在 DAL 中的所有 CRUD 方法都不是单个 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();
                }
            }
    }

将数据访问排除在 BLL 之外的一个很好的理由是,您可以在对业务逻辑进行最少更改的情况下切换数据库或数据库框架。

例如,如果将所有与 ADO.NET 相关的代码移动到 DAL,然后决定改用实体框架,则只会更改 DAL,而不会更改 BLL。

当然,如果你往往只有很少的业务逻辑,而你的 BLL 只是把工作交给你的 DAL,那么你可能不会从拥有单独的层中获益。对于非常微不足道的应用,这可能是这种情况,但它也可能表明您的 DAL 中隐藏了业务逻辑。

我知道普遍接受的最佳实践是将DAC与BL分开,但我的看法是,如果您使用的是L2S或实体框架之类的东西,那么您已经有一个DAL,并且您的业务逻辑可以进入这些类的部分定义。无需在此基础上添加另一个 DAL。我什至认为 ADO 是一个构成 DAL 的抽象。 顺便说一句,由于您正在使用海峡 ADO,您可能想看看 Dapper.这是一个最小而快速的 DAL。

最新更新