2009-04-29 10 views
7

Ho appena iniziato a programmare in C# e stavo leggendo di dividere la tua applicazione/sito web nei tre diversi livelli era la migliore pratica, ma ho difficoltà a capire esattamente come. Sto lavorando a un progetto per animali per avere più informazioni su C#, ma non voglio iniziare su cattive abitudini. Puoi guardare quello che ho e vedere se sto facendo questo giusto? Offri qualche suggerimento su come suddividere tutto in diversi livelli?Presentazione, Business e Data Layer

Presentation Layer

<%@ 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> 

business layer

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); 
    } 
    } 
} 

dati Access Layer (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(); 
     } 
    } 
    } 
} 

risposta

9

Jon,

Una delle prime cose da capire è che se avete intenzione di creare applicazioni basate su livelli, allora non dovrebbe essere memorizzazione istruzioni SQL direttamente all'interno di pagine ASPX (come il SqlDataSource richiede). Il controllo SqlDataSource è stato creato per dimostrare quanto sia facile associare e aggiornare un'applicazione con i dati del database e non è destinato ad essere utilizzato in applicazioni del mondo reale, perché sconfigge lo scopo di avere uno strato BL e Datalayer se si sta andando a store Seleziona/Aggiorna/Elimina/Inserisci istruzioni nella pagina ASPX.

L'intero scopo della progettazione dell'applicazione basata su livelli è incapsulare ogni livello in modo che non vi siano intersezioni. Ogni livello interagisce con l'interfaccia pubblica degli altri livelli e non sa nulla della loro implementazione interna.

L'alternativa valida, quindi, è utilizzare il controllo ObjectDataSource. Questo controllo consente di collegarsi direttamente a un DataLayer oa un livello logico Biz che a sua volta può chiamare Datalayer. Il binding a un Datalayer ha direttamente lo svantaggio di restituire strutture di dati che espongono lo schema delle tabelle del database (ad esempio DataTables o DataView).

Così, il flusso raccomandata di logica è la seguente:

La pagina ASPX utilizza un controllo DataSource per associare a una classe BL. Questa classe BL fornisce funzioni appropriate come GetData, UpdateData, DeleteData and InsertData (con eventuali sovraccarichi necessari) e queste funzioni restituiscono oggetti o raccolte fortemente tipizzati che possono essere visualizzati e visualizzati da ObjectDataSource. Ogni funzione pubblica nella classe BL chiama internamente nel DataLayer per selezionare/aggiornare/eliminare/inserire dati da/verso il database.

Un'eccellente introduzione a questo motivo basato strato in ASP.NET è previsto nel Quickstarts

P.S: @Andy detto datalayers generici che funzionano con tutti gli scenari. Vedi this question per un esempio di come sarebbe.

+0

Off topic forse, ma devo chiudere la connessione nel mio esempio nel DAL? –

+0

Come valuteresti il ​​mio codice attuale sopra? –

+1

@Jon: non è * necessario * chiuderlo esplicitamente perché si sta utilizzando un "costrutto di utilizzo", che eliminerà la connessione. Detto questo, probabilmente dovresti comunque chiuderlo per chiarezza. – Cerebrus

0

Se scrivi il tuo codice per essere alla fine portatile, scoprirai che avrai 3 (o più!) Livelli nella tua applicazione.

Ad esempio, invece di rendere il livello di accesso ai dati specifico per questa applicazione, scriverlo in modo da non doverlo più scrivere di nuovo. Assicurati che tutte le tue funzioni possano essere passate variabili e non ti affidi a variabili globali (o il meno possibile). Quando arriva il momento per il tuo prossimo progetto, copia e incolla il tuo DAL e all'improvviso sei di nuovo attivo e funzionante.

E non finisce qui: potresti voler scrivere un sottolivello per il tuo DAL che interpreti tra MySQL e MSSQL (solo a titolo di esempio). Oppure potresti avere una libreria di funzioni comuni che esegui, come l'igienizzazione del testo o la generazione di CSS o qualcosa del genere.

Se scrivi il tuo codice in modo che un giorno, ti siedi per scrivere un'app - e in gran parte si tratta di tagliare e incollare il codice precedente - hai raggiunto il nirvana del programmatore. :)

+0

Non capisco come possa funzionare, mi dispiace ancora sto imparando tutto questo. Se un programma riguarda le fatture e un altro è un lettore multimediale, come si usa lo stesso DAL per entrambi? Entrambi i programmi non utilizzano classi e oggetti diversi? –

+0

