c# - 三层架构中功能的正确布局

标签 c# asp.net architecture n-tier-architecture

我正在开发一个以 oracle 作为后端的 asp.net 项目。最初,我使用三层架构开发了这个应用程序,UI 作为 aspx 页面,BLL 和 DAL 作为类库项目。我在 BLL 和 DAL 中都使用了静态类和方法。 DAL 仅由单个类组成,该类具有 Select、Execute 和 ExecuteScalar 静态方法,它们接受 BLL 类转发的查询。

DAL

private static string connString = ""

private static OracleConnection conn;

public static OracleConnection OpenConn()
{
  if (conn==null)
  {
    conn = new OracleConnection(connString);
  }

  if (conn.State != ConnectionState.Open)
  {
    conn.Open();
  }

  return conn;
}

public static DataTable Select(string query)
{
  DataTable dt = new DataTable();
  OracleDataAdapter da = new OracleDataAdapter(query, OpenConn());
  da.Fill(dt);
  return dt;
}

public static void Execute(string query)
{
  OracleCommand cmd = new OracleCommand(query, OpenConn());
  cmd.ExecuteNonQuery();
}

public static int ExecuteScaler(string query)
{
  OracleCommand cmd = new OracleCommand(query, OpenConn());
  int id = Convert.ToInt32(cmd.ExecuteScalar());
  return id;
}

BLL类调用这个DAL的方式如下 员工 BLL

public static DataTable GetEmployees(int facilityid)
    {
        DataTable dt = new DataTable();
        string q = string.Format("SELECT * FROM ..");
        dt = OraDAL.Select(q);
        return dt;
    }


    public static DataTable AddEmployee(string name, , int departmentid , int employeeType)
    {
        DataTable dt = new DataTable();
        string q = string.Format("INSERT INTO ...");
        dt = OraDAL.Select(q);
        return dt;
    }

现在我正在重构该应用程序。与 BLL 和 DAL 相同的三层体系结构与类库项目相同,还有一个名为 Domain 的附加类库项目,用于包含所有其他项目引用的域类。我使用这些类在层之间传输数据。这次将 DAL 类保持为静态,将 BLL 保持为普通类。我已将大部分功能(查询)移至 DAL。现在,DAL 具有EmployeesDAL、DepartmentsDAL 等类以及包含Select、Execute 和ExecuteScalar 静态方法的通用类。

Employee.cs

public class Employee
{
    public int Id { get; set; }

    public string Name { get; set; }

    public Department department { get; set; }

    public EmployeeType employeeType { get; set; }
}

员工DAL

public static List<Employee> GetEmployees(int departmentid)
    {
        if (departmentid > 0)
        {
            string query = string.Format("SELECT * FROM EMPLOYEES WHERE department='{0}')", departmentid);
            return GetCollection(OraDAL.Select(query));
        }

        return null;
    }

    public static Employee GetEmployee(int employeeid)
    {
        if (employeeid > 0)
        {
            string query = string.Format("SELECT * FROM PMS_EMPLOYEES WHERE Id='{0}'", employeeid);
            return GetSingle(OraDAL.Select(query));
        }

        throw new Exception("Employee id not valid");
    }

    public static int AddEmployee(Employee employee)
    {
        if (employee !=  null)
        {
            string query = string.Format("INSERT INTO PMS_EMPLOYEES (name, department, employee_type) VALUES ('{0}','{1}','{2}')", employee.Name, employee.department.Id, employee.employeeType.Id);
            return OraDAL.Execute(query);
        }

        throw new Exception("Values not valid");
    }

private static List<Employee> GetCollection(DataTable table)
    {
        List<Employee> employees = null;
        if (table != null)
        {
            if (table.Rows.Count > 0)
            {
                employees = new List<Employee>();
                foreach (DataRow row in table.Rows)
                {
                    employees.Add(ReadDataRow(row));
                }
            }
        }
        return employees;
    }

    private static Employee GetSingle(DataTable table)
    {
        if (table != null)
        {
            if (table.Rows.Count > 0)
            {
                DataRow row = table.Rows[0];
                return ReadDataRow(row);
            }
        }

        return null;
    }

    private static Employee ReadDataRow(DataRow row)
    {
        Employee employee = new Employee() 
        {
            Id=int.Parse(row["ID"].ToString()),
            Name=row["NAME"].ToString(),
            employeeType=EmployeeTypesDAL.GetEmployeeType(int.Parse(row["EMPLOYEE_TYPE"].ToString())),
            department=DepartmentsDAL.GetDepartment(int.Parse(row["DEPARTMENT"].ToString()))
        };

        return employee;
    }

