2009-04-29 6 views
7

방금 ​​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(); 
     } 
    } 
    } 
} 

답변

9

존, 이해하는 첫 번째 것들 중

하나는 레이어 기반 응용 프로그램을 구축하려는 경우합니다 (SqlDataSource의 요구에 따라), 당신은 직접 ASPX 페이지에서 SQL 문을 저장하지 않아야한다는 것입니다. SqlDataSource 컨트롤은 응용 프로그램을 데이터베이스 데이터로 바인딩하고 업데이트하는 것이 얼마나 쉬운지를 보여주기 위해 만들어졌으며 실제 응용 프로그램에서 사용하도록 의도되지 않았습니다. BL 레이어와 Datalayer를 갖게되는 목적을 다소 상쇄하기 때문입니다 ASPX 페이지에서 Select/Update/Delete/Insert 문을 저장하십시오.

레이어 기반 응용 프로그램 디자인의 목적은 각 레이어를 캡슐화하여 교차가 없도록하는 것입니다. 각 레이어는 다른 레이어의 공용 인터페이스와 상호 작용하며 내부 구현에 대해서는 아무 것도 모릅니다.

따라서 실행 가능한 대안은 ObjectDataSource 컨트롤을 사용하는 것입니다. 이 컨트롤을 사용하면 DataLayer 또는 Datalayer를 호출 할 수있는 Biz 로직 계층에 직접 바인딩 할 수 있습니다. Datalayer에 직접 바인딩하는 것은 데이터베이스 테이블 스키마 (예 : DataTables 또는 DataViews)를 노출시키는 데이터 구조를 반환한다는 단점이 있습니다. 다음

따라서 논리 권장 흐름이다

영문 페이지는 BL 클래스에 결합하는 데이터 소스 제어를 사용한다. 이 BL 클래스는 GetData, UpdateData, DeleteData and InsertData (필수 오버로드 포함)과 같은 적절한 함수를 제공하며 이러한 함수는 ObjectDataSource이 작동하고 표시 할 수있는 강력한 형식의 개체 또는 컬렉션을 반환합니다. BL 클래스의 각 public 함수는 내부적으로 DataLayer를 호출하여 데이터베이스에서 데이터를 선택/업데이트/삭제/삽입합니다.

ASP.NET에서이 레이어 기반의 디자인에 대한 훌륭한 소개가 Quickstarts

P.S으로 제공됩니다 @Andy는 모든 시나리오 작업 일반적인 datalayers을 언급했다. 어떻게 생겼는지에 대한 예제는 this question을 참조하십시오.

+0

아마도 주제를 벗어나지 만 DAL의 예에서 연결을 닫아야합니까? –

+0

위의 코드를 어떻게 평가할 수 있습니까? –

+1

@ 존 : 연결을 처리하지 않는 "using 구문"을 사용하기 때문에 명시 적으로 닫을 필요가 없습니다. 즉, 명확성을 위해 닫으십시오. – Cerebrus

0

궁극적으로 이식 가능하도록 코드를 작성하면 애플리케이션에 3 개 이상의 레이어가 있음을 알 수 있습니다.

예를 들어, 데이터 액세스 레이어를이 애플리케이션 용으로 특별히 작동하는 대신, 다시 쓸 필요가 없도록 작성하십시오. 모든 함수가 변수를 전달할 수 있는지 확인하고 전역 변수 (또는 가능한 한 적게)에 의존하지 마십시오. 다음 프로젝트에 시간이 다가 오면 DAL을 복사하여 붙여 넣기 만하면 갑자기 다시 가동됩니다.

그리고 거기서 끝나지 않습니다. MySQL과 MSSQL을 해석하는 DAL 용 하위 레이어를 작성하고 싶을 수도 있습니다. 또는 텍스트 위생 또는 CSS 생성 등의 일반적인 기능을 수행하는 라이브러리가있을 수 있습니다.

언젠가는 코드를 작성하여 앱을 작성하고 이전 코드를 잘라내어 붙여 넣는 작업이 대부분입니다. 프로그래머의 열반에 이르렀습니다. :)

+0

나는 그것이 어떻게 작동 할지를 알지 못한다. 미안하지만 나는이 모든 것을 배우고있다. 하나의 프로그램이 송장에 관한 것이고 다른 프로그램이 미디어 플레이어 인 경우 어떻게 둘 다 동일한 DAL을 사용합니까? 두 프로그램 모두 다른 클래스와 객체를 사용하지 않습니까? –

+0

@ Jon - 서로 다른 개체와 클래스가 있더라도 각 프로젝트 파일은 서로 다를 수 있습니다. – TStamper

+0

@Andy : 두 패러다임은 서로 다른 시나리오에 적합합니다. 때로는 각 프로젝트에 대해 특정 데이터 계층을 갖는 것이 좋습니다. 대부분의 요구 사항에 대해 일반적인 데이터 계층을 사용하는 동안 특정 데이터 계층을 구축해야한다는 필요성을 이해하고 낙담해야한다고 생각하지 않습니다. – Cerebrus

