Pregunta

Acabo de comenzar a programar en C # y estaba leyendo sobre cómo dividir su aplicación / sitio web en las tres capas diferentes fue la mejor práctica, pero me cuesta entender cómo funciona exactamente. Estoy trabajando en un proyecto de mascotas para aprender más sobre C #, pero no quiero empezar con malos hábitos. ¿Puedes ver lo que tengo y ver si estoy haciendo esto correctamente? ¿Ofrecer algunas sugerencias sobre cómo dividir todo en las diferentes capas?

Capa de presentación

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

Capa de acceso a datos (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();
      }
    }
  }
}
¿Fue útil?

Solución

Jon,

Una de las primeras cosas que hay que entender es que si pretende crear aplicaciones basadas en capas, no debería almacenar las sentencias de SQL directamente dentro de las páginas ASPX (como lo requiere el SqlDataSource ). El control SqlDataSource fue creado para demostrar lo fácil que es enlazar y actualizar una aplicación con datos de la base de datos y no está diseñado para ser usado en aplicaciones del mundo real, porque anula un poco el propósito de tener una capa BL y Datalayer si va a almacenar declaraciones Seleccionar / Actualizar / Eliminar / Insertar en la página ASPX.

Todo el propósito del diseño de aplicaciones basado en capas es encapsular cada capa para que no haya intersección. Cada capa interactúa con la interfaz pública de las otras capas y no sabe nada acerca de su implementación interna.

La alternativa viable, por lo tanto, es utilizar el control ObjectDataSource . Este control le permite enlazar directamente a un DataLayer o a una capa de lógica Biz que a su vez puede llamar al Datalayer. El enlace a un Datalayer directamente tiene el inconveniente de que devolverá estructuras de datos que exponen el esquema de las tablas de la base de datos (por ejemplo, DataTables o DataViews).

Por lo tanto, el flujo de lógica recomendado es el siguiente:

La página ASPX utiliza un control DataSource para enlazar a una clase BL. Esta clase de BL proporciona funciones apropiadas como GetData, UpdateData, DeleteData e InsertData (con las sobrecargas necesarias) y estas funciones devuelven objetos o colecciones fuertemente tipados con los que ObjectDataSource pueden trabajar y mostrar. Cada función pública en la clase BL llama internamente al DataLayer para seleccionar / actualizar / eliminar / insertar datos en / desde la base de datos.

Una excelente introducción a este diseño basado en capas en ASP.NET se proporciona en Inicio rápido

P.S : @Andy mencionó los comunicadores de datos genéricos que funcionan con todos los escenarios. Consulte esta pregunta para ver un ejemplo de lo que se vería como.

Otros consejos

La mayor explicación de las capas lógicas en las aplicaciones ASP.NET proviene de dos fuentes. El primero es el propio sitio web ASP.NET de Microsoft, escrito por Scott Mitchell, que proporciona una buena introducción a la separación de la lógica. Los tutoriales son bastante prolijos pero los encontré muy útiles. La URL es http://www.asp.net/learn/data-access/.

El segundo recurso me pareció muy útil a partir de eso, fue escrito por Imar Spaanjaars y está disponible here . Es un artículo mucho más técnico, pero proporciona una excelente manera de agregar la estructura a su aplicación.

Espero que ayude.

Ian.

Si escribes tu código para que sea finalmente portátil, encontrarás que tendrás 3 (o más) capas en tu aplicación.

Por ejemplo, en lugar de hacer que la capa de acceso a datos funcione específicamente para esta aplicación, escríbala para que nunca más tenga que escribirla. Asegúrese de que todas sus funciones puedan pasar variables y que no confíe en las variables globales (o lo menos posible). Cuando llegue el momento de su próximo proyecto, copie y pegue su DAL y, de repente, estará funcionando nuevamente.

Y no termina ahí; es posible que desee escribir una subcapa para su DAL que se interprete entre MySQL y MSSQL (solo como un ejemplo). O puede tener una biblioteca de funciones comunes que realiza, como el saneamiento del texto o la generación de CSS o algo así.

Si escribe su código para que un día, se siente a escribir una aplicación, y en su mayoría implica cortar y pegar el código anterior, ha llegado al nirvana del programador. :)

La idea general de la aplicación de capas en una aplicación es que cada capa no depende de los detalles de implementación de la (s) capa (s) a continuación. Por ejemplo, en su código tiene una declaración T-SQL dentro de su capa de presentación. Esto significa que tiene una dependencia directa de su capa de presentación en su base de datos (la capa inferior). Si realiza un cambio en su base de datos, también debe hacer un cambio en su capa de presentación. Idealmente esto no es lo que quieres. La capa de presentación solo debe preocuparse por la presentación de datos, no sobre cómo recuperarlos. Supongamos que mueves toda tu base de datos a archivos CSV (lo sé, una idea loca), entonces tu capa de presentación no debería estar al tanto de esto en absoluto.

Lo ideal es que tenga un método de capa empresarial que devuelva solo los datos que desea mostrar al usuario. Debería echar un vistazo a ObjectDataSource en lugar de SqlDataSource . SqlDataSource es bueno para pequeños proyectos de creación de prototipos, pero no debe usarlo para proyectos más serios.

Entre la capa empresarial y la capa de datos debería tener una separación similar. La capa de datos es responsable de obtener los datos que desea de alguna ubicación de almacenamiento (base de datos, archivo CSV, servicio web, ...). De nuevo, idealmente, la capa de negocios no debería depender de los detalles de implementación de la capa de datos. Si está hablando con SQL Server, por ejemplo, no debe devolver un SqlDataReader a su capa empresarial. Al hacer esto, creará una dependencia de su capa empresarial en un detalle de implementación de su capa de datos: la base de datos real de la que está recuperando sus datos.

En la práctica, se ve que la capa de negocio depende de los detalles de la implementación de la capa de datos de alguna manera u otra, y generalmente eso no es algo malo. ¿Cuándo fue la última vez que decidiste cambiar de base de datos? Pero eliminar las dependencias y aislar los detalles de la implementación lo más posible casi siempre resulta en una aplicación que es más fácil de mantener y entender.

Puede encontrar una explicación similar aquí .

aparte de la idea principal de su pregunta, te recomendaría que consultes ASPNET_REGSQL para configurar tu base de datos SQL para manejar las capacidades integradas de membresía / perfil / rol de .Net. Se eliminaría un montón de problemas y problemas para crear / actualizar usuarios, etc. No he usado mucho el perfil, pero te permite "seguir" con " atributos adicionales para su usuario, por ejemplo, Código de acceso.

Si está tratando con una estructura de base de datos existente que ya realiza la autenticación de usuarios, etc., podría crear un proveedor de membresía personalizado que aprovecharía las tablas de la base de datos y los procedimientos almacenados existentes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top