演示文稿,业务和数据层

时间:2009-04-29 05:01:24

标签: c# data-access-layer layer presentation

我刚刚开始使用C#进行编程,并且正在阅读将应用程序/网站划分为三个不同层次的文章,这是最佳实践,但我很难理解具体方法。我正致力于一个宠物项目,更多地关注C#,但我不想从任何坏习惯开始。你能看看我有什么,看看我做得对吗?提供一些关于如何将所有内容分解为不同层次的提示建议?

演示文稿图层

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Project: Ruth</title>
  <link href="CSS/StyleSheet.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <form id="form1" runat="server">
    <div class="Body">
      <div class="Header">
        <div class="Nav">
          <img src="images/Header_Main.gif" alt="" width="217" height="101" />
          <div class="Menu">
            <a href="Default.aspx">
              <img src="images/Header_Home-Off.gif" alt="" /></a>
            <a href="Default.aspx">
              <img src="images/Header_About-Off.gif" alt="" /></a>
            <a href="Register.aspx">
              <img src="images/Header_Register-Off.gif" alt="" /></a>
            <a href="Default.aspx">
              <img src="images/Header_Credits-Off.gif" alt="" /></a>
          </div>
        </div>
      </div>
      <div class="Content">
        <div class="CurrentlyListening">
          <asp:Label ID="lblCurrentListen" runat="server" Text="(Nothing Now)" CssClass="Txt"></asp:Label>
        </div>
        <asp:GridView ID="gvLibrary" runat="server" AutoGenerateColumns="False" DataKeyNames="lib_id" DataSourceID="sdsLibrary" EmptyDataText="There are no data records to display." Width="760" GridLines="None">
          <RowStyle CssClass="RowStyle" />
          <AlternatingRowStyle CssClass="AltRowStyle" />
          <HeaderStyle CssClass="HeaderStyle" />
          <Columns>
            <asp:BoundField DataField="artist_name" HeaderText="Artist" SortExpression="artist_name" HeaderStyle-Width="200" />
            <asp:BoundField DataField="album_title" HeaderText="Album" SortExpression="album_title" HeaderStyle-Width="200" />
            <asp:BoundField DataField="song_title" HeaderText="Track" SortExpression="song_title" HeaderStyle-Width="200" />
            <asp:TemplateField HeaderText="DL">
              <ItemTemplate>
                <a href="http://####/Proj_Ruth/Data/<%# Eval("file_path") %>" class="lnk">Link</a>
              </ItemTemplate>
            </asp:TemplateField>
          </Columns>
        </asp:GridView>
        <asp:SqlDataSource ID="sdsLibrary" runat="server" ConnectionString="<%$ ConnectionStrings:MusicLibraryConnectionString %>" DeleteCommand="DELETE FROM [Library] WHERE [lib_id] = @lib_id" InsertCommand="INSERT INTO [Library] ([artist_name], [album_title], [song_title], [file_path]) VALUES (@artist_name, @album_title, @song_title, @file_path)" ProviderName="<%$ ConnectionStrings:MusicLibraryConnectionString.ProviderName %>" SelectCommand="SELECT [lib_id], [artist_name], [album_title], [song_title], [file_path] FROM [Library] ORDER BY [artist_name], [album_title]" UpdateCommand="UPDATE [Library] SET [artist_name] = @artist_name, [album_title] = @album_title, [song_title] = @song_title, [file_path] = @file_path WHERE [lib_id] = @lib_id">
          <DeleteParameters>
            <asp:Parameter Name="lib_id" Type="Int32" />
          </DeleteParameters>
          <InsertParameters>
            <asp:Parameter Name="artist_name" Type="String" />
            <asp:Parameter Name="album_title" Type="String" />
            <asp:Parameter Name="song_title" Type="String" />
            <asp:Parameter Name="file_path" Type="String" />
          </InsertParameters>
          <UpdateParameters>
            <asp:Parameter Name="artist_name" Type="String" />
            <asp:Parameter Name="album_title" Type="String" />
            <asp:Parameter Name="song_title" Type="String" />
            <asp:Parameter Name="file_path" Type="String" />
            <asp:Parameter Name="lib_id" Type="Int32" />
          </UpdateParameters>
        </asp:SqlDataSource>
      </div>
    </div>
  </form>
