2013-06-02 70 views
1

我正在使用oracle作为后端的asp.net项目。 Initialy我使用三层架构开发此应用程序,其中UI为aspx页面,BLL和DAL为类库项目。我在BLL和DAL中都使用了静态类和方法。 DAL仅由具有接受由BLL类转发的查询的Select,Execute和ExecuteScalar静态方法的单个类组成。在三层体系结构中正确的功能布局

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; 
} 

这个DAL由BLL类调用的方法是如下 员工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对象将导致填充部门实例。在大多数情况下,我只需要部门识别码而不是整个部门信息。

+1

你的设计是数据驱动设计的一个很好的例子,是的 - 在它的业务逻辑只是一个封装在crud操作。如果你的应用程序只能做到这一点,那真的很好。顺便说一下,如果您打算对BL承担更多责任,请考虑O/RM方法,否则您会花费太多时间来支持您的数据访问层代码库。 – mikalai

回答

1

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

1. UI

允许用户与域模型

2交互。Domain Model/Middle Tier

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

3.数据访问层

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

在这三个方面,我认为最重要的领域是领域模型/中间层。这是应用程序的核心和灵魂,是应用程序真正解决特定业务问题的地方,并为将使用您的软件的组织提供价值。其他两个领域的目的仅仅是将您的领域模型展示给用户(UI)或者允许它被检索或存储(数据层)。

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

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

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

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

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

+0

谢谢joe ..我已经使用了EF,但是当我尝试将它与oracle一起使用时,我遇到了一些问题。由于我没有完全理解EF并且在不同的论坛上阅读过ORM有自己的问题,所以我采用了这种方法。我想知道在网上有没有好的项目源代码,他们使用的是域对象,但没有使用ORM,因此绝对不能使用ORM。 – ZedBee

+1

完成了两件事情(手工制作DAL vs.使用ORM)我个人发现ORM是一个巨大的节省时间的工具,通过快速处理低级管道并让我在中间层上花费更多时间来节省我的痛苦,这对我来说是最有价值的公司。我已经使用nHibernate很多,我认为它与oracle很好。但是,ORM肯定有学习曲线,需要一些时间才能熟练掌握它们。另外,正如你指出的那样,还有其他选择。 –

+0

谢谢..我一定会尝试nHibernate ..但我仍然想看看如何在不使用ORM的情况下以一种漂亮而整洁的方式完成。我希望找到任何这样的开源项目 – ZedBee

1

表示层

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

业务层

如果你的业务逻辑所在。每个选择操作,计算,验证,插入操作,删除,更新操作都属于这里。它不知道用户界面,所以它可以在任何用户界面中工作,无论是web /桌面/移动应用程序,还是相同的业务逻辑。这意味着在这一层,没有使用明显的缓存/会话。相反,你可以把它包装在一些界面中。它不知道存储,这意味着你需要另一层来做存储,而它是DAL

数据访问层

这一层简单地做CRUD操作与存储(平面文件,数据库,Web服务,XML,JSON等),并返回对象模型。就这样。它不知道用户界面和业务规则。这一层的目的是为了便于更换。假设您需要将数据库中的存储替换为xml,这是该层的工作,而不改变其他层。

回答你的问题

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

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

  1. 为Employee存储不影响BLL和UI
  2. 业务逻辑(新的搜索参数)的任何变化的任何变化确实会影响DAL
  3. 您BLL不知道数据访问(连接/ DB背景等),意味着它并不需要知道BLL
在所有
  • 其他明显的映射连接

    现在,假设你使用2层没有BLL你的情况:

    1. 到员工存储的任何更改不影响UI(无BLL)
    2. 任何更改业务逻辑(新的搜索参数)不影响DAL
    3. 您的用户界面知道DAL。如果您将其设计为分离项目,则会添加引用并打破分层。这为您的用户界面直接访问其他DAL提供了机会。

    是的,在你的情况下使用BLL是没有用的,因为你只能分开你的UI和DAL。但是,这是3层的关键是不是?

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

  • +0

    谢谢Fendy ..你谈论业务层的验证,但我正在做的是验证背后的代码(UI)的输入,因为目标是UI应该发送整齐的对象(例如,员工对象)到buisiness层进行进一步操作。我所说的是,虽然每层都有某种形式的验证,但不是主要验证的UI,因为这是捕获用户提供的数据的入口点。在从这些数据中构造对象之前验证此层的用户输入是否更好? – ZedBee

    +1

    如果谈论数据类型(int等),那么是的。无论如何,您都会将模型对象传递给业务逻辑。我在BLL中讨论的是业务逻辑验证,例如:金钱不得低于0,年龄必须高于17,名字长度必须低于32等等。毕竟,如果您想在UI级别复制验证,那么没关系,但如果它需要的业务角色,将其添加到BLL – Fendy

    +0

    谢谢..这是有道理的..我同意业务规则应在BLL验证。 – ZedBee