三层架构中适当的功能布局

时间:2013-06-02 11:49:37

标签: 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; }
}

EmployeesDAL

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而不是整个部门信息。

2 个答案:

答案 0 :(得分:1)

3层架构部分的目的如下:

<强> 1。 UI

允许用户与您的域模型进行互动

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

包含特定域或业务流程的代码转换。用作表示您的特定业务问题或环境。此层应包含应用程序的所有业务规则和业务对象。

第3。数据访问层

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

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

这意味着您应该尽可能多地花费在域模型/中间层上的工作。这是您解决组织中特定业务问题的地方,也是您真正为公司或客户增值的地方。

在将此一般原则应用于您的特定情况时,我有以下意见/建议。

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

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

  3. 我知道这有点长,但希望它对你有帮助。

答案 1 :(得分:1)

演示文稿层

你已经知道了。这是你的用户界面。它由视图,视图模型和(控制器/演示者)组成。此表示层仅具有与UI(定位,CSS样式等)相关的逻辑。它不知道BLL的实现(只是界面),根本不知道DAL(存储)。

业务层

您的企业逻辑所在的位置。每个选择操作,计算,验证,插入操作,删除,更新操作都属于此处。它不了解UI,因此它可以在任何UI中工作,无论是Web /桌面/移动应用程序,具有相同的业务逻辑。这意味着在这个层中,没有使用明显的缓存/会话。相反,您可以将其包装在某个界面中。它不知道存储,这意味着您需要另一个层来进行存储,它是DAL

数据访问层

这个层只是对存储(平面文件,数据库,Web服务,xml,json等)和返回对象模型进行CRUD操作。就这样。它不知道UI和业务规则。此层的目的是使其易于更换。假设您需要将数据库中的存储替换为xml,这是该层的工作,而不会触及其他层。

回答您的问题

希望你已经知道3层的目的。

现在假设你在你的情况下使用3层BLL:

  1. 对员工存储的任何更改都不会影响BLL和UI
  2. 对业务逻辑(新搜索参数)的任何更改都会影响DAL
  3. 您的BLL不知道数据访问(连接/数据库上下文等),意味着它根本不需要知道连接
  4. BLL的其他明显映射
  5. 现在假设您正在使用2层而不使用BLL:

    1. 对Employee存储的任何更改都不会影响UI(无BLL)
    2. 对业务逻辑(新搜索参数)的任何更改都会影响DAL
    3. 您的用户界面了解DAL。如果将其设计为分离项目,则会添加引用并打破分层。这为您的UI提供了直接访问其他DAL的机会。
    4. 是的,在你的情况下使用BLL是没有用的,因为你只能分享你的UI和DAL。但那就是3层的重点不是吗?

      也就是说,如果您正在使用存储过程选择。您真正需要的是DAL中的通用存储库模式,它将在BLL访问期间为您提供极大的帮助。