Презентация, бизнес и уровень данных - PullRequest
7 голосов
/ 29 апреля 2009

Я только начал программировать на 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();
      }
    }
  }
}

Ответы [ 5 ]

9 голосов
/ 29 апреля 2009

Джон,

Первое, что нужно понять, это то, что если вы намереваетесь создавать приложения на основе слоев, вам не следует хранить операторы SQL непосредственно на страницах ASPX (как этого требует SqlDataSource). Элемент управления SqlDataSource был создан, чтобы продемонстрировать, как легко связать и обновить приложение с данными базы данных, и он не предназначен для использования в приложениях реального мира, потому что он в некотором роде отрицает необходимость иметь слой BL и слой данных, если вы собираюсь сохранить Выберите / Обновить / Удалить / Вставить операторы на странице ASPX.

Вся цель разработки приложений на основе уровней заключается в том, чтобы инкапсулировать каждый уровень, чтобы не было пересечений. Каждый уровень взаимодействует с открытым интерфейсом других уровней и ничего не знает об их внутренней реализации.

Поэтому жизнеспособной альтернативой является использование элемента управления ObjectDataSource. Этот элемент управления позволяет связывать напрямую с DataLayer или с уровнем логики Biz, который, в свою очередь, может вызывать Datalayer. Привязка к Datalayer напрямую имеет тот недостаток, что вы будете возвращать структуры данных, которые представляют схему таблиц базы данных (например, DataTables или DataViews).

Итак, рекомендуемый поток логики выглядит следующим образом:

Страница ASPX использует элемент управления DataSource для привязки к классу BL. Этот класс BL предоставляет соответствующие функции, такие как GetData, UpdateData, DeleteData and InsertData (с любыми необходимыми перегрузками), и эти функции возвращают строго типизированные объекты или коллекции, с которыми ObjectDataSource может работать и отображать. Каждая открытая функция в классе BL внутренне вызывает в DataLayer для выбора / обновления / удаления / вставки данных в / из базы данных.

Отличное введение в этот дизайн на основе слоев в ASP.NET представлено в Quickstarts

P.S : @Andy упомянул универсальные слои данных, которые работают со всеми сценариями. См. этот вопрос для примера того, как это будет выглядеть.

3 голосов
/ 29 апреля 2009

Наилучшее объяснение логических слоев в приложениях ASP.NET происходит из двух источников. Первый - это собственный веб-сайт Microsoft ASP.NET, написанный Скоттом Митчеллом, который представляет собой хорошее введение в разделение логики. Уроки довольно многословны, но я нашел их очень полезными. URL-адрес http://www.asp.net/learn/data-access/.

Второй ресурс, который я нашел очень полезным, был написан Imar Spaanjaars и доступен здесь . Это гораздо более техническая статья, но она предоставляет отличный способ добавить структуру в ваше приложение.

Надеюсь, это поможет.

Ян.

0 голосов
/ 01 октября 2009

В дополнение к основному направлению его вопроса, я бы порекомендовал вам взглянуть на ASPNET_REGSQL, чтобы настроить базу данных SQL для обработки встроенных в .Net возможностей членства / профиля / роли. Это избавило бы от лишних проблем и лишних хлопот при создании / обновлении пользователей и т. Д. Я не очень много использовал профиль, но он позволяет вам «привязывать» дополнительные атрибуты к вашему пользователю, например, AccessCode.

Если вы имеете дело с существующей структурой БД, которая уже выполняет аутентификацию пользователя и т. Д., Вы можете создать собственного поставщика членства, который будет использовать существующие таблицы БД и хранимые процедуры.

0 голосов
/ 29 апреля 2009

Вся идея создания слоя приложения заключается в том, что каждый уровень не зависит от деталей реализации нижеследующего (ых) слоя (ов). Например, в вашем коде у вас есть оператор T-SQL внутри уровня представления. Это означает, что у вас есть прямая зависимость вашего уровня представления от вашей базы данных (нижний уровень). Если вы вносите изменения в свою базу данных, вы также должны внести изменения в свой уровень представления. В идеале это не то, что вы хотите. Уровень представления должен заботиться только о представлении данных, а не о том, как их получить. Предположим, вы переместили всю свою базу данных в файлы CSV (я знаю, безумная идея), тогда ваш уровень представления не должен знать об этом вообще.

Так что в идеале у вас есть метод бизнес-уровня, который возвращает только те данные, которые вы хотите показать пользователю. Вам следует взглянуть на ObjectDataSource вместо SqlDataSource. SqlDataSource хорош для небольших проектов прототипирования, но вы не должны использовать его для более серьезных проектов.

Между бизнес-уровнем и уровнем данных должно быть аналогичное разделение. Уровень данных отвечает за получение нужных данных из некоторого места хранения (базы данных, файла CSV, веб-службы и т. Д.). Опять же, в идеале, бизнес-уровень не должен зависеть от деталей реализации уровня данных. Если вы говорите, например, с SQL Server, вы не должны возвращать экземпляр SqlDataReader на свой бизнес-уровень. Делая это, вы создаете зависимость вашего бизнес-уровня от деталей реализации вашего уровня данных: фактической базы данных, из которой он получает свои данные.

На практике вы видите, что бизнес-уровень действительно так или иначе зависит от деталей реализации уровня данных, и обычно это неплохо. Когда вы в последний раз решили переключать базы данных? Но устранение зависимостей и максимально возможная изоляция деталей реализации почти всегда приводит к тому, что приложение легче поддерживать и понимать.

Подобное объяснение вы можете найти здесь .

0 голосов
/ 29 апреля 2009

Если вы напишите свой код, чтобы он был в конечном итоге переносимым, вы обнаружите, что в вашем приложении будет 3 (или более!) Слоя.

Например - вместо того, чтобы заставить ваш уровень доступа к данным работать специально для этого одного приложения, напишите его, чтобы вам никогда не пришлось писать его снова. Убедитесь, что все ваши функции могут быть переданы переменные, и вы не полагаетесь на глобальные переменные (или как можно меньше). Когда придет время для вашего следующего проекта - скопируйте и вставьте свой DAL, и вдруг вы снова заработаете.

И это еще не все - вы можете написать подслой для вашего DAL, который интерпретирует между MySQL и MSSQL (просто в качестве примера). Или у вас может быть библиотека общих функций, которые вы выполняете, таких как санация текста или генерация CSS или что-то в этом роде.

Если вы пишете свой код так, что однажды вы садитесь за написание приложения - и это в основном включает в себя вырезание и вставку предыдущего кода - вы достигли нирваны программиста. :)

...