从DAL返回什么给BLL

10

我目前有一个应用程序,包括: 用户界面(网页) BLL (管理器和领域对象) DAL (为我的每个领域对象的数据访问类)。

我在UI中使用以下内容来搜索领域对象。

protect sub Button1_Click()
{
    IBook book = BookManager.GetBook(txtID.Text);
}

这是我的业务逻辑层

public class BookManager 
{
    public static IBook GetBook(string bookId)
    {
        return BookDB.GetBook(bookId);
    }
}

public class Book : IBook
{
    private int? _id
    private string _name;
    private string _genre;

    public string Name
    {
        get { return _name; }
        private set 
        {
            if (string.IsNullOrEmpty(value))
                throw new Exception("Invalid Name");
            _name = value;
        }
    }

    public string Genre
    {
        get { return _serial; }
        private set 
        {
            if (string.IsNullOrEmpty(value))
                throw new Exception("Invalid Genre");
            _genre = value;
        }
    }

    // Other IBook Implementations

}

最后这是我的数据访问层(DAL)

public class BookDB
{
    public static IBook GetBook(int id)
    {
        // Get Book from database using sproc (not allowed to use any ORM)
        // ?? Create IBook Item?
        // return IBook
    }
如何创建一个IBook对象并将其返回给Manager? 我考虑从BookDB返回一个DataTable到BookManager中,让它创建Book对象并返回,但这似乎不太对。 还有其他的方法吗?
编辑: 我决定将每个层分开成一个项目,并在尝试添加对BLL的引用时遇到了DAL层中的循环依赖问题。 我无法从DAL中访问Book类、接口或任何BLL中的内容。 我应该只在这里使用ado.net对象,并让我的manager从ado.net对象创建实际的对象吗? 以下是它的布局方式。
BLL.Managers - BookManager
BLL.Interfaces IBook
BLL.Domain - Book
DAL - BookDB.

谢谢!


通常情况下,数据访问层(DAL)不应该持有业务逻辑层(BLL)的任何引用。然而,BLL 应该持有对 DAL 的引用。 - Martin
如果我要添加一个模型项目,那么我应该将虚拟类和领域类/接口一起移动到该项目中吗? - AlteredConcept
7个回答

5
您可以创建虚拟的书籍对象,只包含数据。获取、设置属性和成员值。这本书为数据库中的每个字段都有一个属性,但不验证任何内容。
您可以从数据库中填充对象,然后将其发送到 BLL。
当您想要保存该对象时,也将其发送到 BLL。
如果有必要,BLL 中的类可以包装这些对象。这样,将其发送回 DAL 就很容易了。
虚拟书籍:
public class DummyBook:IBook 
{
    private nullable<int> _id;
    private string _name;
    private string _genre;

    public string Id
    {
        get {return _id;}
        set {_id = value;}
    }

    public string Name 
    {
        get {return _name;}
        set {_name = value;}
    }

    public string Genre 
    {
        get {return _genre;}
        set {_genre= value;}
    }

}

DAL图书:
public class DALBook 
{
    public static IBook:GetBook(int id) 
    {
        DataTable dt;
        DummyBook db = new DummyBook();

        // Code to get datatable from database
        // ...
        // 

        db.Id = (int)dt.Rows[0]["id"];
        db.Name = (string)dt.Rows[0]["name"];
        db.Genre = (string)dt.Rows[0]["genre"];

        return db;
    }

    public static void SaveBook(IBook book) 
    {
        // Code to save the book in the database
        // you can use the properties from the dummy book
        // to send parameters to your stored proc.
    }
}

BLL书:

public class Book : IBook
{
     private DummyBook _book;

     public Book(int id) 
     {
         _book = DALBook.GetBook(id);
     }

     public string Name 
     {
         get {return _book.Name;}
         set 
         {
            if (string.IsNullOrEmpty(value))
            {
                throw new Exception("Invalid Name");
            }
            _book.Name = value;
         }
     }

     // Code for other Properties ...



     public void Save()
     {
         // Add validation if required
         DALBook.Save(_book);
     }

}

编辑1:虚拟类应该放在它们自己的项目中(例如Model,正如评论中所述)。 引用应按以下方式工作:

DAL引用Model项目。
BLL引用Model和DAL。
UI引用BLL。


你会把DummyBook放在哪层? 现在我有三个项目。 1)Web项目(包含对BLL的引用) 2)BLL类库(包含对DAL的引用) 3)DAL类库(包含对BLL的引用) - AlteredConcept
@AlteredConcept:你可以创建另一个模型项目或类似的东西。 - smoothdeveloper

2

BookDB 应该返回 IBook 实例。我喜欢仓储模式,它的核心是从数据库映射到领域。

仓储实现返回领域对象的实例。这将保护其余代码免受特定持久性实现的影响,这些实现可能会受到技术(数据库类型、Web 服务、[插入其他内容])和保存数据所使用的格式的影响。


你会如何构建上述内容以使用存储库模式而不使用ORM? - AlteredConcept

1

我可能会使用ExecuteReader从数据库中的代码中创建一个对象。原因是datatable比reader有更多的开销,因为它具有更多的功能(并且可能是由reader创建的)。既然您没有使用datatable进行更新/删除,那么您就不需要这种开销。

话虽如此,我会在BookManager类中制作一个静态的帮助方法。

internal static IBook BookFromReader(IDataReader reader)
{
     Book B = new Book();
     B.Prop = reader.GetString(0);
     B.Rinse = reader.Repeat();
     return B;
}

