Domanda

Ho appena iniziato a programmare in C # e stavo leggendo come dividere la tua applicazione / sito Web in tre diversi livelli è stata la migliore pratica ma sto facendo fatica a capire esattamente come. Sto lavorando a un progetto per animali domestici per appoggiarmi di più su C # ma non voglio iniziare con cattive abitudini. Puoi vedere cosa ho e vedere se lo sto facendo bene? Offrire alcuni suggerimenti su come suddividere tutto nei diversi livelli?

Livello presentazione

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

Livello aziendale

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

Livello di accesso ai dati (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();
      }
    }
  }
}
È stato utile?

Soluzione

Jon,

Una delle prime cose da capire è che se si intende creare applicazioni basate su layer, non è necessario archiviare le istruzioni SQL direttamente all'interno delle pagine ASPX (come richiesto da SqlDataSource ). Il controllo SqlDataSource è stato creato per dimostrare quanto sia facile associare e aggiornare un'applicazione con i dati del database e non è destinato a essere utilizzato nelle applicazioni del mondo reale, poiché in qualche modo vanifica lo scopo di avere un livello BL e Datalayer se si desidera memorizzare le istruzioni Select / Update / Delete / Insert nella pagina ASPX.

L'intero scopo della progettazione di applicazioni basate su layer è di incapsulare ogni layer 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 praticabile, quindi, è usare il controllo ObjectDataSource . Questo controllo consente di associare direttamente a un DataLayer o a un livello logico Biz che a sua volta può chiamare il Datalayer. Il collegamento a un datalayer ha direttamente l'inconveniente di restituire strutture di dati che espongono lo schema delle tabelle del database (ad esempio, DataTables o DataViews).

Quindi, il flusso di logica raccomandato è il seguente:

La pagina ASPX utilizza un controllo DataSource per associarsi a una classe BL. Questa classe BL offre funzioni appropriate come GetData, UpdateData, DeleteData e InsertData (con eventuali sovraccarichi richiesti) e queste funzioni restituiscono oggetti o raccolte fortemente tipizzati con cui ObjectDataSource può funzionare e display. Ogni funzione pubblica nella classe BL chiama internamente il DataLayer per selezionare / aggiornare / cancellare / inserire dati nel / dal database.

Un'eccellente introduzione a questo progetto basato su layer in ASP.NET è fornita nel Quickstarts

P.S : @Andy ha menzionato datalayer generici che funzionano con tutti gli scenari. Vedi questa domanda per un esempio di cosa sarebbe simile.

Altri suggerimenti

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 è seguita da quella ed è stata scritta da Imar Spaanjaars ed è disponibile qui . È un articolo molto più tecnico ma offre un ottimo modo per aggiungere la struttura alla tua applicazione.

Spero che sia d'aiuto.

Ian.

Se scrivi il tuo codice per renderlo portatile, scoprirai che avrai 3 (o più!) livelli nella tua applicazione.

Ad esempio - invece di far funzionare il tuo livello di accesso ai dati specificamente per questa applicazione, scrivilo in modo da non doverlo più scrivere. Assicurati che a tutte le tue funzioni possano essere passate variabili e non fare affidamento su variabili globali (o il meno possibile). Quando arriva il momento del tuo prossimo progetto, copia e incolla il DAL e improvvisamente sei di nuovo attivo e funzionante.

E non finisce qui - potresti voler scrivere un sottostrato per il tuo DAL che interpreti tra MySQL e MSSQL (solo come esempio). Oppure potresti avere una libreria di funzioni comuni che esegui, come risanamento del testo o generazione CSS o qualcosa del genere.

Se scrivi il tuo codice in modo tale che un giorno ti siedi per scrivere un'app - e per lo più comporta il taglio e l'incollaggio del codice precedente - hai raggiunto il programmatore nirvana. :)

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 dal tuo database (il livello inferiore). Se si apporta una modifica al database, è necessario apportare anche una modifica al livello di presentazione. Idealmente questo non è quello che vuoi. Il livello di presentazione dovrebbe preoccuparsi solo di presentare i dati, non di come recuperarli. Supponiamo di spostare l'intero database in file CSV (lo so, pazza idea), quindi il tuo livello di presentazione non dovrebbe esserne affatto consapevole.

Quindi, idealmente, hai un metodo di livello aziendale che restituisce all'utente solo i dati che desideri mostrare. Dovresti dare un'occhiata a ObjectDataSource anziché SqlDataSource . SqlDataSource è utile per piccoli progetti di prototipazione, ma non dovresti usarlo per altri progetti più seri.

Tra livello aziendale e livello dati dovresti avere una separazione simile. Il livello dati è responsabile per ottenere i dati desiderati da una 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. Ad esempio, se si sta parlando con SQL Server, non si deve restituire SqlDataReader al tuo livello aziendale. In questo modo crei una dipendenza del tuo livello aziendale da un dettaglio di implementazione del tuo livello dati: il database reale da cui sta recuperando i suoi dati.

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

Puoi trovare una spiegazione simile qui .

a parte la principale spinta della sua domanda, ti consiglio di guardare ASPNET_REGSQL per configurare il tuo database SQL per gestire le capacità di appartenenza / profilo / ruolo integrate .Net. Rimuoverebbe molto shag e seccature per la creazione / l'aggiornamento di utenti, ecc. Non ho usato molto il profilo, ma ti permette di "virare su" attributi extra per il tuo utente, ad es. Accesso seguito.

Se si ha a che fare con una struttura DB esistente che già esegue l'autenticazione utente ecc., è possibile creare un provider di appartenenze personalizzato che sfrutterà le tabelle db esistenti e le procedure memorizzate.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top