3层架构问题

时间:2011-02-26 12:24:36

标签: c# asp.net architecture

我正在尝试使用ASP.NET和C#制作简单的3层架构应用程序。 我走向循环依赖的问题。 我在商务层有学生班。 我有一个使用这些方法的表示层接口:

void SaveStudent(Student student);
Student[] GetStudents();

这看起来不错。

但是我还使用这些方法从Data Access层到Business的接口:

void InsertStudent(Student student);
Student[] ReadAllStudents();

问题在于学生班。 由于我的业务层依赖于DAL,因此我无法从数据访问层引用业务层。我知道DAL不应该依赖于业务层。但不知道如何解决这个问题。

我应该如何传递数据?

如果我将Student类放到DAL那么我的表示层将被强制依赖于数据访问层,这是不好的。

我之前从未尝试过制作3层架构。

如何解决这个问题? 如果需要,可以随意更改我的界面方法。

6 个答案:

答案 0 :(得分:4)

这个问题通常通过将您的Student类放在一个单独的模型程序集(称之为您喜欢的程序)中来解决,该程序集将在所有层中共享。在大规模的n层体系结构中,这些类通常只包含数据,通常称为数据转换对象(DTO)。作用于这些对象的任何业务逻辑将保存在业务层(BL)中,该业务层通常作为服务公开(即WCF)。该服务只是一个中介,即使所有代码都位于同一台机器甚至同一个进程中,您也可以通过这种方式进行设计。您至少需要在程序集层面分离您的问题(UI,业务层,DTO,DAL)。

在您的情况下,GetStudents方法将在业务层服务上公开,并将返回Student DTO。业务层将保存对数据访问层(DAL)的引用,它将调用InsertStudent。我再次说过DAL和BL都有对模型程序集的引用,但最重要的是DAL没有对BL的依赖。

客户 - >业务层服务 - >数据访问层

< ------------------学生(DTO)------------------>

答案 1 :(得分:2)

n-tier架构中的关键概念是n-th层只知道n+1-th层。因此,您的用户界面将调用业务逻辑,业务逻辑将调用数据访问层。

答案 2 :(得分:1)

您不应该从DAL调用BLL(业务逻辑层),反之亦然。

// Will contain details about your UI - taking data from text fields, etc
// and passing it to the business object
class Student_UI    
{
  Student_BL _blObject = new Student_BL();

  void SaveStudent()  
  {  
    Student student = new Student();
    // Get student details from UI...
    _blObject.SaveStudent(student);  
  }  

  DisplayStudents()  
  {  
    Student[] students = _blObject.GetStudents();  
    // display students...
  }  
}

class Student_BL
{
  Student_DAL _dalObject = new Student_DAL();

  void SaveStudent(Student student)  
  {  
    _dalObject.InsertStudent(student);  
  }  

  Student[] GetStudents()  
  {  
    return _dalObject.ReadAllStudents();  
  }  
}

答案 3 :(得分:1)

您应该在演示文稿层和持久层之间放置 服务 。持久层调用服务,该服务与模型和持久层交互以完成请求。

三层体系结构是客户端/服务器编程的工件。面向服务的体系结构是对问题的更现代化的解决方案。它在演示文稿和其他演示文稿之间注入服务层。有几个好处:

  1. 它使演示文稿与其他所有内容分开,因此您可以随意更改或添加新演示文稿。
  2. 它将业务逻辑封装到可重用的组件中,而不是将其锁定在演示文稿中。
  3. 它使安全性,日志记录等更加清晰,因为您可以使用面向方面的编程来解决跨服务问题的服务调用。
  4. 以下是Java中界面的外观 - 翻译成您选择的语言:

    package model;
    
    public class Foo
    {
        private Long id;
        private String name;
    }
    
    package service;
    
    public interface FooService
    {
        Foo findFoo(Long id);
        List<Foo> findAllFoos();
        void saveAllFoos(List<Foo> foos);
        void delete(Foo foo);
    }
    
    package persistence
    
    public interface GenericDao<K, V>
    {
        List<V> find();
        V find(Long id);
        K save(V value);
        List<K> save(List<V> values);
        void update(V value);
        void delete(V value);
    }
    

    所以presentation->service->persistence; service使用模型和持久性包来完成请求,这些请求应映射到用例。

    您的演示文稿根本不需要了解模型对象。发送类似XML或JSON或其他一些序列化格式的内容,演示文稿只是呈现它。

答案 4 :(得分:1)

您需要再引入一个名为Model的图层。该层将Student类定义为仅数据对象。 (此图层中没有保存或获取方法)。

Model层可以在自己的项目中(因此它自己的dll)。现在在所有层(Presentation,Business和DA)中引用dll。使用Student类型仅保存数据元素。

在Business层中,引用DA层并使用具有SaveStudent方法的Student类。在DA层中,仅引用Model层并实现save student方法。 (注意,这只显示了这些层应该具有的层和类,理想情况下,类应该实现为此目的而设计的接口,但这本身不是您问题的一部分)

namespace Model{
class Student
{
    public string Name { get; set; }
    public String Address { get; set; }
    // more student properties here ..
    // No methods like SaveStudent in this class , thats up into the business layer
    public bool IsValid(){ // validate the student here }
}

}

namespace Business{
class Student
{
    // call this method from your Presentation layer
    public void SaveStudent(Model.Student student)
    {
        if (student.IsValid())
        {
            DataAccess.StudentDAO student = new DataAccess.StudentDAO();
            student.SaveStudent(student);
        }
        else
        {
            throw new ApplicationException("Invalid student");
        }

    }
}

}

namespace DataAccess{    
class StudentDAO
{
    public void SaveStudent(Model.Student student)
    {
        // impl here to save a student informatin to a 
        // persistent storage
    }

}

}

答案 5 :(得分:0)

下面的代码说明了服务(或业务逻辑)层提供注入存储库的方法的关注点。

这可确保StudentService不知道学生的持久方式和位置。它还使您能够单独测试存储库和服务的行为。

如果您使用的是IoC方法(例如StructureMap或Unity),您可以使用容器进行注射。

public class StudentService
{
  private IStudentRepository _studentRepository;

  public StudentService(IStudentRepository studentRepository)
  {
    _studentRepository = studentRepository;
  }
}

public interface IStudentRepository
{
  void Save(Student student);
  Student[] GetStudents();
}

public class StudentRepository : IStudentRepository
{
 ... implement the methods defined in the interface ...
}