这是因为你拥有接口的原因是你可能想要改变实现方式。你可能最终会有INovel:IBook,IReference:IBook等,然后你会想在数据层中使用抽象工厂实现。
public static IBook GetBook(int id)
{
    // SqlCommand Command = new Command("SQL or sproc", ValidConnection);

    using(IDataReader DR = Command.ExecuteReader(id))
    {
        // checking omitted
        switch(DR.GetInt32(1))
        {
            case 0:
                 return BookManager.BookFromReader(DR);
            case 1:
                 return BookManager.NovelFromReader(DR);
            etc
        }
    }
}

另一个 DAL 的好处是你可以缓存查找结果。你可以有一个字典来保存你已经查找过的书籍,以减少在返回已经返回的对象时产生额外的数据库调用。当进行更新时,你会移除缓存实体......这是另一篇文章。
如果你使用多个程序集,接口和辅助方法需要放置在一个中立(非依赖)程序集中。现在,在博客圈中,有人倾向于使用更少的程序集,这意味着更少的依赖关系等。
这里有一个我读过的关于这个主题的博客链接: http://codebetter.com/blogs/patricksmacchia/archive/2008/12/08/advices-on-partitioning-code-through-net-assemblies.aspx 最终,我认为答案是数据层将返回一个你的接口实例到业务层。
祝你好运 :-)

谢谢Ben,这很有帮助。由于BookManager调用了BookDB.GetBook,所以从BookDB.GetBook方法内部调用它似乎有点奇怪。不知道为什么我会这样想。再次感谢! - AlteredConcept
你可以将静态帮助方法放在数据访问层(DAL)或业务逻辑层(BLL)中。我怀疑如果提到放置在BLL中,会引起争议。我选择BLL是因为DAL可以被“替换”,而帮助方法仍然适用... - Bennett Dill
我觉得我在这里找到了答案,看到-1这个数字让我很吃惊。请问能否通过评论详细解释一下-1的含义? - Bennett Dill
我没有给你投反对票。但是在做这件事时我遇到了一个问题。请检查原始条目中的编辑部分。 - AlteredConcept
我减去了一个已经提到的原因,将使用DataReader的方法放在BLL中会破坏BLL和DAL之间的关注点分离 - 这会导致BLL必须知道存储的具体细节。如果您更改存储怎么办?现在您必须重写BLL和DAL。 - Robert C. Barth
显示剩余2条评论

1
在我看来,你永远不应该让数据访问层(DAL)访问业务逻辑层(BLL)。这是一种不必要的依赖关系。
将 Book 类放入一个新项目中(可能名为 DomainModel)可以解决循环引用的问题。你可以这样做:
BLL 项目引用 DAL 和 DomainModel
DAL 项目引用 DomainModel
UI 项目引用 BLL 和 DomainModel
DomainModel 项目没有引用其他项目

0

为了遵循预期的模型,数据访问层(DAL)负责从数据源检索和发送数据。

DAL 不应关心 BLL 使用的任何业务实体,因为它的唯一工作是检索数据并将其以中立对象的形式返回。它必须是中性的可重用性,否则您可能就不需要将层分开,因为这样会打败它的目的。

你的业务逻辑层(BLL)不必关心 DAL 如何实现检索或写入数据。

为了在 BLL 和 DAL 之间进行通信,必须使用中性对象。

你的 BLL 将一个对象的属性作为单独的参数传递给 DAL 中的方法。DAL 中的参数是中性的,使用字符串、int、bool、任何其他 .NET 对象,既不特定于与之通信的数据库版本,也不是仅存在于 BLL 中的特定类型。

DAL 将通过任何方式从任何地方检索数据并将中性数据对象返回给调用者。例如,这可以是 DataSet 或 DataTable,或者任何其他不特定于正在使用的数据库类型/版本的对象。因此 DataSet 和 DataTable 是 System.Data 命名空间中的对象,而不是 System.Data.SQL 等命名空间中的对象。

实质上: - BLL将中性类型传递给DAL(例如:string、int、bool、long、float等) - DAL负责在将这些类型传递给数据源之前将它们转换为数据库特定类型 DAL将中性数据类型返回给BLL(例如:DataSet、DataTable等) - BLL负责使用这些中性数据类型的内容来创建、填充和返回特定的业务实体
你的BLL必须引用你的DAL。就是这样。
当然,你可以完全忽略这个模型,并像以前建议的那样使用IBOOK等方式进行黑客攻击...但是你不会使用预期的模型,最好将所有内容都放入一个单独的程序集中,因为你无法独立维护它。

我在引用维基百科的话:“[数据]层使数据保持中立,独立于应用服务器或业务逻辑。” 给DAL业务对象“愚蠢”的特性并不违背这一点。只要您的业务对象不自我验证,只是一个包装在类中的基本数据类型存储库,那么最好使用它们。如果您执行Employee_DAL.Save(myEmployee.Id, myEmployee.Name, myEmployee.JobTitle)和Employee_DAL.Save(myEmployee)之间没有任何区别,那么第二种方法更加简洁。 - colithium

0
你想要返回的DataTable与数据库相关,对于BLL而言,它不应该关心你使用了哪个数据库和架构是什么。 你可以使用一个DB-Object Mapper将dbtable映射到DAL中的一个对象。

DataTable并不完全与数据库相关。它是特定于关系模型的,但表示一个内存中断开连接的“表格”,并且不连接到任何数据库。它也不会公开有填充它的任何数据库的信息。 - John Saunders

0

如果您不想返回 DataTable,您可以从 BookManager 传入一个 IBook 实现,以便 DAL 进行填充。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接