PDA

View Full Version : نوشتن لایه DataAccess در معماری 3 لایه



shotshat
سه شنبه 23 مرداد 1386, 14:51 عصر
سلام دوستان

می خواستم ببینم در معماری 3لایه، لایه Data Access که مسئولیت دسترسی به پایگاه را دارد رو چطور باید نوشت؟
فقط در همین حد می دونم که باید یک Calss Library براش تعریف کرد اما اینکه توش چی می نویسیم رو از شما کمک میخوام! (چرا با کلاس معمولی نمیشه؟)
آیا همون دستورات اتصال به پایگاه رو که وقتی برنامه یک لایه می نوشتیم باید بنویسیم یا چیز دیگه؟
اگر با یک مثال ساده که با معماری 3 لایه نوشته شده راهنماییم کنید واقعا ممنون میشم.

asgari2005
سه شنبه 23 مرداد 1386, 15:15 عصر
دوست گرامی در مورد این موضوع مطالب مفید در سایت موجود هست لطفا جستجو کنید

shotshat
سه شنبه 23 مرداد 1386, 15:39 عصر
هر چی تو سایت دیدم توضیح چیستی معماری چند لایه بوده و مثال کامل و ساده ی قابل اجرایی که راهنماییم بکند ندیدم . اگر شما سراغ دارید /ادرسش رو بدید ممنون میشم.

PC2st
سه شنبه 23 مرداد 1386, 17:16 عصر
(چرا با کلاس معمولی نمیشه؟)
کی گفته نمیشه!؟ Class Library هم از کلاسهای معمولی ایجاد میشه.

shotshat
سه شنبه 23 مرداد 1386, 18:21 عصر
اگر با یک مثال ساده و قابل اجرا این معماری 3 لایه رو توضیح بدید خیلی لطف می کنید. خیلی تو اینترنت گشتم که مثالی پیدا کنم اما پیدا نکردم (یا بهتره بگم مثالی که ساده باشه و من بفهمم پیدا نکردم).

PC2st
سه شنبه 23 مرداد 1386, 20:19 عصر
یک مثال خیلی ساده:


public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Layer3 l3 = new Layer3();
l3.SayHelloToUser("ABD-9799");
}
}

public class Layer3
{
Layer2 l2 = new Layer2();
public void SayHelloToUser(string userId)
{
string message = l2.GetUserMessage(userId);
MessageBox.Show(message);
}
}

public class Layer2
{
Layer1 l1 = new Layer1();
public string GetUserMessage(string userId)
{
return "Hello " + l1.GetUserName(userId);
}
}

public class Layer1
{
public string GetUserName(string userId)
{
switch (userId)
{
case "ABD-9799":
return "Saeid";
break;
case "QWW-0090":
return "Majid";
break;
case "GJA-2222":
return "Vahid";
break;
default:
throw new Exception("User ID is not registred.");
break;
}
}
}

در هنگامی که From1 لود میشود یعنی متد Form1_Load، ابتدا یک شیئ از Layer3 ساخته میشه که بعدش متد SayHelloToUser صدا زده میشه. و سپس توسط متد SayHelloToUser متد GetUserMessage صدا زده میشه و توسط متد GetUserMessage، متد GetUserName صدا زده میشه که در سر آخر، پس از عبور از لایه های متوالی، برنامه Form1 میتونه متنی رو به کاربر نمایش بده.

Form1 میتونه یک لایه حساب بشه. پس این برنامه دارای 4 لایه بود.
لایه Data Access رو میشه کلاس Layer1 در نظر گرفت.
هر یک از کلاسهای Layer1 و Layer2 و Layer3 میتوانستند در یک اسمبلی جداگانه نوشته شوند و همچنین این اسمبلی ها را میتوانیم روی یک کامپیوتر دیگر قرار بدیم.

نمیدونم درست بود یا خیر... بهرحال دوستان میتوانند بحث رو کامل تر کنند.

