public class StructureMapValidatorFactory : ValidatorFactoryBase { public override IValidator CreateInstance(Type validatorType) { return ObjectFactory.TryGetInstance(validatorType) as IValidator; } }
ویژگی های پیشرفته ی AutoMapper - قسمت دوم
public class SourceType1 //poco class { public string Name public string JobTitle public string PetsName } public class SourceType2 { public string BankName public decimal CreditCardBalance public DateTime SignupDate } public class Destination { public string Job public string Balance //thousand seprator }
ASP.NET MVC #7
public class Products : List<Product>
inline type name(parameters) { ... }
public class Vector { public double X { get; set; } public double Y { get; set; } public double Z { get; set; } // ... }
public class MyClass { public int A { get; set; } public int C; }
static void Main() { MyClass target = new MyClass(); int a = target.A; Console.WriteLine("A = {0}", a); int c = target.C; Console.WriteLine("C = {0}", c); }
int a = target.A; 0000003e mov ecx,edi 00000040 cmp dword ptr [ecx],ecx 00000042 call dword ptr ds:[05FA29A8h] 00000048 mov esi,eax 0000004a mov dword ptr [esp+4],esi int c = target.C; 00000098 mov edi,dword ptr [edi+4] MyClass.get_A() looks like this: 00000000 push esi 00000001 mov esi,ecx 00000003 cmp dword ptr ds:[03B701DCh],0 0000000a je 00000011 0000000c call 76BA6BA7 00000011 mov eax,dword ptr [esi+0Ch] 00000014 pop esi 00000015 ret
int a = target.A; 00000024 mov ebx,dword ptr [edi+0Ch]
- متد هایی که حجم کد IL آنها بیشتر از 32 بایت است.
- متدهای بازگشتی.
- متدهایی که با اتریبیوت MethodImpl علامتگذاری شدند و MethodImplOptions.NoInlining اعمال شده بر آن
- متدهای virtual
- متدهایی که دارای کد مدیریت خطا هستند
- Methods that take a large value type as a parameter
- Methods with complicated flowgraphs
using System; using System.Diagnostics; using System.Runtime.CompilerServices; class Program { const int _max = 10000000; static void Main() { // ... Compile the methods. Method1(); Method2(); int sum = 0; var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { sum += Method1(); } s1.Stop(); var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { sum += Method2(); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.Read(); } static int Method1() { // ... No inlining suggestion. return "one".Length + "two".Length + "three".Length + "four".Length + "five".Length + "six".Length + "seven".Length + "eight".Length + "nine".Length + "ten".Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static int Method2() { // ... Aggressive inlining. return "one".Length + "two".Length + "three".Length + "four".Length + "five".Length + "six".Length + "seven".Length + "eight".Length + "nine".Length + "ten".Length; } } Output 7.34 ns No options 0.32 ns MethodImplOptions.AggressiveInlining
مطالعه بیشتر:
CAPTCHAfa
reCAPTCHA ایدهای است که با نمایش کلمات واقعی و اسکن شده از کتابهای قدیمی، بخشی از مشکلات را حل کرده و از کاربران اینترنت برای شناسایی کلماتی که رایانه توانایی خواندن آنها را ندارد استفاده میکند. (شکل زیر)
با وارد کردن درست هر کلمه، بخشی از یک کتاب، روزنامه، و یا مجلهی قدیمی در رایانه شناسایی و به فرمت دیجیتال ذخیره میشود. به این شکل شما در دیجیتالی کردن متون کاغذی سهیم هستید.
پروژهی reCAPTCHA توسط گوگل حمایت میشود و در این آدرس قرار دارد.
به تازگی تیمی از دانشکده فنی دانشگاه تهران به همراه انستیتو تکنولوژی ایلینویز شیکاگو، پروژه ای بر همین اساس اما برای متون فارسی با عنوان CAPTCHAfa تولید کرده اند (شکل زیر) که در این آدرس در دسترس است. امیدوارم این پروژه به گونه ای تغییر کنه که برای دیجیتالی کردن متنهای پارسی استفاده بشه. در حال حاضر، این پروژه از کلماتی از پیش تعریف شده استفاده میکنه.
متاسفانه این پروژه در حال حاضر فقط توسط برنامههای PHP قابل استفاده است. از این رو بر آن شدم تا اون رو برای برنامههای ASP.NET (هم Web Forms و هم MVC) آماده کنم. برای استفاده از CAPTCHAfa نیاز به یک کلید خصوصی و یک کلید عمومی دارید که از این آدرس قابل دریافت است.
کدهای پروژهی Class Library به شرح زیر است.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Captchafa demo for ASP.NET applications // by: Behrouz Rad // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= namespace Captcha { using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Web; public class Captchafa { private static readonly string PRIVATE_KEY = "your private key"; private static readonly string PUBLIC_KEY = "your public key"; private static readonly string CAPTCHAFA_API_SERVER = "http://www.captchafa.com/api"; private static readonly string CAPTCHAFA_VERIFY_SERVER = "http://www.captchafa.com/api/verify/"; private IDictionary<string, string> CaptchafaData { get { HttpContext httpContext = HttpContext.Current; string remoteIp = httpContext.Request.ServerVariables["REMOTE_ADDR"]; string challenge = httpContext.Request.Form["captchafa_challenge_field"]; string response = httpContext.Request.Form["captchafa_response_field"]; IDictionary<string, string> data = new Dictionary<string, string>() { {"privatekey" , PRIVATE_KEY }, {"remoteip" , remoteIp }, {"challenge" , challenge }, {"response" , response } }; return data; } } public static string CaptchafaGetHtml() { return string.Format("<script type=\"text/javascript\" src=\"{0}/?challenge&k={1}\"></script>", CAPTCHAFA_API_SERVER, PUBLIC_KEY); } public bool IsAnswerCorrect() { string dataToSend = this.CaptchafaPrepareDataToSend(this.CaptchafaData); string result = this.CaptchafaPostResponse(dataToSend); return result.StartsWith("true"); } private string CaptchafaPrepareDataToSend(IDictionary<string, string> data) { string result = string.Empty; StringBuilder sb = new StringBuilder(); foreach (var item in data) { sb.AppendFormat("{0}={1}&", item.Key, HttpUtility.UrlEncode(item.Value.Replace(@"\", string.Empty))); } result = sb.ToString(); sb = null; result = result.Substring(0, result.LastIndexOf("&")); return result; } private string CaptchafaPostResponse(string data) { StreamReader reader = null; Stream dataStream = null; WebResponse response = null; string responseFromServer = string.Empty; try { WebRequest request = WebRequest.Create(CAPTCHAFA_VERIFY_SERVER); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; byte[] byteData = Encoding.UTF8.GetBytes(data); request.ContentLength = byteData.Length; dataStream = request.GetRequestStream(); dataStream.Write(byteData, 0, byteData.Length); dataStream.Close(); response = request.GetResponse(); dataStream = response.GetResponseStream(); reader = new StreamReader(dataStream); responseFromServer = reader.ReadToEnd(); } finally { if (reader != null) { reader.Close(); } if (dataStream != null) { dataStream.Close(); } if (response != null) { response.Close(); } } return responseFromServer; } } }
ابتدا ارجاعی به فایل Captchafa.dll ایجاد کنید و سپس در روال Page_Load، کد زیر را قرار دهید. این کار برای تزریق اسکریپ CAPTCHAfa به صفحه استفاده میشود.
if (!IsPostBack) { litCaptcha.Text = Captchafa.CaptchafaGetHtml(); }
litCaptcha، یک کنترل Literal است که اسکریپت تولید شده، به عنوان متن آن معرفی میشود.
بررسی صحت مقدار وارد شده توسط کاربر (مثلاً در روال Click یک دکمه) به صورت زیر است.
Captchafa captchaFa = new Captchafa(); bool isAnswerCorrect = captchaFa.IsAnswerCorrect(); if (isAnswerCorrect) { // پاسخ صحیح است } else { // پاسخ صحیح نیست }
استفاده در پروژههای ASP.NET MVC
ابتدا ارجاعی به فایل Captchafa.dll ایجاد کنید. در ASP.NET MVC بهتره تا فرایند کار رو در یک HTML helper کپسوله کنیم.
public static class CaptchaHelper { public static MvcHtmlString Captchafa(this HtmlHelper htmlHelper) { return MvcHtmlString.Create(Captcha.Captchafa.CaptchafaGetHtml()); } }
بررسی صحت مقدار وارد شده توسط کاربر (پس از ارسال فرم به Server) به صورت زیر است.
[HttpPost] [ActionName("Index")] public ViewResult CaptchaCheck() { Captchafa captchaFa = new Captchafa(); bool isAnswerCorrect = captchaFa.IsAnswerCorrect(); if (isAnswerCorrect) { ViewBag.IsAnswerCorrect = true; } else { ViewBag.IsAnswerCorrect = false; } return View(); }
@using CaptchafaDemoMvc.Helpers; @{ ViewBag.Title = "Index"; } <form action="/" method="post"> @Html.Captchafa(); <input type="submit" id="btnCaptchafa" name="btnCaptchafa" value="آزمایش" /> @{ bool isAnswerExists = ViewBag.IsAnswerCorrect != null; } @if (isAnswerExists) { if ((bool)ViewBag.IsAnswerCorrect == true) { <span id="lblResult">پاسخ صحیح است</span> } else { <span id="lblResult">پاسخ صحیح نیست</span> } } </form>
دموی پروژه رو در این آدرس قرار دادم. پروژهی نمونه نیز از این آدرس قابل دریافت است.
پ.ن: به زودی برخی بهبودها رو بر روی این پروژه انجام میدم.
در قسمت قبل ایدهی اصلی و مفاهیم مرتبط با استفاده از Iterators مطرح شد. در این قسمت به یک مثال عملی در این مورد خواهیم پرداخت.
چندین کتابخانه و کلاس جهت مدیریت Coroutines در دات نت تهیه شده که لیست آنها به شرح زیر است:
1) Using C# 2.0 iterators to simplify writing asynchronous code
2) Wintellect's Jeffrey Richter's PowerThreading Library
3) Rob Eisenberg's Build your own MVVM Framework codes
و ...
مورد سوم که توسط خالق اصلی کتابخانهی Caliburn (یکی از فریم ورکهای مشهور MVVM برای WPF و Silverlight) در کنفرانس MIX 2010 ارائه شد، این روزها در وبلاگهای مرتبط بیشتر مورد توجه قرار گرفته و تقریبا به یک روش استاندارد تبدیل شده است. این روش از یک اینترفیس و یک کلاس به شرح زیر تشکیل میشود:
using System;
namespace SLAsyncTest.Helper
{
public interface IResult
{
void Execute();
event EventHandler Completed;
}
}
using System;
using System.Collections.Generic;
namespace SLAsyncTest.Helper
{
public class ResultEnumerator
{
private readonly IEnumerator<IResult> _enumerator;
public ResultEnumerator(IEnumerable<IResult> children)
{
_enumerator = children.GetEnumerator();
}
public void Enumerate()
{
childCompleted(null, EventArgs.Empty);
}
private void childCompleted(object sender, EventArgs args)
{
var previous = sender as IResult;
if (previous != null)
previous.Completed -= childCompleted;
if (!_enumerator.MoveNext())
return;
var next = _enumerator.Current;
next.Completed += childCompleted;
next.Execute();
}
}
}
توضیحات:
مطابق توضیحات قسمت قبل، برای مدیریت اعمال همزمان به شکلی پی در پی، نیاز است تا یک IEnumerable را به همراه yield return در پایان هر مرحله از کار ایجاد کنیم. در اینجا این IEnumerable را از نوع اینترفیس IResult تعریف خواهیم کرد. متد Execute آن شامل کدهای عملیات Async خواهند شد و پس از پایان کار رخداد Completed صدا زده میشود. به این صورت کلاس ResultEnumerator به سادگی میتواند یکی پس از دیگری اعمال Async مورد نظر ما را به صورت متوالی فراخوانی نمائید. با هر بار فراخوانی رخداد Completed، متد MoveNext صدا زده شده و یک مرحله به جلو خواهیم رفت.
برای مثال کدهای ساده WCF Service زیر را در نظر بگیرید.
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Threading;
namespace SLAsyncTest.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService
{
[OperationContract]
public int GetNumber(int number)
{
Thread.Sleep(2000);//Simulating a log running operation
return number * 2;
}
}
}
قصد داریم در طی دو مرحله متوالی این WCF Service را در یک برنامهی Silverlight فراخوانی کنیم. کدهای قسمت فراخوانی این سرویس بر اساس پیاده سازی اینترفیس IResult به صورت زیر درخواهند آمد:
using System;
using SLAsyncTest.Helper;
namespace SLAsyncTest.Model
{
public class GetNumber : IResult
{
public int Result { set; get; }
public bool HasError { set; get; }
private int _num;
public GetNumber(int num)
{
_num = num;
}
#region IResult Members
public void Execute()
{
var srv = new TestServiceReference.TestServiceClient();
srv.GetNumberCompleted += (sender, e) =>
{
if (e.Error == null)
Result = e.Result;
else
HasError = true;
Completed(this, EventArgs.Empty); //run the next IResult
};
srv.GetNumberAsync(_num);
}
public event EventHandler Completed;
#endregion
}
}
اکنون قسمتهای اصلی کدهای View Model برنامه به شکل زیر خواهند بود:
private void doFetch(object obj)
{
new ResultEnumerator(executeAsyncOps()).Enumerate();
}
private IEnumerable<IResult> executeAsyncOps()
{
FinalResult = 0;
IsBusy = true; //Show BusyIndicator
//Sequential Async Operations
var asyncOp1 = new GetNumber(10);
yield return asyncOp1;
//using the result of the previous step
if(asyncOp1.HasError)
{
IsBusy = false; //Hide BusyIndicator
yield break;
}
var asyncOp2 = new GetNumber(asyncOp1.Result);
yield return asyncOp2;
FinalResult = asyncOp2.Result; //Bind it to the UI
IsBusy = false; //Hide BusyIndicator
}
اگر از این روش استفاده نمیشد ممکن بود به این جواب برسیم یا خیر. ممکن بود مرحلهی دوم ابتدا شروع شود و سپس مرحلهی اول رخ دهد. اما با کمک Iterators و yield return به همراه کلاس ResultEnumerator موفق شدیم تا عملیات دوم همزمان را در حالت تعلیق قرار داده و پس از پایان اولین عملیات غیر همزمان، مرحلهی بعدی فراخوانی را بر اساس مقدار حاصل شده از WCF Service آغاز کنیم.
این روش برای برنامه نویسها آشناتر است و همان سیستم فراخوانی A->B->C را تداعی میکند اما کلیه اعمال غیرهمزمان هستند و ترد اصلی برنامه قفل نخواهد شد.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
بررسی سیستم جدید گرید بوت استرپ 3
<div class="container"> <div class="alert alert-info"> <h4> ایجاد فاصله بین ستونها </h4> </div> <div class="row"> <div class="col-lg-3 col-sm-4"> <div class="alert alert-danger" role="alert"> ستون اول </div> </div> <div class="col-lg-8 col-lg-offset-1 col-sm-7 col-sm-offset-1"> <div class="alert alert-success" role="alert"> ستون دوم </div> </div> </div> <!-- end row --> </div>
mkdir ps_cmdlet_with_csharp && cd "$_" dotnet new classlib dotnet add package PowerShellStandard.Library mv Class1.cs GetHelloCommand.cs
namespace ps_cmdlet_with_csharp; using System.Management.Automation; [Cmdlet(VerbsCommon.Get, "Hello")] public class GetHelloCommand : PSCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] public string Name { get; set; } protected override void BeginProcessing() { WriteObject("Start processing"); } protected override void ProcessRecord() { WriteObject("Hello " + Name); } protected override void EndProcessing() { WriteObject("End processing"); } }
using System.Collections.ObjectModel; using System.Management.Automation.Host; namespace System.Management.Automation { public abstract class PSCmdlet : Cmdlet { protected PSCmdlet(); public PSEventManager Events { get; } public PSHost Host { get; } public CommandInvocationIntrinsics InvokeCommand { get; } public ProviderIntrinsics InvokeProvider { get; } public JobManager JobManager { get; } public JobRepository JobRepository { get; } public InvocationInfo MyInvocation { get; } public PagingParameters PagingParameters { get; } public string ParameterSetName { get; } public SessionState SessionState { get; } public PathInfo CurrentProviderLocation(string providerId); public Collection<string> GetResolvedProviderPathFromPSPath(string path, out ProviderInfo provider); public string GetUnresolvedProviderPathFromPSPath(string path); public object GetVariableValue(string name); public object GetVariableValue(string name, object defaultValue); } }
PS /> dotnet build PS /> Import-Module ./bin/Debug/net7.0/ps_cmdlet_with_csharp.dll
PS /> Get-Command -Module ps_cmdlet_with_csharp CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Get-Hello 1.0.0.0 ps_cmdlet_with_csharp
namespace ps_cmdlet_with_csharp; using System.Management.Automation; [Cmdlet(VerbsCommon.Get, "Hello")] public class GetHelloCommand : PSCmdlet { // as before } [Cmdlet(VerbsCommon.Get, "Greetings")] public class GetGreetingsCommand : PSCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] public string Name { get; set; } protected override void BeginProcessing() { WriteObject("Start processing"); } protected override void ProcessRecord() { WriteObject("Greetings " + Name); } protected override void EndProcessing() { WriteObject("End processing"); } }
Import-Module: Invalid assembly public key.
<Project Sdk="Microsoft.NET.Sdk"> <!-- other tags --> <ItemGroup> <Compile Include="./GetHelloCommand.cs" /> </ItemGroup> </Project>
PS /> Get-Command -Module ps_cmdlet_with_csharp CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Get-Greetings 1.0.0.0 ps_cmdlet_with_csharp Cmdlet Get-Hello 1.0.0.0 ps_cmdlet_with_csharp
namespace ps_cmdlet_with_csharp; using System.Management.Automation; using System.Net.Http.Headers; [Cmdlet(VerbsCommon.Push, "SlackMessage")] [Alias("ssm")] [OutputType(typeof(string))] public class SlackMessageCommand : PSCmdlet { [Parameter(Mandatory = true)] [Alias("m")] public string Message { get; set; } [Parameter(Mandatory = true)] [Alias("t")] [ValidatePattern(@"xoxp-[0-9]{11}-[0-9]{12}-[0-9]{13}-[0-9a-zA-Z]{32}")] public string Token { get; set; } [Parameter(Mandatory = true)] [Alias("cid")] public string ChannelID { get; set; } protected async override void ProcessRecord() { base.ProcessRecord(); try { using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.Token); var values = new Dictionary<string, string> { { "channel", this.ChannelID }, { "text", this.Message } }; var content = new FormUrlEncodedContent(values); var response = await client.PostAsync("https://slack.com/api/chat.postMessage", content); var responseString = await response.Content.ReadAsStringAsync(); WriteObject("Message sent"); } catch (Exception ex) { WriteObject(ex.Message); } } }
PS /> $PROFILE | Get-Member -Type NoteProperty | Select-Object Name, Definitionbject Name, Definition Name Definition ---- ---------- AllUsersAllHosts string AllUsersAllHosts=/usr/local/microsoft/powershell/7/… AllUsersCurrentHost string AllUsersCurrentHost=/usr/local/microsoft/powershell… CurrentUserAllHosts string CurrentUserAllHosts=/Users/sirwanafifi/.config/powe… CurrentUserCurrentHost string CurrentUserCurrentHost=/Users/sirwanafifi/.config/p…
$SlackProjectPath = "/Users/sirwanafifi/Desktop/ps_cmdlet_with_csharp/bin/Debug/net7.0/ps_cmdlet_with_csharp.dll" Import-Module $SlackProjectPath
PS /> Get-Command -Module ps_cmdlet_with_csharp CommandType Name Version Source ----------- ---- ------- ---- Cmdlet Push-SlackMessage 1.0.0.0 ps_…
بروز رسانی موجودیتهای منفصل توسط WCF
سناریویی را در نظر بگیرید که در آن عملیات CRUD توسط WCF پیاده سازی شده اند و دسترسی دادهها با مدل Code-First انجام میشود. فرض کنید مدل اپلیکیشن مانند تصویر زیر است.
همانطور که میبینید مدل ما متشکل از پستها و نظرات کاربران است. برای ساده نگاه داشتن مثال جاری، اکثر فیلدها حذف شده اند. مثلا متن پست ها، نویسنده، تاریخ و زمان انتشار و غیره. میخواهیم تمام کد دسترسی دادهها را در یک سرویس WCF پیاده سازی کنیم تا کلاینتها بتوانند عملیات CRUD را توسط آن انجام دهند. برای ساختن این سرویس مراحل زیر را دنبال کنید.
- در ویژوال استودیو پروژه جدیدی از نوع Class Library بسازید و نام آن را به Recipe2 تغییر دهید.
- با استفاده از NuGet Package Manager کتابخانه Entity Framework 6 را به پروژه اضافه کنید.
- سه کلاس با نامهای Post, Comment و Recipe2Context به پروژه اضافه کنید. کلاسهای Post و Comment موجودیتهای مدل ما هستند که به جداول متناظرشان نگاشت میشوند. کلاس Recipe2Context آبجکت DbContext ما خواهد بود که بعنوان درگاه عملیاتی EF عمل میکند. دقت کنید که خاصیتهای لازم WCF یعنی DataContract و DataMember در کلاسهای موجودیتها بدرستی استفاده میشوند. لیست زیر کد این کلاسها را نشان میدهد.
[DataContract(IsReference = true)] public class Post { public Post() { comments = new HashSet<Comments>(); } [DataMember] public int PostId { get; set; } [DataMember] public string Title { get; set; } [DataMember] public virtual ICollection<Comment> Comments { get; set; } } [DataContract(IsReference=true)] public class Comment { [DataMember] public int CommentId { get; set; } [DataMember] public int PostId { get; set; } [DataMember] public string CommentText { get; set; } [DataMember] public virtual Post Post { get; set; } } public class EFRecipesEntities : DbContext { public EFRecipesEntities() : base("name=EFRecipesEntities") {} public DbSet<Post> posts; public DbSet<Comment> comments; }
- یک فایل App.config به پروژه اضافه کنید و رشته اتصال زیر را به آن اضافه نمایید.
<connectionStrings> <add name="Recipe2ConnectionString" connectionString="Data Source=.; Initial Catalog=EFRecipes; Integrated Security=True; MultipleActiveResultSets=True" providerName="System.Data.SqlClient" /> </connectionStrings>
- حال یک پروژه WCF به Solution جاری اضافه کنید. برای ساده نگاه داشتن مثال جاری، نام پیش فرض Service1 را بپذیرید. فایل IService1.cs را باز کنید و کد زیر را با محتوای آن جایگزین نمایید.
[ServiceContract] public interface IService1 { [OperationContract] void Cleanup(); [OperationContract] Post GetPostByTitle(string title); [OperationContract] Post SubmitPost(Post post); [OperationContract] Comment SubmitComment(Comment comment); [OperationContract] void DeleteComment(Comment comment); }
- فایل Service1.svc.cs را باز کنید و کد زیر را با محتوای آن جایگزین نمایید. بیاد داشته باشید که پروژه Recipe2 را ارجاع کنید و فضای نام آن را وارد نمایید. همچنین کتابخانه EF 6 را باید به پروژه اضافه کنید.
public class Service1 : IService { public void Cleanup() { using (var context = new EFRecipesEntities()) { context.Database.ExecuteSqlCommand("delete from [comments]"); context. Database.ExecuteSqlCommand ("delete from [posts]"); } } public Post GetPostByTitle(string title) { using (var context = new EFRecipesEntities()) { context.Configuration.ProxyCreationEnabled = false; var post = context.Posts.Include(p => p.Comments).Single(p => p.Title == title); return post; } } public Post SubmitPost(Post post) { context.Entry(post).State = // if Id equal to 0, must be insert; otherwise, it's an update post.PostId == 0 ? EntityState.Added : EntityState.Modified; context.SaveChanges(); return post; } public Comment SubmitComment(Comment comment) { using (var context = new EFRecipesEntities()) { context.Comments.Attach(comment); if (comment.CommentId == 0) { // this is an insert context.Entry(comment).State = EntityState.Added); } else { // set single property to modified, which sets state of entity to modified, but // only updates the single property – not the entire entity context.entry(comment).Property(x => x.CommentText).IsModified = true; } context.SaveChanges(); return comment; } } public void DeleteComment(Comment comment) { using (var context = new EFRecipesEntities()) { context.Entry(comment).State = EntityState.Deleted; context.SaveChanges(); } } }
- در آخر پروژه جدیدی از نوع Windows Console Application به Solution جاری اضافه کنید. از این اپلیکیشن بعنوان کلاینتی برای تست سرویس WCF استفاده خواهیم کرد. فایل program.cs را باز کنید و کد زیر را با محتوای آن جایگزین نمایید. روی نام پروژه کلیک راست کرده و گزینه Add Service Reference را انتخاب کنید، سپس ارجاعی به سرویس Service1 اضافه کنید. رفرنسی هم به کتابخانه کلاسها که در ابتدای مراحل ساختید باید اضافه کنید.
class Program { static void Main(string[] args) { using (var client = new ServiceReference2.Service1Client()) { // cleanup previous data client.Cleanup(); // insert a post var post = new Post { Title = "POCO Proxies" }; post = client.SubmitPost(post); // update the post post.Title = "Change Tracking Proxies"; client.SubmitPost(post); // add a comment var comment1 = new Comment { CommentText = "Virtual Properties are cool!", PostId = post.PostId }; var comment2 = new Comment { CommentText = "I use ICollection<T> all the time", PostId = post.PostId }; comment1 = client.SubmitComment(comment1); comment2 = client.SubmitComment(comment2); // update a comment comment1.CommentText = "How do I use ICollection<T>?"; client.SubmitComment(comment1); // delete comment 1 client.DeleteComment(comment1); // get posts with comments var p = client.GetPostByTitle("Change Tracking Proxies"); Console.WriteLine("Comments for post: {0}", p.Title); foreach (var comment in p.Comments) { Console.WriteLine("\tComment: {0}", comment.CommentText); } } } }
Comment: I use ICollection<T> all the time
شرح مثال جاری
ابتدا با اپلیکیشن کنسول شروع میکنیم، که کلاینت سرویس ما است. نخست در یک بلاک {} using وهله ای از کلاینت سرویس مان ایجاد میکنیم. درست همانطور که وهله ای از یک EF Context میسازیم. استفاده از بلوکهای using توصیه میشود چرا که متد Dispose بصورت خودکار فراخوانی خواهد شد، چه بصورت عادی چه هنگام بروز خطا. پس از آنکه وهله ای از کلاینت سرویس را در اختیار داشتیم، متد Cleanup را صدا میزنیم. با فراخوانی این متد تمام دادههای تست پیشین را حذف میکنیم. در چند خط بعدی، متد SubmitPost را روی سرویس فراخوانی میکنیم. در پیاده سازی فعلی شناسه پست را بررسی میکنیم. اگر مقدار شناسه صفر باشد، خاصیت State موجودیت را به Added تغییر میدهید تا رکورد جدیدی ثبت کنیم. در غیر اینصورت فرض بر این است که چنین موجودیتی وجود دارد و قصد ویرایش آن را داریم، بنابراین خاصیت State را به Modified تغییر میدهیم. از آنجا که مقدار متغیرهای int بصورت پیش فرض صفر است، با این روش میتوانیم وضعیت پستها را مشخص کنیم. یعنی تعیین کنیم رکورد جدیدی باید ثبت شود یا رکوردی موجود بروز رسانی گردد. رویکردی بهتر آن است که پارامتری اضافی به متد پاس دهیم، یا متدی مجزا برای ثبت رکوردهای جدید تعریف کنیم. مثلا رکوردی با نام InsertPost. در هر حال، بهترین روش بستگی به ساختار اپلیکیشن شما دارد.
اگر پست جدیدی ثبت شود، خاصیت PostId با مقدار مناسب جدید بروز رسانی میشود و وهله پست را باز میگردانیم. ایجاد و بروز رسانی نظرات کاربران مشابه ایجاد و بروز رسانی پستها است، اما با یک تفاوت اساسی: بعنوان یک قانون، هنگام بروز رسانی نظرات کاربران تنها فیلد متن نظر باید بروز رسانی شود. بنابراین با فیلدهای دیگری مانند تاریخ انتشار و غیره اصلا کاری نخواهیم داشت. بدین منظور تنها خاصیت CommentText را بعنوان Modified علامت گذاری میکنیم. این امر منجر میشود که Entity Framework عبارتی برای بروز رسانی تولید کند که تنها این فیلد را در بر میگیرد. توجه داشته باشید که این روش تنها در صورتی کار میکند که بخواهید یک فیلد واحد را بروز رسانی کنید. اگر میخواستیم فیلدهای بیشتری را در موجودیت Comment بروز رسانی کنیم، باید مکانیزمی برای ردیابی تغییرات در سمت کلاینت در نظر میگرفتیم. در مواقعی که خاصیتهای متعددی میتوانند تغییر کنند، معمولا بهتر است کل موجودیت بروز رسانی شود تا اینکه مکانیزمی پیچیده برای ردیابی تغییرات در سمت کلاینت پیاده گردد. بروز رسانی کل موجودیت بهینهتر خواهد بود.
برای حذف یک دیدگاه، متد Entry را روی آبجکت DbContext فراخوانی میکنیم و موجودیت مورد نظر را بعنوان آرگومان پاس میدهیم. این امر سبب میشود که موجودیت مورد نظر بعنوان Deleted علامت گذاری شود، که هنگام فراخوانی متد SaveChanges اسکریپت لازم برای حذف رکورد را تولید خواهد کرد.
در آخر متد GetPostByTitle یک پست را بر اساس عنوان پیدا کرده و تمام نظرات کاربران مربوط به آن را هم بارگذاری میکند. از آنجا که ما کلاسهای POCO را پیاده سازی کرده ایم، Entity Framework آبجکتی را بر میگرداند که Dynamic Proxy نامیده میشود. این آبجکت پست و نظرات مربوط به آن را در بر خواهد گرفت. متاسفانه WCF نمیتواند آبجکتهای پروکسی را مرتب سازی (serialize) کند. اما با غیرفعال کردن قابلیت ایجاد پروکسیها (ProxyCreationEnabled=false) ما به Entity Framework میگوییم که خود آبجکتهای اصلی را بازگرداند. اگر سعی کنید آبجکت پروکسی را سریال کنید با پیغام خطای زیر مواجه خواهید شد:
The underlying connection was closed: The connection was closed unexpectedly
می توانیم غیرفعال کردن تولید پروکسی را به متد سازنده کلاس سرویس منتقل کنیم تا روی تمام متدهای سرویس اعمال شود.
در این قسمت دیدیم چگونه میتوانیم از آبجکتهای POCO برای مدیریت عملیات CRUD توسط WCF استفاده کنیم. از آنجا که هیچ اطلاعاتی درباره وضعیت موجودیتها روی کلاینت ذخیره نمیشود، متدهایی مجزا برای عملیات CRUD ساختیم. در قسمتهای بعدی خواهیم دید چگونه میتوان تعداد متدهایی که سرویس مان باید پیاده سازی کند را کاهش داد و چگونه ارتباطات بین کلاینت و سرور را سادهتر کنیم.
بررسی سیستم جدید گرید بوت استرپ 3
<div class="container"> <h4 class="alert alert-info">ایجاد فاصله بین ستونها</h4> <div class="row"> <div class="col-lg-3 col-sm-4 alert alert-danger"> ستون اول </div> <div class="col-lg-8 col-lg-offset-1 col-sm-7 col-sm-offset-1 alert alert-success"> ستون دوم </div> </div> <!-- end row --> </div>
ممکنه راهنمائی بفرمائید