سؤال

لقد بدأت للتو البرمجة بلغة 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();
      }
    }
  }
}
هل كانت مفيدة؟

المحلول

جون,

أحد الأشياء الأولى التي يجب عليك فهمها هو أنه إذا كنت تنوي إنشاء تطبيقات قائمة على الطبقات، فيجب ألا تقوم بتخزين عبارات SQL مباشرة داخل صفحات ASPX (كما هو الحال في SqlDataSource يتطلب).ال SqlDataSource تم إنشاء عنصر التحكم لتوضيح مدى سهولة ربط التطبيق وتحديثه ببيانات قاعدة البيانات وليس المقصود استخدامه في تطبيقات العالم الحقيقي، لأنه يتعارض نوعًا ما مع الغرض من وجود طبقة BL وطبقة بيانات إذا كنت ستقوم بتخزين Select /تحديث/حذف/إدراج البيانات في صفحة ASPX.

الغرض الكامل من تصميم التطبيق القائم على الطبقة هو تغليف كل طبقة بحيث لا يكون هناك تقاطع.تتفاعل كل طبقة مع الواجهة العامة للطبقات الأخرى ولا تعرف شيئًا عن تنفيذها الداخلي.

ولذلك فإن البديل العملي هو استخدام ObjectDataSource يتحكم.يتيح لك عنصر التحكم هذا الارتباط مباشرة بطبقة DataLayer أو بطبقة منطق Biz والتي يمكنها بدورها استدعاء Datalayer.الارتباط المباشر بـ Datalayer له عيب وهو أنك ستعيد هياكل البيانات التي تكشف مخطط جداول قاعدة البيانات (على سبيل المثال، DataTables أو DataViews).

لذلك، فإن التدفق المنطقي الموصى به هو كما يلي:

تستخدم صفحة ASPX عنصر تحكم DataSource للربط بفئة BL.توفر فئة BL هذه وظائف مناسبة مثل GetData, UpdateData, DeleteData and InsertData (مع أي حمولات زائدة مطلوبة) وتقوم هذه الوظائف بإرجاع كائنات أو مجموعات مكتوبة بقوة ObjectDataSource يمكن العمل مع وعرضها.تستدعي كل وظيفة عامة في فئة BL داخليًا DataLayer لتحديد/تحديث/حذف/إدراج البيانات من/إلى قاعدة البيانات.

يتم توفير مقدمة ممتازة لهذا التصميم القائم على الطبقة في ASP.NET في بدايات سريعة

ملاحظة:ذكر @Andy طبقات البيانات العامة التي تعمل مع جميع السيناريوهات.يرى هذا السؤال للحصول على مثال لما سيبدو عليه.

نصائح أخرى

أعظم شرح للطبقات المنطقية في تطبيقات ASP.NET يأتي من مصدرين.الأول هو موقع ASP.NET الخاص بشركة Microsoft والذي كتبه سكوت ميتشل وهو يوفر مقدمة جيدة لفصل المنطق.البرامج التعليمية مليئة بالألفاظ ولكني وجدتها مفيدة جدًا.عنوان URL هو http://www.asp.net/learn/data-access/.

المصدر الثاني الذي وجدته مفيدًا جدًا تابعته، وقد كتبه إيمار سبانجارس وهو متاح هنا.إنها مقالة أكثر تقنية ولكنها توفر طريقة رائعة لإضافة البنية إلى تطبيقك.

أتمنى أن يساعد ذلك.

إيان.

إذا كتبت التعليمات البرمجية الخاصة بك لتكون محمولة في النهاية، فستجد أنه سيكون لديك 3 طبقات (أو أكثر!) في تطبيقك.

على سبيل المثال - بدلاً من جعل طبقة الوصول إلى البيانات الخاصة بك تعمل خصيصًا لهذا التطبيق، اكتبها بحيث لن تضطر أبدًا إلى كتابتها مرة أخرى.تأكد من إمكانية تمرير المتغيرات لجميع وظائفك وأنك لا تعتمد على المتغيرات العامة (أو بأقل قدر ممكن).عندما يحين وقت مشروعك التالي - انسخ والصق DAL الخاص بك وفجأة تصبح جاهزًا للعمل مرة أخرى.

