با این وجود تست کرده و نتیجه را مجددا خدمت شما اعلام خواهم کرد
متشکرم
کدهای این قسمت بهروزرسانی شده و از این ریپازیتوری قابل دسترسی است.
docker run --name eventstore-node -d -p 2113:2113 -p 1113:1113 eventstore/eventstore
Install-Package EventStore.Client
public class EventStoreDbContext : IEventStoreDbContext { public async Task<IEventStoreConnection> GetConnection() { IEventStoreConnection connection = EventStoreConnection.Create( new IPEndPoint(IPAddress.Loopback, 1113), nameof(MediatrTutorial)); await connection.ConnectAsync(); return connection; } public async Task AppendToStreamAsync(params EventData[] events) { const string appName = nameof(MediatrTutorial); IEventStoreConnection connection = await GetConnection(); await connection.AppendToStreamAsync(appName, ExpectedVersion.Any, events); } }
services.AddSingleton<IEventStoreDbContext, EventStoreDbContext>();
public class EventLoggerBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> { readonly IEventStoreDbContext _eventStoreDbContext; public EventLoggerBehavior(IEventStoreDbContext eventStoreDbContext) { _eventStoreDbContext = eventStoreDbContext; } public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) { TResponse response = await next(); string requestName = request.ToString(); // Commands convention if (requestName.EndsWith("Command")) { Type requestType = request.GetType(); string commandName = requestType.Name; var data = new Dictionary<string, object> { { "request", request }, { "response", response } }; string jsonData = JsonConvert.SerializeObject(data); byte[] dataBytes = Encoding.UTF8.GetBytes(jsonData); EventData eventData = new EventData(eventId: Guid.NewGuid(), type: commandName, isJson: true, data: dataBytes, metadata: null); await _eventStoreDbContext.AppendToStreamAsync(eventData); } return response; } }
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(EventLoggerBehavior<,>));
var result = _onlinePayment.Request(Gateways.Mellat, 123, 25000, "http://www.mywebsite.com/foo/bar/");
var result = _onlinePayment.Request(invoice => { invoice .SetTrackingNumber(123) .SetAmount(25000) .SetCallbackUrl("http://www.mywebsite.com/foo/bar/") .UseGateway(Gateways.Mellat); });
var result = _onlinePayment.Request(invoice => { invoice .UseAutoIncrementTrackingNumber() .SetAmount(25000) .SetCallbackUrl("http://www.mywebsite.com/foo/bar/") .UseGateway(Gateways.Mellat); });
var result = _onlinePayment.Request(invoice => { invoice .UseAutoRandomTrackingNumber() .SetAmount(25000) .SetCallbackUrl("http://www.mywebsite.com/foo/bar/") .UseGateway(Gateways.Mellat); });
public class MyTrackingNumberProvider : ITrackingNumberProvider { public Task<long> ProvideAsync(CancellationToken cancellationToken = new CancellationToken()) { // تولید و برگشت کد در اینجا } }
var result = _onlinePayment.Request(invoice => { invoice .UseTrackingNumberProvider<MyTrackingNumberProvider>() // یا .UseTrackingNumberProvider(new MyTrackingNumberProvider()) // یا .UseTrackingNumberProvider(services => new MyTrackingNumberProvider()) });
services.AddParbad() .ConfigureGateways(gateways => { gateways .AddParbadVirtual() .WithOptions(options => options.GatewayPath = "/MyVirtualGateway"); });
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); // ثبت درگاه مجازی app.UseParbadVirtualGateway(); }
public void Configuration(IAppBuilder app) { var parbad = ParbadBuilder.CreateDefaultBuilder() .ConfigureGateways(gateways => { gateways .AddParbadVirtual() .WithOptions(options => options.GatewayPath = "/MyVirtualGateway"); }) .Build(); app.UseParbadVirtualGateway(parbad.Services); }
var result = _onlinePayment.Request(Gateways.ParbadVirtualGateway, 123, 25000, "http://www.mywebsite.com/foo/bar/");
services.AddParbad() .ConfigureGateways(gateways => { gateways .AddMellat() .WithOptions(options => { options.TerminalId = 123; options.UserName = "abc"; options.UserPassword = "xyz; ) .WithProxy(new Uri("Proxy Server URL"), "UserName", "Password"); });
public class MyTokenProvider : IPaymentTokenProvider { public Task<string> ProvideTokenAsync(Invoice invoice, CancellationToken cancellationToken = new CancellationToken()) { // تولید و برگرداندن توکن در اینجا } public Task<string> RetrieveTokenAsync(CancellationToken cancellationToken = new CancellationToken()) { // خواندن و برگرداندن توکن در اینجا } }
services.AddParbad() .ConfigurePaymentToken(builder => builder.AddPaymentTokenProvider<MyTokenProvider>(ServiceLifetime.Transient));
شاید سادهترین تعریف برای Saltarelle این باشد که «کامپایلریست که کدهای C# را به جاوا اسکریپت تبدیل میکند». محاسن زیادی را میتوان برای اینگونه کامپایلرها نام برد؛ مخصوصا در پروژههای سازمانی که نگهداری از کدهای جاوا اسکریپت بسیار سخت و گاهی خارج از توان است و این شاید مهمترین عامل ظهور ابزارهای جدید از قبیل Typescript باشد.
در هر صورت اگر حوصله و وقت کافی برای تجهیز تیم نرم افزاری، به دانش یک زبان جدید مانند Typescript نباشد، استفاده از توان و دانش تیم تولید، از زبان C# سادهترین راه حل است و اگر ابزاری مطمئن برای استفاده از حداکثر قدرت JavaScript همراه با امکانات نگهداری و توسعه کدها وجود داشته باشد، بی شک Saltarelle یکی از بهترینهای آنهاست.
قبلا کامپایلر هایی از این دست مانند Script# وجود داشتند، اما فاقد همه امکانات C# بوده وعملا قدرت کامل C# در کد نویسی وجود نداشت. اما با توجه به ادعای توسعه دهندگان این کامپایلر سورس باز در استفادهی حداکثری از کلیه ویژگیهای C# 5 و با وجود Library های متعدد میتوان Saltarelle را عملا یک کامپایلر موفق در این زمینه دانست.
برای استفاده از Saltarelle در یک برنامه وب ساده باید یک پروژه Console Application به Solution اضافه کرد و پکیج Saltarelle.Compiler را از nuget نصب نمایید. بعد از نصب این پکیج، کلیه Reference ها از پروژه حدف میشوند و هر بار Build توسط کامپایلر Saltarelle انجام میشود. البته با اولین Build، مقداری Error را خواهید دید که برای از بین بردنشان نیاز است پکیج Saltarelle.Runtime را نیز در این پروژه نصب نمایید:
PM> Install-Package Saltarelle.Compiler PM> Install-Package Saltarelle.Runtime
در صورتیکه کماکان Build نهایی با Error همرا بود، یکبار این پروژه را Unload و سپس مجددا Load نمایید
UI یک پروژه وب MVC است و Client یک Console Application که پکیجهای مورد نیاز Saltarelle روی آن نصب شده است.
در صورتیکه پروژه را Build نماییم و نگاهی به پوشهی Debug بیاندازیم، یک فایل JavaScript همنام پروژه وجود دارد:
برای اینکه بعد از هر بار Build ، فایل اسکریپت به پوشهی مربوطه در پروژه UI منتقل شود کافیست کد زیر را در Post Build پروژه Client بنویسیم:
copy "$(TargetDir)$(TargetName).js" "$(SolutionDir)SalratelleSample.UI\Scripts"
اکنون پس از هر بار Build ، فایل اسکریپت مورد نظر در پوشهی Scripts پروژه UI آپدیت میشود:
در ادامه کافیست فایل اسکریپت را به layout اضافه کنیم.
<script src="~/Scripts/SaltarelleSample.Client.js"></script>
در پوشهی Saltarelle.Runtime در پکیجهای نصب شده، یک فایل اسکریپت به نام mscorlib.min.js نیز وجود دارد که حاوی اسکریپتهای مورد نیاز Saltarelle در هنگام اجراست. آن را به پوشه اسکریپتهای پروژه UI کپی نمایید و سپس به Layout اضافه کنید.
<script src="~/Scripts/mscorlib.min.js"></script> <script src="~/Scripts/SaltarelleSample.Client.js"></script>
حال نوبت به اضافه نمودن libraryهای مورد نیازمان است. برای دسترسی به آبجکت هایی از قبیل document, window, element و غیره در جاوااسکریپت میتوان پکیج Saltarelle.Web را در پروژهی Client نصب نمود و برای دسترسی به اشیاء و فرمانهای jQuery، پکیج Salratelle.jQuery را نصب نمایید.
> Install-Package Saltarelle.Web > Install-Package Saltarelle.jQuery
به این libraryها imported library میگویند. در واقع، در زمان کامپایل، برای این libraryها فایل اسکریپتی تولید نمیشود و فقط آبجکتهای #C هستند که که هنگام کامپایل تبدیل به کدهای ساده اسکریپت میشوند که اگر اسکریپت مربوط به آنها به صفحه اضافه نشده باشد، اجرای اسکریپت با خطا مواجه میشود.
به طور سادهتر وقتی از jQuery library استفاده میکنید هیچ فایل اسکریپت اضافهای تولید نمیشود، اما باید اسکریپت jQuery به صفحه شما اضافه شده باشد.
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
مثال ما یک اپلیکیشن ساده برای خواندن فیدهای همین سایت است. ابتدا کدهای سمت سرور را در پروژه UI می نویسیم.
کلاسهای مورد نیاز ما برای این فید ریدر:
public class Feed { public string FeedId { get; set; } public string Title { get; set; } public string Address { get; set; } } public class Item { public string Title { get; set; } public string Link { get; set; } public string Description { get; set; } }
و یک کلاس برای مدیریت منطق برنامه
public class SiteManager { private static List<Feed> _feeds; public static List<Feed> Feeds { get { if (_feeds == null) _feeds = CreateSites(); return _feeds; } } private static List<Feed> CreateSites() { return new List<Feed>() { new Feed(){ FeedId = "1", Title = "آخرین تغییرات سایت", Address = "https://www.dntips.ir/rss.xml" }, new Feed(){ FeedId = "2", Title = "مطالب سایت", Address = "https://www.dntips.ir/feeds/posts" }, new Feed(){ FeedId = "3", Title = "نظرات سایت", Address = "https://www.dntips.ir/feeds/comments" }, new Feed(){ FeedId = "4", Title = "خلاصه اشتراک ها", Address = "https://www.dntips.ir/feed/news" }, }; } public static IEnumerable<Item> GetNews(string id) { XDocument feedXML = XDocument.Load(Feeds.Find(s=> s.FeedId == id).Address); var feeds = from feed in feedXML.Descendants("item") select new Item { Title = feed.Element("title").Value, Link = feed.Element("link").Value, Description = feed.Element("description").Value }; return feeds; } }
کلاس SiteManager فقط یک لیست از فیدها دارد و متدی که با گرفتن شناسهی فید ، یک لیست از آیتمهای موجود در آن فید ایجاد میکند.
حال دو ApiController برای دریافت دادهها ایجاد میکنیم
public class FeedController : ApiController { // GET api/<controller> public IEnumerable<Feed> Get() { return SiteManager.Feeds; } } public class ItemsController : ApiController { // GET api/<controller>/5 public IEnumerable<Item> Get(string id) { return SiteManager.GetNews(id); } }
در View پیشفرض که Index از کنترلر Home است، یک Html ساده برای
فرم صفحه اضافه میکنیم
<div> <div> <h2>Feeds</h2> <ul id="Feeds"> </ul> </div> <div> <h2>Items</h2> <p id="FeedItems"> </p> </div> </div>
در المنت Feeds لیست فیدها را قرار میدهیم و در FeedItems آیتمهای مربوط به هر فید. حال به سراغ کدهای سمت کلاینت میرویم و به جای جاوا اسکریپت از Saltarelle استفاده میکنیم.
کلاس Program را از پروژه Client باز میکنیم و متد Main را به شکل زیر تغییر میدهیم:
static void Main() { jQuery.OnDocumentReady(() => { FillFeeds(); }); }
بعد از کامپایل شدن، کد #C شارپ بالا به صورت زیر در میآید:
$SaltarelleSample_Client_$Program.$main = function() { $(function() { $SaltarelleSample_Client_$Program.$fillFeeds(); }); }; $SaltarelleSample_Client_$Program.$main();
و این همان متد معروف jQuery است که Saltarelle.jQuery برایمان ایجاد کرده است.
متد FillFeeds را به شکل زیر پیاده سازی میکنیم
private static void FillFeeds() { jQuery.Ajax(new jQueryAjaxOptions() { Url = "/api/feed", Type = "GET", Success = (d,t,r) => { // Fill var ul = jQuery.Select("#Feeds"); jQuery.Each((List<Feed>)d, (idx,i) => { var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer"); li.Click(eve => { FillData(i.FeedId); }); ul.Append(li); }); } }); }
آبجکت jQuery، متدی به نام Ajax دارد که یک شی از کلاس jQueryAjaxOptions را به عنوان پارامتر میپذیرد. این کلاس کلیه خصوصیات متد Ajax در jQuery را پیاده سازی میکند. نکته شیرین آن توانایی نوشتن lambda برای Delegate هاست.
خاصیت Success یک Delegate است که 3 پارامتر ورودی را میپذیرد.
public delegate void AjaxRequestCallback(object data, string textStatus, jQueryXmlHttpRequest request);
data همان مقداریست که api باز میگرداند که یک لیست از Feed هاست. برای زیبایی کار، من یک کلاس Feed در پروژه Client اضافه میکنم که خصوصیاتی مشترک با کلاس اصلی سمت سرور دارد و مقدار برگشی Ajax را به آن تبدیل میکنم.
کلاس Feed و Item
[PreserveMemberCase()] public class Feed { //[ScriptName("FeedId")] public string FeedId; //[ScriptName("Title")] public string Title; //[ScriptName("Address")] public string Address; } [PreserveMemberCase()] public class Item { // [ScriptName("Title")] public string Title; // [ScriptName("Link")] public string Link; // [ScriptName("Description")] public string Description; }
jQuery.Each((List<Feed>)d, (idx,i) => { var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer"); li.Click(eve => { FillData(i.FeedId); }); ul.Append(li); });
به ازای هر آیتمی که در شیء بازگشتی وجود دارد، با استفاد از متد each در jQuery یک li ایجاد میکنیم. همان طور که میبینید کلیه خواص، به شکل Fluent قابل اضافه شدن میباشد. سپس برای li یک رویداد کلیک که در صورت وقوع، متد FillData را با شناسه فید کلیک شده فراخوانی میکند و در آخر li را به المنت ul اضافه میکنیم.
برای هر کلیک هم مانند مثال بالا api را با شناسهی فید مربوطه فراخوانی کرده و به ازای هر آیتم، یک سطر ایجاد میکنیم.
private static void FillData(string p) { jQuery.Ajax(new jQueryAjaxOptions() { Url = "/api/items/" + p, Type = "GET", Success = (d, t, r) => { var content = jQuery.Select("#FeedItems"); content.Html(""); foreach (var item in (List<Item>)d) { var row = jQuery.Select("<div>").AddClass("row").CSS("direction", "rtl"); var link = jQuery.Select("<a>").Attribute("href", item.Link).Text(item.Title); row.Append(link); content.Append(row); } } }); }
در این مثال ما از Saltarelle.jQuery برای استفاده از jQuery.js استفاده نمودیم. libraryهای متعددی برای Saltarelle از قبیل linq,angular,knockout,jQueryUI,nodeJs ایجاد شده و همچنین قابلیتهای زیادی برای نوشتن imported libraryهای سفارشی نیز وجود دارد.
مطمئنا استفاده از چنین کامپایلرهایی راه حلی سریع برای رهایی از مشکلات متعدد کد نویسی با جاوا اسکریپت در نرم افزارهای بزرگ مقیاس است. اما مقایسه آنها با ابزارهایی از قبیل typescript احتیاج به زمان و تجربه کافی در این زمینه دارد.
این مورد، روش پیشنهادی در Angular Seed است و بدین صورت است که تعاریف ماژولها در فایل app.js انجام میگیرد. تعاریف و پیاده سازی تمام کنترلرها در فایل controller.js است. و همچنین دایرکتیوها و فیلترها و سرویسها هر کدام در فایلها جداگانه تعریف و پیاده سازی میشوند. این روش راه حلی سریع برای پروژههای کوچک با تعداد developerهای کم است. برای مثال زمانی که یک developer در حال ویرایش فایل controller.js است، از آن جا که فایل مورد نظر checkout خواهد شد در نتیجه سایر developerها امکان تغییر در فایل مورد نظر را نخواهند داشت. سورس فایلها به مرور زیاد خواهد شد و در نتیجه debug آن سخت میشود.
روش دوم
در این حالت تعاریف کنترلر ها، مدلها و سرویسها هرکدام در یک دایرکتوری مجزا قرار خواهد گرفت. برای هر view یک کنترلر و بنا بر نیاز مدل تعریف میکنیم. ساختار آن به صورت زیر میشود:
دایرکتیوها و فیلترها عموما در یک فایل قرار داده خواهند شد تا بنابر نیاز در جای مناسب رفرنس داده شوند. این روش ساختار مناسبتری نسبه به روش قبلی دارد اما دارای معایبی هم چون موارد زیر است:
همان طور که ملاحظه میکنید سرویس ها، کنترلرها و حتی مدلهای مربوط به هر بخش در یک مسیر جداگانه قرار میگیرند. علاوه بر آن فایل هایی که قابلیت اشتراکی دارند در مسیری به نام common وجود دارند تا بتوان در جای مناسب برای استفاده از آنها رفرنس داده شود. حتی اگر در پروژه خود فقط یک ماژول دارید باز سعی کنید از این روش برای مدیریت فایلهای خود استفاده نمایید. اگر با ngStart آشنایی داشته باشید به احتمال زیاد با این روش بیگانه نیستید.