2009-04-29 11 views
7

C#でプログラミングを始めたばかりで、アプリケーション/ Webサイトを3つの異なるレイヤーに分割することを読んでいたのがベストプラクティスでしたが、私はペットプロジェクトに取り組んで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(); 
     } 
    } 
    } 
} 

答えて

9

ジョン、

理解するための最初のものの一つは、レイヤーベースのアプリケーションを構築する場合(SqlDataSource必要に応じて)、その後、あなたはASPXページ内で直接SQL文を格納すべきではないということです。 SqlDataSourceコントロールは、アプリケーションをデータベースデータでバインドして更新するのが簡単で、実世界のアプリケーションでは使用されないように設計されています。BLレイヤとDatalayerを持っている場合は、 select/update/delete/insertステートメントをASPXページに格納します。

レイヤーベースのアプリケーション設計の目的は、各レイヤーをカプセル化して交差がないようにすることです。各レイヤは他のレイヤのパブリックインターフェイスとやり取りし、内部実装については何も知らない。

したがって実行可能な代替策は、ObjectDataSourceコントロールを使用することです。このコントロールを使用すると、DataLayerまたはBizロジックレイヤーに直接バインドし、データレイヤーを呼び出すことができます。データレイヤへのバインドには、データベーステーブルのスキーマ(DataTablesやDataViewなど)を公開するデータ構造を返すという欠点があります。

次のように、ロジックの推奨フローは次のとおりです。

ASPXページには、BLクラスにバインドするデータソースコントロールを使用しています。 このBLクラスはGetData, UpdateData, DeleteData and InsertData(必要なオーバーロードを含む)などの適切な関数を提供し、これらの関数は強い型指定のオブジェクトまたはObjectDataSourceが動作して表示できるコレクションを返します。 BLクラスの各パブリック関数は、内部的にDataLayerを呼び出して、データベースとの間でデータの選択/更新/削除/挿入を行います。

ASP.NETでの設計がQuickstarts

P.Sで提供され基づいて、この層への優れた入門:@Andyは、すべてのシナリオで動作する汎用的なdatalayersを述べました。どのように見えるかの例は、this questionを参照してください。

+0

おそらくトピックはありませんが、DALの私の例では接続を閉じる必要がありますか? –

+0

上記の私の現在のコードをどのように評価しますか? –

+1

@Jon: "使用する"構造を使用しているため、明示的に閉じる必要はありません。接続を破棄します。つまり、分かりやすくするために、おそらくそれを閉じておくべきでしょう。 – Cerebrus

0

あなたがコードを書いて最終的に移植可能な場合は、アプリケーションに3つ以上のレイヤーがあることがわかります。

たとえば、データアクセスレイヤーをこの1つのアプリケーション専用にする代わりに、再度書き込む必要がないように書き込みます。すべての関数が変数を渡すことができ、グローバル変数に(またはできるだけ)依存しないようにしてください。あなたの次のプロジェクトに時間が来たら、あなたのDALをコピー&ペーストしてください。

そして、それは終わりではありません。MySQLとMSSQLの間で解釈するDAL用のサブレイヤを作成することができます(例として)。あるいは、テキスト衛生やCSSの生成など何か共通の機能を持つライブラリを用意しているかもしれません。

コードを書くと、ある日、あなたはアプリを書くために座っています。これは主に前のコードの切り取りと貼り付けを含んでいます。プログラマーのnirvanaに達しました。 :)

+0

私はそれがうまくいかない方法を手に入れません。申し訳ありません、私はまだこれをすべて学んでいます。 1つのプログラムが請求書に関するもので、別のプログラムがメディアプレーヤーの場合、両方で同じDALをどのように使用しますか?両方のプログラムが異なるクラスとオブジェクトを使用するのではないでしょうか? –

+0

@ Jon-異なるオブジェクトとクラスを持っていても、それぞれに異なるプロジェクトファイルが存在する可能性があります。 – TStamper

+0