ولا ينتهي الأمر عند هذا الحد - قد ترغب في كتابة طبقة فرعية لـ DAL الخاص بك والتي تترجم بين MySQL وMSSQL (فقط كمثال).أو قد يكون لديك مكتبة من الوظائف الشائعة التي تقوم بها، مثل تنقية النص أو إنشاء CSS أو شيء من هذا القبيل.

إذا كتبت الكود الخاص بك بحيث تجلس يومًا ما لكتابة تطبيق ما - والذي يتضمن في الغالب قص ولصق الكود السابق - فقد وصلت إلى نيرفانا المبرمج.:)

الفكرة الكاملة وراء طبقات التطبيق هي أن كل طبقة لا تعتمد على تفاصيل التنفيذ للطبقة (الطبقات) الموجودة أدناه.على سبيل المثال، في التعليمات البرمجية الخاصة بك لديك عبارة T-SQL داخل طبقة العرض التقديمي الخاص بك.هذا يعني أن لديك تبعية مباشرة لطبقة العرض التقديمي الخاصة بك على قاعدة البيانات الخاصة بك (الطبقة السفلية).إذا قمت بإجراء تغيير في قاعدة البيانات الخاصة بك، فيجب عليك أيضًا إجراء تغيير في طبقة العرض التقديمي الخاصة بك.من الناحية المثالية، هذا ليس ما تريده.يجب أن تهتم طبقة العرض التقديمي فقط بتقديم البيانات، وليس بكيفية استرجاعها.لنفترض أنك قمت بنقل قاعدة البيانات بأكملها إلى ملفات CSV (أعلم أنها فكرة مجنونة)، فيجب ألا تكون طبقة العرض التقديمي لديك على علم بهذا على الإطلاق.

لذا، من الناحية المثالية، لديك طريقة طبقة أعمال تقوم بإرجاع البيانات التي تريد عرضها للمستخدم فقط.يجب عليك إلقاء نظرة على ObjectDataSource بدلاً من SqlDataSource. SqlDataSource يعد أمرًا رائعًا لمشاريع النماذج الأولية الصغيرة، لكن لا يجب استخدامه في أي مشاريع أكثر جدية.

بين طبقة الأعمال وطبقة البيانات يجب أن يكون لديك فصل مماثل.طبقة البيانات مسؤولة عن الحصول على البيانات التي تريدها من بعض مواقع التخزين (قاعدة البيانات، ملف CSV، خدمة الويب، ...).مرة أخرى، من الناحية المثالية، يجب ألا تعتمد طبقة الأعمال على تفاصيل تنفيذ طبقة البيانات.إذا كنت تتحدث إلى SQL Server على سبيل المثال، فيجب ألا تقوم بإرجاع ملف SqlDataReader مثيل لطبقة عملك.من خلال القيام بذلك، يمكنك إنشاء تبعية لطبقة عملك على تفاصيل تنفيذ طبقة البيانات الخاصة بك:قاعدة البيانات الفعلية التي يتم استرداد البيانات منها.

من الناحية العملية، ترى أن طبقة الأعمال تعتمد على تفاصيل تنفيذ طبقة البيانات بطريقة أو بأخرى، وهذا ليس بالأمر السيئ عادةً.متى كانت آخر مرة قررت فيها تبديل قواعد البيانات؟لكن التخلص من التبعيات وعزل تفاصيل التنفيذ قدر الإمكان يؤدي دائمًا إلى تطبيق يسهل صيانته وفهمه.

يمكنك العثور على تفسير مماثل هنا.

بغض النظر عن التوجه الرئيسي لسؤاله، أوصيك بالنظر إلى ASPNET_REGSQL لتكوين قاعدة بيانات SQL الخاصة بك للتعامل مع قدرات العضوية/الملف الشخصي/الأدوار المضمنة في .Net.سيؤدي ذلك إلى إزالة الكثير من المتاعب والمتاعب لإنشاء/تحديث المستخدمين، وما إلى ذلك.لم أستخدم الملف الشخصي كثيرًا، لكنه يسمح لك "بإضافة" سمات إضافية للمستخدم، على سبيل المثال.رمز الدخول.

إذا كنت تتعامل مع بنية قاعدة بيانات موجودة والتي تقوم بالفعل بمصادقة المستخدم وما إلى ذلك، فيمكنك إنشاء موفر عضوية مخصص يمكنه الاستفادة من جداول قاعدة البيانات الحالية والإجراءات المخزنة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top