</body>
</html>

业务层

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class User
{
  DA da = new DA();

  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string EmailAddress { get; set; }
  public string Password { get; set; }
  public string AccessCode { get; set; }

  public User(string firstName, string lastName, string emailAddress, string password, string accessCode)
  {
    FirstName = firstName;
    LastName = lastName;
    EmailAddress = emailAddress;
    Password = password;
    AccessCode = accessCode;
  }

  public void CreateUser(User newUser)
  {
    if (da.IsValidAccessCode(newUser.AccessCode))
    {
      da.CreateUser(newUser);
    }
  }
}

数据访问层(DAL)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Configuration;

public class DA
{
  public DA()
  {
  }

  public bool IsValidAccessCode(string accessCode)
  {
    bool isValid = false;
    int count = 0;

    using (SqlConnection sqlCnn = new SqlConnection(ConfigurationManager.ConnectionStrings["MusicLibraryConnectionString"].ConnectionString))
    {
      sqlCnn.Open();
      using (SqlCommand sqlCmd = new SqlCommand(String.Format("SELECT COUNT(*) FROM [AccessCodes] WHERE [accessCode_accessCode] = '{0}';", accessCode), sqlCnn))
      {
        count = (int)sqlCmd.ExecuteScalar();
        if (count == 1)
        {
          isValid = true;
        }
      }
    }
    return isValid;
  }

  public void CreateUser(User newUser)
  {
    using (SqlConnection sqlCnn = new SqlConnection(ConfigurationManager.ConnectionStrings["MusicLibraryConnectionString"].ConnectionString))
    {
      sqlCnn.Open();
      using (SqlCommand sqlCmd = new SqlCommand(String.Format("INSERT INTO [Users] (user_firstName, user_lastName, user_emailAddress, user_password, user_accessCode) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}');", newUser.FirstName, newUser.LastName, newUser.EmailAddress, newUser.Password, newUser.AccessCode), sqlCnn))
      {
        sqlCmd.ExecuteNonQuery();
      }
    }
    DeleteAccessCode(newUser.AccessCode);
  }

  public void DeleteAccessCode(string accessCode)
  {
    using (SqlConnection sqlCnn = new SqlConnection(ConfigurationManager.ConnectionStrings["MusicLibraryConnectionString"].ConnectionString))
    {
      sqlCnn.Open();
      using (SqlCommand sqlCmd = new SqlCommand(String.Format("DELETE FROM [AccessCodes] WHERE [accessCode_accessCode] = '{0}';", accessCode), sqlCnn))
      {
        sqlCmd.ExecuteNonQuery();
      }
    }
  }
}

5 个答案:

答案 0 :(得分:9)

乔,

首先要了解的一点是,如果您打算构建基于图层的应用程序,那么您不应该直接在ASPX页面中存储SQL语句(如SqlDataSource所要求的那样)。构建SqlDataSource控件是为了演示使用数据库数据绑定和更新应用程序是多么容易,并且不打算在现实世界的应用程序中使用,因为它有点破坏了拥有BL层和Datalayer的目的。您将在ASPX页面中存储选择/更新/删除/插入语句。

基于图层的应用程序设计的全部目的是封装每个层,以便没有交集。每个层都与其他层的公共接口交互,并且对其内部实现一无所知。

因此,可行的替代方案是使用ObjectDataSource控件。此控件允许您直接绑定到DataLayer或Biz逻辑层,而Biz逻辑层又可以调用Datalayer。直接绑定到Datalayer的缺点是,您将返回公开数据库表模式的数据结构(例如,DataTables或DataViews)。

因此,推荐的逻辑流程如下:

ASPX页面使用DataSource控件绑定到BL类。 此BL类提供适当的函数,例如GetData, UpdateData, DeleteData and InsertData(带有任何必需的重载),这些函数返回ObjectDataSource可以使用和显示的强类型对象或集合。 BL类中的每个公共函数都在内部调用DataLayer来选择/更新/删除/插入数据库中的数据。

Quickstarts

中提供了对ASP.NET中基于图层的设计的精彩介绍

P.S :@Andy提到了适用于所有方案的通用数据层。请参阅this question,了解它的外观示例。

答案 1 :(得分:3)

ASP.NET应用程序中逻辑层的最佳解释来自两个来源。第一个是由Scott Mitchell编写的微软自己的ASP.NET网站,它提供了对逻辑分离的良好介绍。这些教程非常冗长,但我发现它们非常有用。网址为http://www.asp.net/learn/data-access/

我发现的第二个资源非常有用,它由Imar Spaanjaars编写,可用here。这是一篇技术性更强的文章,但提供了一种将结构添加到应用程序的好方法。

我希望有所帮助。

伊恩。

答案 2 :(得分:0)

如果你编写的代码最终是可移植的,你会发现你的应用程序中有3层(或更多层)。

例如 - 不要让您的数据访问层专门针对这个应用程序工作,而是编写它以便您永远不必再次编写它。确保所有函数都可以传递变量,并且不依赖于全局变量(或尽可能少)。到了下一个项目的时候 - 复制并粘贴你的DAL,突然你又开始运行了。

它并没有结束 - 您可能想要为DAL编写一个子层,用于解释MySQL和MSSQL之间的区域(仅作为示例)。或者您可能拥有一个常用功能库,例如文本卫生或CSS生成等。

如果您编写代码以便有一天,您坐下来编写应用程序 - 而且主要涉及剪切和粘贴以前的代码 - 您已经达到了程序员的必杀技。 :)

答案 3 :(得分:0)

分层应用程序背后的整个想法是每个层不依赖于下面层的实现细节。例如,在您的代码中,您在表示层中有一个T-SQL语句。这意味着您的表示层与数据库(底层)直接相关。如果在数据库中进行更改,则还必须在表示层中进行更改。理想情况下,这不是你想要的。表示层应仅关注呈现数据,而不关心如何检索数据。假设您将整个数据库移动到CSV文件中(我知道,疯狂的想法),那么您的表示层根本不应该意识到这一点。

理想情况下,您有一个业务层方法,只返回您要向用户显示的数据。您应该查看ObjectDataSource而不是SqlDataSourceSqlDataSource适用于小型原型项目,但不应将其用于任何更严肃的项目。

在业务层和数据层之间,您应该有类似的分离。数据层负责从某个存储位置(数据库,CSV文件,Web服务......)获取所需的数据。同样,理想情况下,业务层不应该依赖于数据层的实现细节。例如,如果您正在与SQL Server交谈,则不应将SqlDataReader实例返回到业务层。通过这样做,您可以在数据层的实现细节上创建业务层的依​​赖关系:它从中检索数据的实际数据库。

在实践中,您会发现业务层确实以某种方式依赖于数据层的实现细节,通常这并不是件坏事。您最后一次决定何时切换数据库是什么时候?但是,尽可能地消除依赖关系并隔离实现细节几乎总会导致应用程序更易于维护和理解。

您可以找到类似的解释here

答案 4 :(得分:0)

除了他的问题的主旨之外,我建议你看看ASPNET_REGSQL来配置你的SQL数据库来处理.Net的内置成员/配置文件/角色能力。它会消除很多用于创建/更新用户等的粗略和麻烦。我没有使用过很多个人资料,但是它允许你为用户“添加”额外的属性,例如: AccessCode。

如果您正在处理已经进行用户身份验证等的现有数据库结构,则可以创建一个自定义成员资格提供程序,该提供程序将利用现有的数据库表和存储过程。