shotshat
سه شنبه 23 مرداد 1386, 20:56 عصر
دوست عزیز بهتره سوالم رو یک جور دیگه مطرح کنم :
من یک برنامه login رو که قبلا نوشته بودم به شکل زیر 3لایه کردم:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace Login
{
publicpartialclassForm1 : Form
{
public Form1()
{
InitializeComponent();
}
staticBoolean drz;
#region DAL
publicclassDAL
{
publicvoid Search(string UserName,string PassWord)
{
SqlConnection cn = newSqlConnection();
cn.ConnectionString = "Server=.;DataBase=checku;UID=sa";
SqlCommand cmd = newSqlCommand();
cmd.Connection = cn;
SqlDataAdapter da = newSqlDataAdapter(cmd);
DataSet ds = newDataSet();
SqlDataReader dr;
if (cn.State == ConnectionState.Closed)
cn.Open();
cmd.Parameters.Clear();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "Usp_login";

cmd.Parameters.Add("@UserName", SqlDbType.NVarChar);
cmd.Parameters["@UserName"].Value = UserName;
cmd.Parameters.Add("@Pass", SqlDbType.Int);
cmd.Parameters["@Pass"].Value = PassWord;
dr = cmd.ExecuteReader();
drz=dr.Read();
cn.Close();
}
}
#endregion
#region BLL
publicclassBLL
{
publicvoid Search(string UserName,string PassWord)
{
DAL obj_DAL = newDAL();
obj_DAL.Search (UserName,PassWord );

if (drz)

{
MessageBox.Show("valid UserName or Password", "Correct", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
MessageBox.Show("Invalid UserName or Password", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);


}
}

#endregion
#region Peresentation
privatevoid btnlogin_Click(object sender, EventArgs e)
{
BLL obj_BLL = newBLL();
obj_BLL.Search (txtuser.Text,txtpass .Text );
}
#endregion



حالا میخوام ببینم چه جوری میشه لایه( DataAccess(DAL رو طورد بنویسم که برای فرم های دیگه هم قابل استفاده باشه؟ اینجوری که من نوشتم فقط برای همین فرم قابل دسترسی است. مثل اینکه برای این کار باید یک لایبرری نوشت!
وقتی که یک کلاس جدید add می کنم وکد های DAL رو توش میذارم اتصال به دیتا بیسم برقرار نمیشه و ارور میده.

اصلا درست 3 لایه اش کردم؟

PC2st
سه شنبه 23 مرداد 1386, 21:21 عصر
:-) اینطوری که شما نوشتید باید از کلاسهای DAL و BLL بصورت Form1.DAL و Form1.BLL استفاده بشه و اگر خواستید اینطوری نباشه، در قسمت کدها از region DAL تا endregion رو در بیرون از کدهای From1 بگذار تا در تمام فرم ها قابل دسترسی باشه.
مثلا به شکل زیر:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace WindowsApplication1
{
#region Peresentation
public partial class Form1 : Form
{
static Boolean drz;

public Form1()
{
InitializeComponent();
}

private void btnlogin_Click(object sender, EventArgs e)
{
//your code here...
}
}
#endregion
#region DAL
public class DAL
{
public void Search(string UserName, string PassWord)
{
//your code here...
}
}
#endregion
#region BLL
public class BLL
{
public void Search(string UserName, string PassWord)
{
//your code here...
}
}
#endregion
}

تفاوتش اینه که کلاسهای DAL و BLL در خارج از محدوده کدهای Form1 قرار دارند، پس دیگه جزء Form1 نیستند. البته میتونستید کلاسهای DAL و BLL رو در یک فایل جداگانه قرار دهید، مثلا DAL.cs و BLL.cs و یا اینکه اینها رو درون یک اسمبلی دیگه قرار میدادید، مثلا برای هر کدوم DAL.dll و BLL.dll ...
کل کلاس Form1 هم در بین region Peresentation و endregion قرار میگیره.

shotshat
سه شنبه 23 مرداد 1386, 21:50 عصر
این برنامه ام وقتی اینطوری همه اش توی یک صفحه است درست کار میکنه اما وقتی DAL اش رو جدا کردم و یک class جدید add کردم وکد هام رو اونجا نوشتم n تا ارور داد، حالا نمی دونم باید تغییری توی کد هام می دادم یا مشکل جای دیگه است نمی دونم!
خدا خیرت بده، اگه بگی این DAL و BLL ام رو چه جوری dll کنم که کار کنه فکر کنم مشکلم حل بشه(ایشالا)

PC2st
سه شنبه 23 مرداد 1386, 23:35 عصر
اما وقتی DAL اش رو جدا کردم و یک class جدید add کردم وکد هام رو اونجا نوشتم n تا ارور داد
وقتی که کلاس DAL را در فایل DAL.cs ریخته اید، به احتمال زیاد، فضاهای نام لازم را در کلاس DAL.cs اضافه نکرده اید.

مثلا برای فایل DAL.cs:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace WindowsApplication1
{
#region DAL
public class DAL
{
public void Search(string UserName, string PassWord)
{
//your code here...
}
}
#endregion
}


حال باید بدون خطا، کار کند.
اگر یادتان باشد، در کلاس Form1 یک فیلد بنام drz از نوع Boolean تعریف کرده بودید، اما وقتی که کلاس BLL را از کلاس Form1 جدا کردیم، دیگر کلاس BLL نمیتواند به فیلد drz از کلاس Form1 دسترسی داشته باشد، و چون این متغیر در Form1 استفاده ای ندارد و مورد استفاده کلاس BLL میباشد پس باید این فیلد را به این کلاس افزود و از کلاس Form1 حذف کرد، پس اگر کلاس BLL را در فایل BLL.cs بریزیم، آن فایل باید شبیه به زیر باشد:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace WindowsApplication1
{
#region BLL
public class BLL
{
static Boolean drz;

public void Search(string UserName, string PassWord)
{
DAL obj_DAL = new DAL();
obj_DAL.Search(UserName, PassWord);
if (drz)
MessageBox.Show("valid UserName or Password", "Correct", MessageBoxButtons.OK, MessageBoxIcon.Error);
else
MessageBox.Show("Invalid UserName or Password", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
#endregion
}


و کدهای داخل Form1 باید مثل زیر باشه:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace WindowsApplication1
{
#region Peresentation
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnlogin_Click(object sender, EventArgs e)
{
//your code here...
}
}
#endregion
}


حالا هر کلاس در یک فایل قرار داره.

درسته تمام این کلاسهای در یک فایل مجزا قرار دارند اما همه آنها در فضای نام WindowsApplication1 تعریف شده اند.

حالا فرض کنیم میخواهیم که کلاس DAL و کلاس BLL را در یک Class Library قرار دهید (فایل dll).
برای این منظور از منوی File گزینه Add و از آن گزینه New Project را انتخاب نمائید.
از قسمت Visual C# آیتم Class Library را انتخاب کرده کلیک کرده و روی OK کلیک کنید.
حالا یک پروژه جدید به نام ClassLibrary1 به برنامه شما اضافه شده است. فعلا این پروژه خالی است و تنها کلاس Class1.cs در آن قرار دارد.
در حال حاضر برنامه شما دارای دو پروژه است بنامهای WindowsApplication1 و ClassLibrary1.
از پنجره Solution Explorer (اگر آنرا نمی بینید، کلید Ctrl+W و سپس S را فشار دهید)
فایلهای DAL.cs و BLL.cs را که قبلا ایجاد کرده بودیم و در پروژه WindowsApplication1 قرار دارند را انتخاب کرده و با کلیک راست روی آنها، گزینه Cut را برگزینید. سپس روی پروژه ClassLibrary1 که به همین نام در Solution Explorer قرار دارد روی ClassLibrary1 کلیک راست کرده و گزینه Paste را انتخاب کنید که با اینکار، فایلهای DAL.cs و BLL.cs به پروژه جدید منتقل شدند. حال باید فایلهای DAL.cs و BLL.cs را ویرایش کنید، کافیست در این فایلها، خط زیر را:


namespace WindowsApplication1

با خط زیر جایگزین کنید (تغییر دهید):


namespace ClassLibrary1


حال نقریبا تمام است، کافیست که پروژه ClassLibrary1 را به پروژه WindowsApplication1 ارجاع دهید. از پنجره Solution Explorer روی پروژه WindowsApplication1 (روی نام آن) کلیک راست کرده و گزینه Add Reference را انتخاب کنید، از پنجره ظاهر شده، روی زبانه Projects کلیک کرده و با انتخاب ClassLibrary1 از لیست موجود، روی OK کلیک کنید.

حال اگر بخواهید برنامه را با F5 تست کنید، نخواهید توانست و هنوز یک مشکل دیگر وجود دارد، همانطور که میدانید، در فایلهای DAL.cs و BLL.cs فضاهای نام System.Drawing و System.Windows.Forms وجود دارند، اما این فضاهای نام برای پروژه ClassLibrary1 هنوز قابل تشخیص نیستند، پس باید از پنجزه Solution Explorer روی نام پروژه ClassLibrary1 کلیک راست کرده و گزینه Add Reference را انتخاب کنیم، از پنجره ظاهر شده، روی زبانه .NET (دات نت) کلیک کنید و از لیست موجود، گزینه System.Drawing را به حالت انتخاب در آورید، سپس به قسمت پائینتر لیست رفته و با یافتن گزینه System.Windows.Forms، آنرا با نگهداشتن کلید Ctrl، با کلیک چپ مووس انتخاب کنید، این کار باعث میشود که از لیست موجود، فقط دو گزینه System.Drawing و System.Windows.Forms انتخاب شده باشد، حال روی OK کلیک کنید.
حال تنها یک تغییر دیگر مانده و آن این است که در فایل Form1.cs از پروژه WindowsApplication1 تغییر کوچکی ایجاد کنیم. یعنی در فایلی که کلاس Form1 تعریف شده است، باید قبل از آن، فضای نام ClassLibrary1 را برایش مشخص کنیم، یعنی خط زیر را به فایل Form1.cs اضافه میکنیم:


using ClassLibrary1;


حالا، میتوانید کلاسهای DAL و BLL را در یک فایل dll توسعه دهید و از این فایل dll در Form1 از پروژه WindowsApplication1 استفاده کنید.

اگر به پوشه WindowsApplication1\WindowsApplication1\bin\Debug بروید، یعنی همان خروجی برنامه شما، خواهید دید که فایل ClassLibrary1.dll در کنار فایل WindowsApplication1.exe قرار دارد. هر کدام در یک اسمبلی.

نمیدونم تا چه حد توضیحاتم گویا بود، جائیش رو نفهمیده بودید، با نقل قول آن قسمت از نوشته، من رو متوجه کنید تا بهتر توضیح بدم.

hdv212
چهارشنبه 24 مرداد 1386, 03:08 صبح
shotshat جان، Data Access Layer چیزی نیست که بخوای مثل DataType ازش استفاده کنی، Data Access Layer یه مفهومه که باید درکش کنی، پاسخ هایی که دوستان دادن همه به نوعی درسته و درست تر اونه که برنامه ت رو از طریق دیدگاه OOP طبقه بندی کنی و برای هر طبقه بخش های مختلف رو برای عملیات مختلف ایجاد کنی، نهایت اینکه تمام این مسائل باعث اشکال زدایی راحت، افزایش سرعت تولید نرم افزار و در کل مدیریت راحت تر بشه.

shotshat
چهارشنبه 24 مرداد 1386, 17:24 عصر
دستت واقعا درد نکنه، خیلی کمک کردی ولی با عرض پوزش چندتا مشکل دیگه مونده:

اصلا میدونی drz این وسط چه کاره بیده؟ چون من احتیاج داشتم بگم :(() if (dr.Read و این if چون مسئول چک کردن هاست باید در BLL تعریف میشد اما تعریف datareader ما(dr)در DAL بود، یک متغیر static که همه کلاسها بتونن بشناسنش به اسم drz تعریف کردم و dr.Read رو توش ریختم( ()drz=dr.Read)و در لایه BLL اینطور نوشتم:( if(drz.
حالا الان باید چه کارکنم؟ چه جوری یک متغیر تعریف کنم که همه کلاسها بشناسنش؟ یا چه جوری در لایه BLL بگم :(() if (dr.Read ؟

PC2st
چهارشنبه 24 مرداد 1386, 18:09 عصر
خواهش میکنم...
در اصل بهتره که متغیر drz رو در DAL قرار دهید و توسط کلاس BLL، مقدار این متغیر رو از کلاس DAL درخواست کنید. مثال میزنم، مثلا در کلاس DAL موارد زیر رو اضافه کنید:


private bool _drz;

public bool Drz
{
get
{
return this_.drz;
}

set
{
this._drz = value;
}
}

که در کد فوق، drz_ همان ;()drz = dr.Read است که فقط نامش رو عوض کردم.
و بعد در کلاس BLL بصورت زیر ازش استفاده کنید:


if(oDal.Drz)
//...

که oDal بصورت زیر تعریف شده است:


private DAL oDal = new DAL();

علیرضا مداح
چهارشنبه 24 مرداد 1386, 19:56 عصر
سلام .
یکی از نمونه های موجود برای پیاده سازی معماری سه لایه :
http://www.codeproject.com/cs/design/three_tier_architecture.asp

hdv212
چهارشنبه 24 مرداد 1386, 20:42 عصر
سایتت باز نشد عزیز.

ببین این کلاس رو من پیاده سازی کردم، ببین شاید به دردت بخوره :


using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;

namespace DataAccessClass
{
public static class DataAccess
{
private static SqlConnection connection = new SqlConnection();
public static SqlConnection Connection
{
get { return DataAccess.connection; }
set { DataAccess.connection = value; }
}

private static SqlCommand command = new SqlCommand();
public static SqlCommand Command
{
get { return DataAccess.command; }
set { DataAccess.command = value; }
}

private static SqlDataReader reader;
public static SqlDataReader Reader
{
get { return DataAccess.reader; }
set { DataAccess.reader = value; }
}

private static SqlDataAdapter adapter = new SqlDataAdapter();
public static SqlDataAdapter Adapter
{
get { return DataAccess.adapter; }
set { DataAccess.adapter = value; }
}
}
}

اینطوری هم ازش استفاده میکنم :


if (DataAccess.Connection.State == ConnectionState.Closed)
DataAccess.Connection.Open();

// Update table Request
DataAccess.Command.CommandText = "sp_Upd_requests";
DataAccess.Command.CommandType = System.Data.CommandType.StoredProcedure;
DataAccess.Command.Parameters.Clear();
System.Data.SqlClient.SqlParameter p1_0 = new System.Data.SqlClient.SqlParameter("@requestName", this.txt_name.Text);
DataAccess.Command.Parameters.Add(p1_0);

DataAccess.Command.ExecuteNonQuery();

if (DataAccess.Connection.State == ConnectionState.Open)
DataAccess.Connection.Close();