@Andy:両方のパラダイムが異なるシナリオに適しています。場合によっては、プロジェクトごとに特定のデータレイヤーを持つことが望ましい場合もあります。大部分のデータニーズには一般的なデータ層を使用していますが、特定のデータ層を構築する必要性を理解しています。 – Cerebrus

0

アプリケーションをレイヤーすることの背景にあるアイデアは、各レイヤーが以下のレイヤーの実装の詳細に依存しないということです。たとえば、コード内にプレゼンテーションレイヤー内にT-SQLステートメントがあります。これは、プレゼンテーション層がデータベース(最下層)に直接依存していることを意味します。データベースを変更する場合は、プレゼンテーション層を変更する必要があります。理想的にはこれはあなたが望むものではありません。プレゼンテーション層は、データを取得する方法ではなく、データを提示することについてのみ心配する必要があります。あなたのデータベース全体をCSVファイルに移動すると(私は知っている、夢中です)、あなたのプレゼンテーション層はこれをまったく認識すべきではありません。

理想的には、ユーザーに表示するデータだけを返すビジネスレイヤメソッドがあります。 SqlDataSourceの代わりにObjectDataSourceをご覧ください。 SqlDataSourceは小規模なプロトタイププロジェクトには適していますが、それ以上の深刻なプロジェクトには使用しないでください。

ビジネスレイヤとデータレイヤの間には、同様の分離が必要です。データレイヤーは、いくつかの格納場所(データベース、CSVファイル、Webサービスなど)から必要なデータを取得する役割を担います。ここでも理想的には、ビジネスレイヤーはデータレイヤーの実装の詳細に依存するべきではありません。たとえば、SQL Serverと話している場合は、ビジネスレイヤにSqlDataReaderインスタンスを返さないでください。こうすることで、データレイヤの実装の詳細、つまりデータを取得している実際のデータベースに、ビジネスレイヤの依存関係を作成できます。

実際には、ビジネスレイヤーはデータレイヤーの実装の詳細に何らかの形で依存していますが、通常は悪いことではありません。データベースを切り替えることにしたのはいつですか?しかし、依存関係を排除し、実装の詳細を可能な限り分離することは、ほとんどの場合、アプリケーションの保守と理解が容易になります。

同様の説明hereがあります。

+0

ありがとう、私はObjectDataSourceについて知らなかった。これは多くの助けになりました! –

3

ASP.NETアプリケーションのロジックレイヤの最大の説明は、2つのソースから来ています。最初は、Scott Mitchellによって書かれたマイクロソフト自身のASP.NETウェブサイトであり、ロジックの分離についての良い紹介を提供しています。チュートリアルは非常にばかげていますが、私はそれらが非常に有用であることを発見しました。 URLはhttp://www.asp.net/learn/data-access/です。

私は非常に便利だと分かった2番目のリソースは、Imar Spaanjaarsによって書かれており、利用可能ですhereです。それははるかに技術的な記事ですが、あなたのアプリケーションに構造を追加する素晴らしい方法を提供します。

私は役立つことを願っています。

イアン。

+0

Imar Spaanjaarsのリンクは、とても役に立ちました。 –

+1

私はあなたがそれを好きになったことをうれしく思っています。 –

+0

私はImarの記事を見てきました。ただ一つの簡単な質問 - それは悪名高い "貧血ドメインモデル"の導入ではないのですか?とにかく、ビジネスロジックとビジネスオブジェクトを分離する必要があるのはなぜですか?そして私がそうするなら、なぜビジネスオブジェクトを "ビジネスオブジェクト"と呼ぶだろうか? – atiyar

0

彼の質問の主な理由は別として、ASPNET_REGSQLを見て、.NETの組み込みのメンバーシップ/プロファイル/ロールの能力を処理するようにSQLデータベースを構成することをお勧めします。ユーザーの作成や更新などのためにたくさんのシャグや面倒さがなくなります。私はプロファイルを大変に使いませんでしたが、余分な属性をユーザーに「タック」させることができます。アクセスコード。

既にユーザー認証などを行っている既存のDB構造を扱っている場合は、既存のdbテーブルとストアドプロシージャを活用するカスタムメンバーシッププロバイダを作成できます。

関連する問題