با سلام، بهتر نیست از jay data یا از breeze.js استفاده کنید ؟
در ابتدا اجازه بدهید تعریف درستی از این دو واژه، ارائه کنیم.
DTO (Data Transfer Object)
به بیان خیلی ساده، DTOها برای انتقال اطلاعات استفاده میشوند؛ پس هیچ منطق و رفتاری در این اشیاء تعریف نمیشود .اگر در DTO منطقی پیاده سازی شود، دیگر به آن DTO گفته نمیشود. اجازه بدید منظورمان را از منطق یا رفتار مشخص کنیم. منطق یا رفتار، همان متدهایی هستند که در نوع داده خود تعریف میکنیم. در #C، یک DTO تنها از خصوصیتها (Properties) که از بلوکهای Get و Set تشکیل شدهاند، ساخته میشود. البته بدون کدهایی جهت اعتبار سنجی (Validation) مقادیر.
سؤال: وضعیت attribute ها و Metadataها چه میشود؟
خیلی غیر معمول نیست که از metadataها در DTO، بهمنظور اعتبار سنجی یا اهداف خاص، استفاده کنیم. بعضی از attributeها هیچ رفتاری را به DTOها اضافه نمیکنند؛ ولی استفاده از DTOها را در بخشهای دیگر سیستم، سادهتر میکنند. در نتیجه هیچکدام از attribute ها و metadataها، شرایط DTO بودن را نقض نمیکنند.
مدلهای دیگری مثل ViewModelsها و API Modelها چه میشوند؟
واژه DTO خیلی مبهم است. تنها چیزی که بیان میکند این است که شیء است و فقط و فقط شامل اطلاعات است و رفتاری ندارد. در این تعریف دربارهی کاربرد مورد نظر یک DTO چیزی گفته نشده. در بسیاری از معماریها، DTO نقش خاصی را ایفا میکند. بطور مثال در معماری MVC، از DTOها برای انقیاد داده (Binding) و ارسال اطلاعات به یک View استفاده میکنند. به همین خاطر این DTOها بعنوان ViewModel، در معماری MVC شناخته میشوند که رفتاری را در خود تعریف نمیکنند و تنها فرمت اطلاعات مورد انتظار یک View را مهیا میکنند.
پس در این سناریوی خاص، ViewModel نوعی DTO میباشد. اما باید دقت داشته باشید، همه ViewModelها را نمیتوان DTO محسوب کرد؛ مثلا در معماری MVVM، ویوو مدلهای تعریف شده، شامل رفتار هم میباشند. حتی در معماری MVC نیز گاهی اوقات منطقی به ViewModelها اضافه میشود که دیگر به آنها DTO نمیگوییم.
در صورت امکان، نام DTOها را بر اساس استفادهی آنها تعیین کنید. بطور مثال کلاسی با نام FoodDTO، مشخص نمیکند که این نوع، کجا و چگونه قرار است در معماری برنامه شما مورد استفاده قرار بگیرید؛ برعکس نامگذاری به صورت FoodViewModels کاربرد آن را صراحتا بیان میکند.
مثالی از DTO در زبان سی شارپ :
public class ProductViewModel { public int ProductId { get; set; } public string Name { get; set; } public string Description { get; set; } public string ImageUrl { get; set; } public decimal UnitPrice { get; set; } }
کپسوله سازی و DTO ها
کپسوله سازی، یکی از اصول برنامه نویسی شیءگرا میباشد. اما این کپسوله سازی به DTOها اعمال نمیشوند. به این علت که هدف کپسوله سازی، پنهان کردن فرآیند پشت صحنهی ذخیره سازی اطلاعات است؛ اما در DTO هیچ فرآیندی پیاده سازی نشده و نباید هیچ State پنهانی وجود داشته باشد. پس بحث Encapsulation در DTO منتفی است. پس کار را برای خودتان سخت نکنید؛ با تعریف private setter ها یا تبدیل کردن DTO به یک شیء غیرقابل تغییر (immutable). شما باید بهراحتی بتوانید عملیات ایجاد، نوشتن و خواندن DTOها را انجام دهید؛ همچنین باید بتوانید عملیات سریالایز کردن بر روی DTOها را بدون فرآیند سفارشی اضافهای، انجام دهید.
Field ها یا Property ها
سؤالی که مطرح میشود این است که وقتی کپسوله سازی در DTO مفهومی ندارد، چرا باید همیشه از property ها استفاده کنیم؟ چرا از فیلدها استفاده نکنیم (فیلدهای public )؟
ما میتوانیم هم از property استفاده کنیم و هم از fieldها؛ اما بعضی از فریم ورکها که کار Serialization را انجام میدهند، فقط با property ها کار میکنند. بنابراین بسته به نیاز خودتان، از fieldهای عمومی یا propertyها استفاده کنید. اما عموما از Property استفاده میکنند. البته در این پیوند، پرسش و پاسخ مفصلی در این رابطه وجود دارد.
غیرقابل تغییر بودن (Immutability) و نوع رکورد ( Record Type )
غیرقابل تغییر بودن، یکی از مزیتهای مهم در توسعه نرم افزار است. اما همانطور که در مثل بالا بیان شد، نیازی به غیرقابل تغییر کردن DTOها نیست. با ارائه رکورد در سی شارپ 9 شرایط کمی تغییر کرد. شاید عبارت مخفف دیگری که اضافه شده Data transfer Records یا (DTRs) است. یکی از روشهای تعریف DTR در سی شارپ 9، به شکل زیر است:
public record ProductDTO(int Id, string Name, string Description);
البته روش دیگری هم وجود دارد که شما propertyها را تعریف کنید و از طریق سازنده، مقدار دهی شوند. ویژگی جدید init-only این امکان را فراهم میکند که فقط در زمان مقدار دهی اولیه (initialization)، خصوصیات مقداردهی شوند و در ادامهی چرخه حیات شیء، property ها فقط خواندنی هستند. این ویژگی، recordها را غیر قابل تغییر میکند.
مثال:
public record ProductDTO { public int Id { get; init; } public string Name { get; init; } } var dto = new ProductDTO { Id = 1, Name = "some name" };
کلاسهای POCO یا همان Plain Old CLR/C# Object
شی Plain Old چیست؟ هر شیءای که Plain Old باشد، میتواند در هر جایی از برنامهی ما مورد استفاده قرار بگیرد؛ حتی در کلاسهای Test برنامه. این اشیاء هیچگونه وابستگی برای اجرا وظایف خود، به بانکهای اطلاعاتی و کتابخانههای ثالت ندارند.
برای درک بهتر این نوع کلاسها، به مثال زیر دقت کنید:
public class Product : DataObject<Product> { public Product(int id) { Id = id; InitializeFromDatabase(); } private void InitializeFromDatabase() { DataHelpers.LoadFromDatabase(this); } public int Id { get; private set; } // other properties and methods }
همانطور که مشاهده میکنید، این کلاس به متد استاتیکی برای کار با دیتابیس وابسته است؛ در نتیجه باعث میشود که کل کلاس، به وجود بانک اطلاعاتی وابسته شود. همچنین با ارث بری از کلاس پایهی دیگری، وابستگی به یک کتابخانهی ثالث ایجاد شدهاست. اجرای آزمون واحد برای چنین کلاسی، سبب fail شدن عملیات میشود. به این علت که ارتباط با بانک اطلاعاتی مورد نیاز متد DataHelpers، تامین نشدهاست. این شرایط، مثالی از الگوی Active Record Pattern میباشند. همچنین این کلاس دسترسی به منبع داده را در درون خود گنجانده است که این به معنای نقض اصل Persistence Ignorant (اصل Persistence Ignorance به طور خلاصه بیان میکند که در تحلیل و طراحی Business Logic به موضوع ذخیرهسازی (Persistence) فکر نکنید (تا جای ممکن) یا به عبارت دیگر، ذهن خود را درگیر پیچیدگیهای ذخیره سازی نکنید. برگرفته شده از breakpoint.blog.ir : روح الله دلپاک)می باشد. یکی از ویژگیهای POCO عدم نقض الگوی فوق است.
مثالی از POCO :
public class Product { public Product(int id) { Id = id; } private Product() { // required for EF } public int Id { get; private set; } // other properties and methods }
این کلاس یک POCO است:
- برای اجرای وظایف خود به فریم ورک ثالثی وابسته نیست.
- به کلاس پایهای ( Base class) نیاز ندارد.
- وابستگی به متد استاتیکی ندارد.
- می تواند در هر جایی از پروژه، نمونه سازی شود.
- اصل Persistence Ignorant را بیشتر رعایت کرده، نه بطور کامل؛ چون یک سازنده دارد که به کتابخانهی ثالثی نیازمند است (سازندهی بدون پارامتر که مورد نیاز EF میباشد).
POCO و DTO :
شاید این دو مفهموم گیج کننده باشند، ولی DTO همان POCO هست. اگر یک کلاس، DTO باشد، حتما POCO نیز هست. (مرور ویژگیهای دو مورد در بخشهای قبلی) ولی برعکس این وضعیت ممکن است صادق نباشد؛ مثال قبلی که در آن وابستگی به کتابخانهی ثالثی در سازندهی بدون پارامتر وجود داشت، DTO بودن را نقض میکرد. پس اگر هر دو حالت صادق بود، میتوان گفت این دو مفهوم یکی است.
با استفاده از Computed Properties ها میتوانید برای هر خاصیت، یک شرط بنویسید و آنرا مقداردهی کنید. در این بحث بسیار مهم نیز قادر خواهید بود تا به صورت بلادرنگ (Real Time) خروجی بگیرید. در واقع به محض ارسال اطلاعات، خروجی مورد نظر برای شما نمایش داده میشود.
ابتدا لازم است تا یک ویو را ایجاد کنید. یک new vue جدید ساخته و مشخصههای لازم را وارد کنید؛ مطابق کد زیر
<html> <body> <div id="dotnet"> <h2>New Sample: {{ a }}</h2> <input type="text" v-model="a"> </div> <script src="https://unpkg.com/vue@2.2.6"> </script> <script type="text/javascript"> new Vue({ el: '#dotnet', data:{ a:'' }, computed: { a:{ get: function () { return this.a + ''; } } } }); </script> </body> </html>
data:{ a:'' },
در کد زیر مقدار a درون data باید به v-model نسبت داده شود تا درون input اطلاعات نمایش داده شوند.
<input type="text" v-model="a">
computed: { a:{ get: function () { return this.a + ''; } } }
پس از اجرای کد درون مرورگر هر نوع مقداری که درون input وارد شود، توسط خاصیت v-model بلافاصله نمایش داده میشود.
مطالب
Static Reflection
قابلیت Dynamic reflection یا به اختصار همان reflection متداول، از اولین نگارشهای دات نت فریم در دسترس است و امکان دسترسی به اطلاعات مرتبط با کلاسها، متدها، خواص و غیره را در زمان اجرا مهیا میسازد. تابحال به کمک این قابلیت، امکان تهیهی ابزارهای پیشرفتهی زیر مهیا شده است:
انواع و اقسام
- فریم ورکهای آزمون واحد
- code generators
- ORMs
- ابزارهای آنالیز کد
و ...
برای مثال فرض کنید که میخواهید برای یک کلاس به صورت خودکار، متدهای آزمون واحد تهیه کنید (تهیه یک code generator ساده). اولین نیاز این برنامه، دسترسی به امضای متدها به همراه نام آرگومانها و نوع آنها است. برای حل این مساله باید برای مثال یک parser زبان سی شارپ یا اگر بخواهید کاملتر کار کنید، به ازای تمام زبانهای قابل استفاده در دات نت فریم ورک باید parser تهیه کنید که ... کار سادهای نیست. اما با وجود reflection به سادگی میتوان به این نوع اطلاعات دسترسی پیدا کرد و نکتهی مهم آن هم این است که مستقل است از نوع زبان مورد استفاده. به همین جهت است که این نوع ابزارها را در فریم ورکهایی که فاقد امکانات reflection هستند، کمتر میتوان یافت. برای مثال کیفیت کتابخانههای آزمون واحد CPP در مقایسه با آنچه که در دات نت مهیا هستند، اصلا قابل مقایسه نیستند. برای نمونه به یکی از معظمترین فریم ورکهای آزمون واحد CPP که توسط گوگل تهیه شده مراجعه کنید : (+)
قابلیت Reflection ، مطلب جدیدی نیست و برای مثال زبان جاوا هم سالها است که از آن پشتیبانی میکند. اما نگارش سوم دات نت فریم ورک با معرفی lambda expressions ، LINQ و Expressions در یک سطح بالاتر از این Dynamic reflection متداول قرار گرفت.
تعریف Static Reflection :
استفاده از امکانات Reflection API بدون بکارگیری رشتهها، به کمک قابلیت اجرای به تعویق افتادهی LINQ، جهت دسترسی به متادیتای المانهای کد، مانند خواص، متدها و غیره.
برای مثال کد زیر را در نظر بگیرید:
//dynamic reflection
PropertyInfo property = typeof (MyClass).GetProperty("Name");
MethodInfo method = typeof (MyClass).GetMethod("SomeMethod");
چقدر خوب میشد اگر این قابلیت بجای dynamic بودن (مشخص شدن در زمان اجرا)، استاتیک میبود و در زمان کامپایل قابل بررسی میشد. این امکان به کمک lambda expressions و expression trees دات نت سه بعد، میسر شده است. کلیدهای اصلی Static Reflection کلاسهای Func و Expression هستند. با استفاده از کلاس Func میتوان lambda expression ایی را تعریف کرد که مقداری را بر میگرداند و توسط کلاس Expression میتوان به محتوای یک delegate دسترسی یافت. ترکیب این دو، قدرت دستیابی به اطلاعاتی مانند PropertyInfo را در زمان طراحی کلاسها، میدهد؛ با توجه به اینکه:
- کاملا توسط intellisense موجود در VS.NET پشتیبانی میشود.
- با استفاده از ابزارهای refactoring قابل کنترل است.
- از همه مهمتر، دیگری خبری از رشتهها نبوده و همه چیز تحت کنترل کامپایلر قرار میگیرد.
و شاید هیچ قابلیتی به اندازهی Static Reflection در این چندسال اخیر بر روی اکوسیستم دات نت فریم ورک تاثیرگذار نبوده باشد. این روزها کمتر کتابخانه یا فریم ورکی را میتوانید پیدا کنید که از Static Reflection استفاده نکند. سرآغاز استفاده گسترده از آن به Fluent NHibernate بر میگردد؛ سپس در انواع و اقسام mocking frameworks ، ORMs و غیره استفاده شد و مدتی است که در ASP.NET MVC نیز مورد استفاده قرار میگیرد (برای مثال TextBoxFor معروف آن):
public string TextBoxFor<T>(Expression<Func<T,object>> expression);
<%= this.TextBoxFor(model => model.FirstName); %>
یک مثال ساده از تعریف و بکارگیری Static Reflection :
public PropertyInfo GetProperty<T>(Expression<Func<T, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
throw new InvalidOperationException("Not a member access.");
return memberExpression.Member as PropertyInfo;
}
برای نمونه Fluent NHibernate در پشت صحنه متد Map ، به کمک متدی شبیه به GetProperty فوق، a => a.Address1 را به رشته متناظر خاصیت Address1 تبدیل کرده و جهت تعریف نگاشتها مورد استفاده قرار میدهد:
public class AddressMap : DomainMap<Address>
{
public AddressMap()
{
Map(a => a.Address1);
}
}
جهت اطلاع؛ قابلیت استفاده از «کد به عنوان اطلاعات» هم مفهوم جدیدی نیست و برای مثال زبان Lisp چند دهه است که آنرا ارائه داده است!
برای مطالعه بیشتر:
در مورد معرفی مقدماتی MEF میتوانید به این مطلب مراجعه کنید و در مورد الگوی Singleton به اینجا.
کاربردهای الگوی Singleton عموما به شرح زیر هستند:
1) فراهم آوردن دسترسی ساده و عمومی به DAL (لایه دسترسی به دادهها)
2) دسترسی عمومی به امکانات ثبت وقایع سیستم در برنامه logging -
3) دسترسی عمومی به تنظیمات برنامه
و موارد مشابهی از این دست به صورتیکه تنها یک روش دسترسی به این اطلاعات وجود داشته باشد و تنها یک وهله از این شیء در حافظه قرار گیرد.
با استفاده از امکانات MEF دیگر نیازی به نوشتن کدهای ویژه تولید کلاسهای Singleton نمیباشد زیرا این چارچوب کاری دو نوع روش وهله سازی از اشیاء (PartCreationPolicy) را پشتیبانی میکند: Shared و NonShared . حالت Shared دقیقا همان نام دیگر الگوی Singleton است. البته لازم به ذکر است که حالت Shared ، حالت پیش فرض تولید وهلهها بوده و نیازی به ذکر صریح آن همانند ویژگی زیر نیست:
[PartCreationPolicy(CreationPolicy.Shared)]
مثال:
فرض کنید قرار است از کلاس زیر تنها یک وهله بین صفحات یک برنامهی Silverlight توزیع شود. با استفاده از ویژگی Export به MEF اعلام کردهایم که قرار است سرویسی را ارائه دهیم :
using System;
using System.ComponentModel.Composition;
namespace SlMefTest
{
[Export]
public class WebServiceData
{
public int Result { set; get; }
public WebServiceData()
{
var rnd = new Random();
Result = rnd.Next();
}
}
}
کدهای صفحه اصلی برنامه (که از یک دکمه و یک Stack panel جهت نمایش محتوای یوزر کنترل تشکیل شده) به شرح بعد هستند:
<UserControl x:Class="SlMefTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<StackPanel>
<Button Content="MainPageButton" Height="23"
HorizontalAlignment="Left"
Margin="10,10,0,0" Name="button1"
VerticalAlignment="Top" Width="98" Click="button1_Click" />
<StackPanel Name="panel1" Margin="5"/>
</StackPanel>
</UserControl>
using System.ComponentModel.Composition;
using System.Windows;
namespace SlMefTest
{
public partial class MainPage
{
[Import]
public WebServiceData Data { set; get; }
public MainPage()
{
InitializeComponent();
this.Loaded += mainPageLoaded;
}
void mainPageLoaded(object sender, RoutedEventArgs e)
{
CompositionInitializer.SatisfyImports(this);
panel1.Children.Add(new SilverlightControl1());
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Data.Result.ToString());
}
}
}
کدهای User control ساده اضافه شده به شرح زیر هستند:
<UserControl x:Class="SlMefTest.SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="UserControlButton"
Height="23"
HorizontalAlignment="Left"
Margin="10,10,0,0"
Name="button1"
VerticalAlignment="Top"
Width="125"
Click="button1_Click" />
</Grid>
</UserControl>
using System.ComponentModel.Composition;
using System.Windows;
namespace SlMefTest
{
public partial class SilverlightControl1
{
[Import]
public WebServiceData Data { set; get; }
public SilverlightControl1()
{
InitializeComponent();
this.Loaded += silverlightControl1Loaded;
}
void silverlightControl1Loaded(object sender, RoutedEventArgs e)
{
CompositionInitializer.SatisfyImports(this);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Data.Result.ToString());
}
}
}
با سلام و احترام؛ ضمن سپاس از نوشتن این مطلب، نکات تکمیلی زیر برای خوانندگان ارائه میگردد:
پس از ایجاد Linked Server به طریقی که در مطلب فوق بدان اشاره گردید، به دو صورت میتوان به اجرای دستورات در Server دیگر پرداخت. به شکل Distributed Query و یا استفاده از تابع ()OPENQUERY .
برای مثال دو SQL Instance به نامهای SQL1 و SQL2 موجود است که قصد داریم عملیاتی را از SQL2 با استفاده از امکان Distributed Query و OpenQuery در SQL1 انجام دهیم، برای مثال:
در استفاده از OpenQuery باید جانب احتیاط را رعایت نمود، چنانچه قصد اجرای دستورات ساده ای را در Remote Server دارید، توصیه میشود از Distributed Query استفاده شود و اگر دستورات پیچیده و محاسباتی هستند استفاده از OPENQUERY هزینه کمتری دارد. همچنین در نظر داشته باشید چنانچه در دستورات مربوط به جدولی ستونی با دیتا تایپ XML وجود داشت قادر به اجرای دستورات به هر دو شکل نخواهید بود، در واقع کوئریهای توزیع شده این مورد را پشتیبانی نمیکنند. برای مثال پس از اجرای دستورات زیر با پیغام خطا زیر مواجه میشوید: «Xml data type is not supported in distributed queries»
همچنین در نظر داشته باشید چنانچه در دستورات مربوط به جدولی ستونی با دیتا
تایپ CLR وجود داشت قادر به اجرای دستورات به شکل OpenQuery خواهید بود ولی در صورت استفاده از Distributed Query با خطایی مواجه میشوید با این پیغام «Objects exposing columns with CLR types are not allowed in distributed queries»
برای کسب اطلاعات بیشتر به لینک زیر مراجعه فرمائید:
پس از ایجاد Linked Server به طریقی که در مطلب فوق بدان اشاره گردید، به دو صورت میتوان به اجرای دستورات در Server دیگر پرداخت. به شکل Distributed Query و یا استفاده از تابع ()OPENQUERY .
برای مثال دو SQL Instance به نامهای SQL1 و SQL2 موجود است که قصد داریم عملیاتی را از SQL2 با استفاده از امکان Distributed Query و OpenQuery در SQL1 انجام دهیم، برای مثال:
:Distributed Query
SELECT * FROM [SQL2].[test].[dbo].[emp]
:OPENQUERY
SELECT * from OPENQUERY([SQL2], 'SELECT * FROM [test].[dbo].[emp]')
SELECT * FROM [نام Linked Server].[AdventureWorks].[Person].[Person] GO SELECT * FROM OPENQUERY([نام Linked Server ],'SELECT * FROM [AdventureWorks].[Person].[Person]')
SELECT * FROM [نام Linked Server] .[AdventureWorks].[Person].[Address] GO SELECT * FROM OPENQUERY([نام Linked Server] ,'SELECT * FROM [AdventureWorks].[Person].[Address]')
مطالب
لینکهای هفته اول دی
وبلاگها و سایتهای ایرانی
امنیت
ASP. Net
طراحی وب
PHP
- خبرهایی در مورد مایکروسافت و PHP
اسکیوال سرور
سی شارپ
عمومی دات نت
مسایل اجتماعی و انسانی برنامه نویسی
کتابهای رایگان جدید
متفرقه
- آهن بجای کروم! (یک برنامه نویس آلمانی قسمتهایی از مرورگر کروم را که در جهت جمع آوری اطلاعات برای گوگل بکار میرفته، حذف کرده و مرورگر دیگری به نام آهن را ارائه داده است!)