0

응용 프로그램을 레이어하는 모든 아이디어는 각 레이어가 아래의 레이어 구현 세부 정보에 종속되지 않는다는 것입니다. 예를 들어, 코드에서 프레젠테이션 계층 내에 T-SQL 문이 있습니다. 즉, 프리젠 테이션 레이어가 데이터베이스 (하단)에 직접 종속되어 있음을 의미합니다. 데이터베이스를 변경하는 경우 프리젠 테이션 계층을 변경해야합니다. 이상적으로 이것은 당신이 원하는 것이 아닙니다. 프리젠 테이션 레이어는 데이터를 가져 오는 방법에 관한 것이 아니라 데이터를 표시하는 것에 관심을 가져야합니다. 전체 데이터베이스를 CSV 파일로 이동한다고 가정하면 (예 : 미친 아이디어), 프리젠 테이션 계층은이를 전혀 인식하지 않아야합니다.

따라서 이상적으로 사용자에게 표시하려는 데이터 만 반환하는 비즈니스 계층 메서드가 있습니다. SqlDataSource 대신 ObjectDataSource을 확인해야합니다. SqlDataSource은 소규모 프로토 타이핑 프로젝트에 적합하지만 더 심각한 프로젝트에는 사용하지 않아야합니다.

비즈니스 계층과 데이터 계층 사이에는 비슷한 구분이 있어야합니다. 데이터 영역은 일부 저장 위치 (데이터베이스, CSV 파일, 웹 서비스 등)에서 원하는 데이터를 가져와야합니다. 다시 말하면, 비즈니스 계층은 데이터 계층의 구현 세부 사항에 의존해서는 안됩니다. 예를 들어 SQL Server와 통신하는 경우 SqlDataReader 인스턴스를 비즈니스 계층에 반환하면 안됩니다. 이렇게하면 데이터 계층의 구현 세부 사항에 비즈니스 계층의 종속성을 생성 할 수 있습니다. 실제 데이터베이스에서 데이터를 가져옵니다.

실제로 비즈니스 계층은 데이터 계층의 구현 세부 사항에 따라 달라지며 대개 나쁜 것은 아닙니다. 데이터베이스를 전환하기로 결정한 마지막 시간은 언제입니까? 그러나 종속성을 제거하고 구현 세부 사항을 가능한 한 많이 분리하면 거의 항상 유지 및 이해하기 쉬운 응용 프로그램이됩니다.

비슷한 설명은 here입니다.

+0

ObjectDataSource에 대해 알지 못해서 고맙습니다. 이것은 많은 도움이되었습니다! –

3

ASP.NET 응용 프로그램의 논리 계층에 대한 가장 큰 설명은 두 가지 출처에서 비롯됩니다. 첫 번째는 스콧 미첼 (Scott Mitchell)이 작성한 Microsoft의 자체 ASP.NET 웹 사이트로, 논리 분리에 대한 좋은 소개를 제공합니다. 튜토리얼은 매우 어리지만 아주 유용하다고 생각합니다. URL은 http://www.asp.net/learn/data-access/입니다.

매우 유용한 두 번째 리소스는 Imar Spaanjaars가 작성했으며 here입니다. 훨씬 더 기술적 인 기사이지만 구조를 응용 프로그램에 추가하는 좋은 방법을 제공합니다.

도움이 되었기를 바랍니다.

이안.

+0

Imar Spaanjaars의 링크는 그 덕분에 매우 유용했습니다! –

+1

내가 좋아하는 것을 기쁘게 생각합니다. 배워야 할 훌륭한 중급 리소스였습니다. –

+0

저는 Imar의 기사를 보았습니다. 한 가지 간단한 질문 - 악명 높은 "Anemic Domain Model"의 도입이 아닌가? 어쨌든 비즈니스 로직과 비즈니스 로직을 분리해야하는 이유는 무엇입니까? 그리고 그렇게한다면, 나는 왜 비즈니스 객체를 "비즈니스 객체"라고 부를까요? – atiyar

0

그의 질문의 주요 추진력은 제쳐두로 ASPNET_REGSQL에서 .NET 데이터베이스의 기본 멤버십/프로필/역할 기능을 처리하도록 SQL 데이터베이스를 구성하는 것이 좋습니다. 그것은 사용자를 생성/업데이트하기 위해 많은 털복숭이와 번거 로움을 제거 할 것입니다. 프로필을 많이 사용하지는 않았지만 사용자에게 여분의 속성을 "추가"할 수 있습니다. 액세스 코드.

이미 사용자 인증 등을 수행하는 기존 DB 구조를 다루는 경우 기존 db 테이블과 저장 프로 시저를 활용하는 사용자 지정 멤버십 공급자를 만들 수 있습니다.

관련 문제