Question

Je venais juste de commencer à programmer en C # et je lisais à propos de la division de votre application / site Web en trois couches différentes. C'était la meilleure pratique, mais j'ai du mal à comprendre exactement comment. Je travaille sur un projet animalier pour en apprendre davantage sur C # mais je ne veux pas commencer avec de mauvaises habitudes. Pouvez-vous regarder ce que j'ai et voir si je le fais bien? Proposez quelques astuces sur la manière de tout décomposer en différentes couches?

Couche de présentation

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

Couche de gestion

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

Couche d'accès aux données (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();
      }
    }
  }
}
Était-ce utile?

La solution

Jon,

Une des premières choses à comprendre est que si vous avez l'intention de créer des applications basées sur des couches, vous ne devriez pas stocker les instructions SQL directement dans les pages ASPX (comme le requiert le SqlDataSource ). Le contrôle SqlDataSource a été créé pour montrer à quel point il est facile de lier et de mettre à jour une application avec des données de base de données. Il n'est pas destiné à être utilisé dans des applications du monde réel, car il va un peu à l'encontre du but de disposer d'une couche BL. et Datalayer si vous allez stocker les instructions Select / Update / Delete / Insert dans la page ASPX.

L'objectif de la conception d'applications basées sur des couches est d'encapsuler chaque couche de manière à ce qu'il n'y ait pas d'intersection. Chaque couche interagit avec l'interface publique des autres couches et ignore tout de leur implémentation interne.

L’alternative viable consiste donc à utiliser le contrôle ObjectDataSource . Ce contrôle vous permet de vous lier directement à un DataLayer ou à une couche logique Biz qui peut à son tour appeler le Datalayer. La liaison directe à une couche de données a l'inconvénient de renvoyer des structures de données qui exposent le schéma des tables de la base de données (par exemple, DataTables ou DataViews).

Le flux logique recommandé est donc le suivant:

La page ASPX utilise un contrôle DataSource pour se lier à une classe BL. Cette classe BL fournit des fonctions appropriées telles que GetData, UpdateData, DeleteData et InsertData (avec les surcharges requises) et ces fonctions renvoient des objets ou des collections fortement typés avec lesquels ObjectDataSource peut fonctionner. et l'affichage. Chaque fonction publique de la classe BL appelle en interne dans le DataLayer pour sélectionner / mettre à jour / supprimer / insérer des données dans / depuis la base de données.

Une excellente introduction à cette conception basée sur une couche dans ASP.NET est fournie dans Démarrage rapide

P.S : @Andy a mentionné les couches de données génériques qui fonctionnent avec tous les scénarios. Voir cette question pour un exemple de ce qu'il ressemblerait.

Autres conseils

La meilleure explication des couches logiques dans les applications ASP.NET provient de deux sources. Le premier est le site Web ASP.NET de Microsoft, écrit par Scott Mitchell. Il fournit une bonne introduction à la séparation de la logique. Les tutoriels sont assez verbeux mais je les ai trouvés très utiles. L’URL est http://www.asp.net/learn/data-access/.

La deuxième ressource que j’ai trouvée très utile, qui a été écrite par Imar Spaanjaars, est disponible ici . C'est un article beaucoup plus technique, mais il constitue un excellent moyen d'ajouter la structure à votre application.

J'espère que cela aide.

Ian.

Si vous écrivez votre code pour qu'il soit finalement portable, vous constaterez que vous aurez 3 couches (ou plus!) dans votre application.

Par exemple, au lieu de faire en sorte que votre couche d'accès aux données fonctionne spécifiquement pour cette application, écrivez-la de sorte que vous n'ayez plus jamais à l'écrire. Assurez-vous que toutes vos fonctions peuvent être transmises à des variables et que vous ne comptez pas sur des variables globales (ou le moins possible). Le moment venu pour votre prochain projet: copiez et collez votre DAL et, tout à coup, vous êtes à nouveau opérationnel.

Et cela ne s'arrête pas là - vous voudrez peut-être écrire une sous-couche pour votre DAL qui interprète entre MySQL et MSSQL (à titre d'exemple). Vous pouvez également avoir une bibliothèque de fonctions courantes que vous effectuez, telles que l’assainissement du texte ou la génération de CSS, par exemple.

Si vous écrivez votre code de manière à ce qu'un jour, vous vous assoyiez pour écrire une application (ce qui implique principalement de couper et coller du code précédent), vous avez atteint le programmeur nirvana. :)

L’idée de superposer une application est que chaque couche ne dépend pas des détails d’implémentation de la ou des couches ci-dessous. Par exemple, dans votre code, vous avez une instruction T-SQL dans votre couche de présentation. Cela signifie que votre couche de présentation dépend directement de votre base de données (la couche inférieure). Si vous modifiez votre base de données, vous devez également modifier votre couche de présentation. Idéalement, ce n'est pas ce que vous voulez. La couche de présentation ne doit s'intéresser qu'à la présentation des données, et non à la manière de les récupérer. Supposons que vous déplaciez toute votre base de données dans des fichiers CSV (je sais, idée folle), votre couche de présentation ne devrait en être absolument pas consciente.

Idéalement, vous disposez d'une méthode de couche de gestion qui renvoie uniquement les données que vous souhaitez afficher à l'utilisateur. Vous devez consulter ObjectDataSource . au lieu de SqlDataSource . SqlDataSource est utile pour les petits projets de prototypage, mais vous ne devriez pas l'utiliser pour des projets plus sérieux.

Entre la couche de gestion et la couche de données, vous devriez avoir une séparation similaire. La couche de données est chargée de récupérer les données souhaitées depuis un emplacement de stockage (base de données, fichier CSV, service Web, ...). Encore une fois, idéalement, la couche de gestion ne devrait pas dépendre des détails d'implémentation de la couche de données. Si vous parlez à SQL Server par exemple, vous ne devez pas renvoyer un SqlDataReader dans votre couche métier. En faisant cela, vous créez une dépendance de votre couche métier sur un détail d'implémentation de votre couche de données: la base de données à partir de laquelle elle extrait ses données.

En pratique, vous constatez que la couche de gestion dépend des détails de la mise en œuvre de la couche de données, d'une manière ou d'une autre et que ce n'est généralement pas une mauvaise chose. Quand avez-vous décidé de changer de base de données pour la dernière fois? Cependant, éliminer les dépendances et isoler autant que possible les détails de la mise en œuvre permet presque toujours de rendre l’application plus facile à gérer et à comprendre.

Vous pouvez trouver une explication similaire ici . .

En plus de l'essentiel de sa question, je vous recommanderais de consulter ASPNET_REGSQL pour configurer votre base de données SQL afin de gérer les capacités intégrées d'adhésion / de profil / de rôles .Net. Cela supprime beaucoup de problèmes de création / mise à jour d'utilisateurs, etc. Je n'ai pas utilisé beaucoup de profils, mais cela vous permet de "clouer". attributs supplémentaires à votre utilisateur, par exemple. Code d'accès.

Si vous traitez avec une structure de base de données existante qui effectue déjà l'authentification d'utilisateur, etc., vous pouvez créer un fournisseur d'adhésion personnalisé qui exploiterait les tables de base de données et les procédures stockées existantes.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top