EmployeesBLL.cs

 public class EmployeesBLL
{
    public List<Employee> GetEmployees(int departmentid)
    {
        if (departmentid > 0)
        {
            return EmployeesDAL.GetEmployees(departmentid);
        }

        return null;
    }

    public int AddEmployee(Employee employee)
    {
        if (employee != null)
        {
            return EmployeesDAL.AddEmployee(employee);
        }

        throw new Exception("Employee cannot be null");
    }

帖子越来越长,但我希望您能更好地了解情况。 我在新设计中遇到了一些问题,这让我问了这个问题:这是正确的做事方法吗?因为主要目标是避免在不同层之间来回移动数据表。虽然这种新的逻辑编码方式太耗时,但值得付出努力。业务层变得太薄,因为他们提供的唯一服务似乎是从 DAL 调用类似的方法,我首先需要 BLL 吗?如果是,什么情况下可能需要单独的 BLL。

编辑

我在上述设计中注意到的一个问题是数据库调用的数量。调用过多,因为填充对象时,所有子对象也会填充,从而导致对数据库的调用。例如,填充 Employee 对象将导致填充部门实例。在大多数情况下,我只需要部门 ID,而不是整个部门信息。

最佳答案

三层架构各部分的用途如下:

<强>1。用户界面

允许用户与您的域模型交互

<强>2。领域模型/中间层

包含将您的特定域或业务流程翻译成代码。代表您的特定业务问题或环境。该层应包含应用程序的所有业务规则和业务对象。

<强>3。数据访问层

允许将域模型对象保留到数据存储中并从数据存储中检索。数据访问层只是从持久数据存储(即数据库)中获取数据并将其转换为域模型对象,然后获取域模型对象并将它们持久保存到数据存储中。

在这三个领域中,在我看来,迄今为止最重要的领域是领域模型/中间层。这是应用程序的核心和灵魂,也是应用程序实际解决特定业务问题并为将使用您的软件的组织提供值(value)的地方。其他两个区域的目的只是向用户公开您的域模型(UI)或允许检索或存储它(数据层)。

这意味着您应该努力将尽可能多的精力花在领域模型/中间层上。您可以在这里解决组织的特定业务问题,也可以真正为公司或客户增加值(value)。

在将这一一般原则应用于您的具体情况时,我有以下评论/建议。

  1. 您正在手动创建数据访问层并手工编写 SQL 语句。一般来说,这对您的时间来说值(value)非常低。有多个object-relation mapper (ORM) 工具,例如 nHibernateentity framework它将负责从数据存储加载域模型对象并将对象保留回数据存储所需的低级管道。我想你可能想调查这些。我认为使用这些 ORM 工具之一的解决方案将是比您建议的任何一个选项更好的解决方案。

  2. 根据您发布的代码,您的域模型/中间层只是一组属性,没有业务逻辑或业务规则。如果真是这样的话,那就应该如此。但是,最好利用您的时间与利益相关者交谈,并真正努力在域模型中对他们的流程进行建模,因为这是您可以最好地为组织增加值(value)的地方。

我知道这有点啰嗦,但希望对您有所帮助。

关于c# - 三层架构中功能的正确布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16882458/

相关文章:

c# - 为什么可选参数在 Visual Studio 2015 中传递错误值?

asp.net - 如何在 REST Web 服务中处理使用 100 Continue?

c# - 如果达到最大连接数/ session 数,则 asp.net 重定向

C++:带有 API 的应用程序架构

winforms - Windows 窗体应用程序的物理中间层分离

c# - 根据到达时间和旅行时间计算发射时间

c# - 删除或不写入行中的最后一个 ';' 符号

c# - Entity Framework 数据库优先 .Net Core

c# - 将自定义 CSS 应用于 ASP.NET 中的 Crystal Reports

ios - 在选项卡栏中实现 iOS 角标(Badge)通知的最佳方式