@ Jon- anche se hanno oggetti e classi diversi, possono comunque esserci diversi file di progetto per ogni – TStamper

+0

@Andy: Entrambi i paradigmi sono appropriati per diversi scenari. A volte è preferibile avere un datalayer specifico per ogni progetto. Mentre uso i datalayers generici per la maggior parte dei miei bisogni, capisco la necessità di creare datalayers specifici e non credo che dovrebbero essere scoraggiati. – Cerebrus

0

L'idea alla base della stratificazione di un'applicazione è che ogni livello non dipende dai dettagli di implementazione dei livelli sottostanti. Ad esempio, nel tuo codice hai un'istruzione T-SQL all'interno del tuo livello di presentazione. Ciò significa che hai una dipendenza diretta del tuo livello di presentazione sul tuo database (il livello inferiore). Se apporti una modifica al tuo database, devi anche apportare una modifica al tuo livello di presentazione. Idealmente questo non è quello che vuoi. Il livello di presentazione dovrebbe preoccuparsi solo della presentazione dei dati, non di come recuperarli. Supponiamo di spostare l'intero database in file CSV (lo so, idea pazza), quindi il tuo livello di presentazione non dovrebbe essere a conoscenza di questo.

Quindi, idealmente, si dispone di un metodo del livello aziendale che restituisce solo i dati che si desidera mostrare all'utente. Dovresti dare un'occhiata a ObjectDataSource invece di SqlDataSource. SqlDataSource è utile per piccoli progetti di prototipazione, ma non dovresti usarlo per progetti più seri.

Tra livello aziendale e livello dati si dovrebbe avere una separazione simile. Il livello dati è responsabile per ottenere i dati desiderati da qualche posizione di archiviazione (database, file CSV, servizio Web, ...). Ancora una volta, idealmente, il livello aziendale non dovrebbe dipendere dai dettagli di implementazione del livello dati. Se stai parlando con SQL Server, ad esempio, non dovresti restituire un'istanza SqlDataReader al tuo livello aziendale. In questo modo crei una dipendenza del tuo livello aziendale su un dettaglio di implementazione del tuo livello dati: il vero database dal quale recupera i suoi dati.

In pratica, si vede che il livello aziendale dipende in qualche modo dai dettagli di implementazione del livello dati e in genere non è una cosa negativa. Quando è stata l'ultima volta che hai deciso di cambiare database? Tuttavia, eliminare le dipendenze e isolare il più possibile i dettagli di implementazione porta quasi sempre a un'applicazione più facile da gestire e comprendere.

È possibile trovare una spiegazione simile here.

+0

Grazie, non sapevo di ObjectDataSource. Questo ha aiutato molto! –

3

La più grande spiegazione dei livelli logici nelle applicazioni ASP.NET proviene da due fonti. Il primo è il sito Web ASP.NET di Microsoft scritto da Scott Mitchell che fornisce una buona introduzione alla separazione della logica. I tutorial sono piuttosto prolissi ma li ho trovati molto utili. L'URL è http://www.asp.net/learn/data-access/.

La seconda risorsa che ho trovato molto utile è stata seguita da quello ed è stata scritta da Imar Spaanjaars ed è disponibile here. È un articolo molto più tecnico ma fornisce un ottimo modo per aggiungere la struttura alla tua applicazione.

Spero che questo aiuti.

Ian.

+0

Il link di Imar Spaanjaars è stato molto utile grazie per questo! –

+1

Sono contento che ti piaccia, l'ho trovato un'ottima risorsa intermedia da cui imparare. –

+0

Ho appena guardato l'articolo di Imar. Solo una semplice domanda - non è forse l'introduzione del famigerato "modello di dominio anemico"? Intendo perché dovrei separare la mia logica di business dai miei oggetti di business, comunque? E se lo facessi, perché dovrei chiamare l'oggetto business "oggetti business" ?? – atiyar

0

per mettere da parte la spinta principale della sua domanda, ti consiglierei di guardare ASPNET_REGSQL per configurare il tuo database SQL per gestire le abilità di appartenenza/profilo/ruolo integrate di .Net. Rimuoverà un sacco di shag e problemi per la creazione/aggiornamento degli utenti, ecc. Non ho usato molto il profilo, ma ti permette di "attaccare" attributi extra al tuo utente, ad es. Codice d'accesso.

Se si ha a che fare con una struttura DB esistente che già esegue l'autenticazione dell'utente ecc., È possibile creare un provider di appartenenza personalizzato che utilizzi le tabelle DB e le stored procedure esistenti.

Problemi correlati