مطالب
پیاده سازی ServiceHostFactory سفارشی در WCF
در این مثال برای اینکه Instance Provider سفارشی خود را بتوانیم به عنوان یک Behavior به سرویس اضافه نماییم باید به خاصیت Description.Behaviors شی ServiceHost دسترسی داشته باشیم. زمانی که در پروژه‌های WCF از روش Self Hosting برای هاست سرویس‌ها استفاده کنیم به دلیل دسترسی مستقیم به شی ServiceHost هر گونه تنظیمات و عملیات Customization به راحتی امکان پذیر است؛ اما در IIS Hosting، از آن جا که به صورت پیش فرض از ServiceHostFactory موجود در WCF استفاده می‌شود ما دسترسی به شی ServiceHost نداریم. برای حل این مسئله باید یک CustomServiceHostFactory ایجاد نماییم که به راحتی در WCF این امکان تدارک دیده شده است.
بررسی یک مثال:
ابتدا کلاسی به صورت زیر ایجاد نمایید. در این کلاس می‌توانید کد‌های لازم برای سفارشی کردن شی ServiceHost را قرار دهید:
public class CustomServiceHost : ServiceHost
{
   public CustomServiceHost( Type t, params Uri baseAddresses ) :
      base( t, baseAddresses ) {}
      
   public override void OnOpening()
   {
       this.Description.Add( new MyServiceBehavior() );
   }
}
اگر از این به بعد به جای استفاده از ServiceHost مستقیما از CustomServiceHost استفاده نماییم، MyServiceBehavior به صورت خودکار به عنوان یک ServiceBehavior برای سرویس مورد نظر در نظر گرفته می‌شود. برای این که هنگام هاست سرویس مورد نظر به صورت خودکار از این شی کلاس استفاده شود می‌توان کلاس Factory ساخت سرویس را تغییر داد به صورت زیر:
public class CustomServiceHostFactory : ServiceHostFactory
{
   public override ServiceHost CreateServiceHost( Type t, Uri[] baseAddresses )
   {
      return new CustomServiceHost( t, baseAddresses )
   }
}
حال بر روی سرویس مورد نظر کلیک راست کرده و گزینه View MarkUp را انتخاب نمایید، چیزی شبیه به گزینه زیر را مشاهده خواهید کرد:
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService1.Service1" CodeBehind="Service1.svc.cs" %>
کافیست کلاس CustomServiceHostFactory را به عنوان Factory این سرویس مشخص نماییم. به صورت زیر:
<%@ ServiceHost Language="C#" Debug="true" Factory="CustomServiceHostFactory" Service="WcfService1.Service1" CodeBehind="Service1.svc.cs" %>

از این به بعد عملیات وهله سازی از سرویس بر اساس تنظیمات پیش فرض صورت گرفته در این کلاس‌ها انجام می‌گیرد.

نظرات مطالب
بررسی روش آپلود فایل‌ها از طریق یک برنامه‌ی Angular به یک برنامه‌ی ASP.NET Core
یک نکته‌ی تکمیلی: به روز رسانی مثال مطلب جاری جهت گزارش درصد پیشرفت آپلود فایل‌ها توسط HTTP Client جدید Angular

در اینجا می‌خواهیم بدون استفاده از هیچگونه کامپوننت ثالثی، صرفا بر اساس قابلیت‌های جدید ماژول HttpClient ارائه شده‌ی در Angular 4.3، درصد پیشرفت آپلود را نیز نمایش دهیم. تغییرات مورد نیاز برای این منظور به شرح زیر هستند:

1) تغییر سرویس برنامه جهت استفاده از HTTP Client و گزارش درصد پیشرفت

  postTicket(ticket: Ticket, filesList: FileList): Observable<HttpEvent<any>> {

    //… the same as before

    const headers = new HttpHeaders().set("Accept", "application/json");
    return this.http
      .post(`${this.baseUrl}/SaveTicket`, formData, {
        headers: headers,
        reportProgress: true,
        observe: "events"
      })
      .map(response => response || {})
      .catch((error: HttpErrorResponse) => {
        console.error("observable error: ", error);
        return Observable.throw(error.statusText);
      });
  }
در اینجا متد postTicket، به این صورت تغییر کرده‌است:
الف) خروجی متد آن یک Observable از نوع HttpEvent تعیین شده‌است. به این ترتیب مشترکین به آن قادر خواهند شد به رخ‌دادهای HTTP Client گوش فرا دهند:
 postTicket(ticket: Ticket, filesList: FileList): Observable<HttpEvent<any>> {

ب) به قسمت options متد post، گزینه‌ها‌ی "observe: "events و reportProgress: true اضافه شده‌اند:
{
  headers: headers,
  reportProgress: true,
  observe: "events"
}
به این ترتیب، رخ‌دادها به همراه گزارش درصد پیشرفت آپلود و دانلود، به مشترکین این متد ارسال خواهند شد.

2) تغییر متد submitForm کامپوننت جهت گزارش و اعمال تغییرات به قالب برنامه

  queueProgress: number;
  isUploading: boolean;
  uploadTimeRemaining: number;
  uploadTimeElapsed: number;
  uploadSpeed: number;

  submitForm(form: NgForm) {
    const fileInput: HTMLInputElement = this.screenshotInput.nativeElement;
    this.queueProgress = 0;
    this.isUploading = true;
    let startTime = Date.now();

    this.uploadService.postTicket(this.model, fileInput.files).subscribe(
      (event: HttpEvent<any>) => {
        switch (event.type) {
          case HttpEventType.Sent:
            startTime = Date.now();
            console.log("Request sent!");
            break;
          case HttpEventType.DownloadProgress:
          case HttpEventType.UploadProgress:
            if (event.total) {
              this.queueProgress = Math.round(event.loaded / event.total * 100);

              const timeElapsed = Date.now() - startTime;
              const uploadSpeed = event.loaded / (timeElapsed / 1000);
              this.uploadTimeRemaining = Math.ceil(
                (event.total - event.loaded) / uploadSpeed
              );
              this.uploadTimeElapsed = Math.ceil(timeElapsed / 1000);
              this.uploadSpeed = uploadSpeed / 1024 / 1024;
            }
            break;
          case HttpEventType.Response:
            this.queueProgress = 100;
            this.isUploading = false;
            console.log("Done! ResponseBody:", event.body);
            break;
        }
      },
      (error: HttpErrorResponse) => {
        this.isUploading = false;
        console.log(error);
      }
    );
  }
پس از اینکه خروجی متد سرویس ارسال فایل‌ها را از نوع <<Observable<HttpEvent<any تعیین کردیم، اکنون پس از subscribe به آن، اینبار event: HttpEvent را بجای data متداول دریافت می‌کنیم. سپس بر اساس event.type می‌توان در مورد رخ‌داد گزارش داده شده، تصمیم‌گیری کرد:
- HttpEventType.Sent شروع عملیات است. برای مثال از آن می‌توان جهت تعیین زمان شروع به آپلود استفاده کرد. سپس این زمان را با زمان جاری، در رخ‌داد آپلود به سرور مقایسه و گزارش سرعت آپلود، زمان‌های صرف شده و باقیمانده را محاسبه کرد.
- HttpEventType.DownloadProgress و HttpEventType.UploadProgress گزارش درصد آپلود را مشخص می‌کنند. در اینجا event.total حجم کلی است و event.loaded حجم ارسالی کلی به سمت سرور می‌باشد. به همین جهت از این اطلاعات می‌توان برای نمایش درصد کل آپلود استفاده کرد.
- HttpEventType.Response در پایان عملیات رخ‌خواهد داد. در اینجا event.body همان بدنه‌ی response دریافتی از سمت سرور است.

3) تغییر رابط کاربری برنامه (قالب کامپوننت) جهت گزارش درصد پیشرفت کلی آپلود
    <div *ngIf="queueProgress > 0">
      <table class="table">
        <thead>
          <tr>
            <th width="15%">Event</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><strong>Elapsed time</strong></td>
            <td nowrap>{{uploadTimeElapsed | number:'.1'}} second(s)</td>
          </tr>
          <tr>
            <td><strong>Remaining time</strong></td>
            <td nowrap>{{uploadTimeRemaining | number:'.1'}} second(s)</td>
          </tr>
          <tr>
            <td><strong>Upload speed</strong></td>
            <td nowrap>{{uploadSpeed | number:'.3'}} MB/s</td>
          </tr>
          <tr>
            <td><strong>Queue progress</strong></td>
            <td>
              <div class="progress-bar progress-bar-info progress-bar-striped" role="progressbar"
                aria-valuemin="0" aria-valuemax="100" [attr.aria-valuenow]="queueProgress"
                [ngStyle]="{ 'width': queueProgress + '%' }">
                {{queueProgress}}%
              </div>
            </td>
          </tr>
        </tbody>
      </table>
    </div>

    <button class="btn btn-primary" [disabled]="form.invalid || isUploading" type="submit">Submit</button>
در اینجا کار نمایش زمان صرف شده، زمان باقیمانده، سرعت آپلود و همچنین درصد میزان پیشرفت آپلود انجام شده‌اند. درصد محاسبه شده به ngStyle متصل شده‌است تا سبب حرکت progressbar بوت استرپ شود.
همچنین در طی مدت آپلود، خاصیت isUploading به true تنظیم می‌شود تا دکمه‌ی ارسال را غیرفعال کند. این خاصیت در صورت بروز خطایی و یا تکمیل شدن عملیات، false می‌شود.



یک نکته: اگر می‌خواهید درصد پیشرفت آپلود را در حالت آزمایش local بهتر مشاهده کنید، دربرگه‌ی network، سرعت را بر روی 3G تنظیم کنید:



کدهای کامل این تغییرات را از اینجا می‌توانید دریافت کنید.
مطالب
توسعه برنامه‌های Cross Platform با Xamarin Forms & Bit Framework - قسمت یازدهم
در این قسمت قصد بررسی کامپوننت‌های فوق العاده‌ی Syncfusion را داریم. احتمالا Syncfusion را با کتاب‌های Succinctly Series آن می شناسید. این شرکت برای Xamarin Forms نزدیک به 130‌ کامپوننت، شامل موارد کار با دیتا، اعم از فرم‌های Data Entry ،Data Grid و ListView را نوشته که در کنار کنترل‌های کار با فایل‌های Office-PDF و همچنین گزارشات و چارت‌ها و سایر کنترل‌های آن، نیاز هر برنامه‌ای را برآورده می‌کند. یکی از چند ده کتاب Xamarin Forms نیز توسط این شرکت نوشته شده‌است.
ما ضمن استفاده کامپوننت List View آن، هم کار با List View را یاد می‌گیریم و هم کار با Syncfusion را. در نظر داشته باشید که خود Xamarin Forms نیز List View دارد و در نسخه‌ی 4 آن که هم اکنون در مرحله‌ی Preview است، کنترل جدیدی به نام CollectionView نیز ارائه شده که امکانات خیلی خوبی دارد.
توجه: مطالب آموزش زیر، از این لینک آورده شده‌است.

برای شروع، ابتدا Nuget Package مربوطه را در پروژه‌ی XamApp نصب کنید. نیازی به نصب کردن آن در XamApp.Android و XamApp.iOS و XamApp.UWP نیست، ولی باز و بسته کردن ویژوال استودیو بعد از نصب و راست کلیک کردن بر روی Solution و زدن Restore nuget packages ایده‌ی خوبی است!

در پروژه‌ی iOS، در فایل AppDelegate.cs، بعد از Forms.Init، کد زیر را کپی کنید:

SfListViewRenderer.Init();

همین کد را در MainPage.xaml.cs در پروژه UWP، قبل از LoadApplication قرار دهید. نیازی به انجام کاری در Android نیست.

سپس Product Key این محصول را به دست آورده و در پروژه XamApp، فولدر Views در فایل SyncfusionLicense قرار دهید.

حال برای نمایش لیستی از محصولات، ابتدا کلاس Product را ایجاد می‌کنیم. چه در زمانیکه یک Rest api را در سمت سرور فراخوانی می‌کنیم و چه زمانیکه با دیتابیس بر روی گوشی یعنی Sqlite کار می‌کنیم، در نهایت لیستی از یک کلاس را داریم (در اینجا Product).

    public class Product : Bindable
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool IsActive { get; set; }
        public decimal Price { get; set; }
    }

در یک View Model جدید با نام ProductsViewModel، در OnNavigatedToAsync، دیتا را از سرور یا دیتابیس، بر روی گوشی دریافت می‌کنیم؛ اما در این مثال، برای راحتی بیشتر یک List را New می‌کنیم:

    public class ProductsViewModel : BitViewModelBase
    {
        public List<Product> Products { get; set; }

        public async override Task OnNavigatedToAsync(INavigationParameters parameters)
        {
            Products = new List<Product> // getting products from server or sqlite database
            {
                new Product { Id = 1, IsActive = true, Name = "Product1" , Price = 12.2m /* m => decimal */ },
                new Product { Id = 2, IsActive = false, Name = "Product2" , Price = 14 },
                new Product { Id = 3, IsActive = true, Name = "Product3" , Price = 11 },
            };
            await base.OnNavigatedToAsync(parameters);
        }
    }

حال نوبت به دادن یک Template می‌رسد. مثلا فرض کنید می‌خواهیم نام را درون یک Label نمایش دهیم و بر اساس فعال یا غیر فعال بودن Product، یک Checkbox را تغییر داده، تیک بزنیم یا نزنیم و در نهایت نمایش قیمت را در یک Label دیگر خواهیم داشت.

    <sfListView:SfListView ItemsSource="{Binding Products}">
        <sfListView:SfListView.ItemTemplate>
            <DataTemplate>
                <FlexLayout x:DataType="model:Product" Direction="Row">
                    <Label
                        FlexLayout.Basis="50%"
                        Text="{Binding Name}"
                        VerticalTextAlignment="Center" />
                    <bitControls:BitCheckbox InputTransparent="True" FlexLayout.Basis="25%" IsChecked="{Binding IsActive}" />
                    <Label
                        FlexLayout.Basis="25%"
                        Text="{Binding Price}"
                        VerticalTextAlignment="Center" />
                </FlexLayout>
            </DataTemplate>
        </sfListView:SfListView.ItemTemplate>
    </sfListView:SfListView>

همانطور که می‌بینید، در DataTemplate از Flex Layout استفاده شده است. Flex Layout در کنار Grid, Stack, Relative, Absolute و سایر Layout‌‌های Xamarin Forms در پروژه قابلیت استفاده دارد و مزیت‌های خاص خود را دارد.

این Data Template توسط List View، حداکثر سه بار ساخته می‌شود؛ چون View Model در لیست مثال خود، سه Product دارد. خود List View تکنیک‌های Virtualization و Cell Reuse را بدون نیاز به هیچ کد اضافه‌ای هندل می‌کند و Performance خوبی دارد. در View مربوطه یعنی ProductsView.xaml، هر Binding ای (مثل Binding Products) به View Model اشاره می‌کند، اما درون Data Template، هر Binding به Product ای اشاره می‌کند که آن ردیف List View، دارد نمایش‌اش می‌دهد. برای همین x:DataType را روی Flex Layout درون Data Template به Product وصل کرده‌ایم. در این صورت اگر بنویسیم Binding N_ame، به ما خطا داده می‌شود که کلاس Product هیچ Property با نام N_ame ندارد که خطای درستی است.

روی BitCheckbox مقدار InputTransparent را برابر با True داده‌ایم که باعث می‌شود کلیک روی Checkbox عملا در نظر گرفته نشود. این منطقی است، زیرا عوض کردن مقدار Checkbox در این مثال ما ذخیره نمی‌شود و کاربرد نمایشی دارد و فقط باعث گیج شدن کاربر می‌شود.

کنترل BitCheckbox از مجموعه کنترل‌های Bit است که اخیرا با BitDatePicker آن آشنا شده‌اید. برای آشنایی با نحوه افزودن این کنترل‌ها به یک پروژه، به مستندات Bit Framework مراجعه کنید. خود Syncfusion نیز Checkbox دارد.

حال فرض کنید که قرار است دکمه‌ای برای هر ردیف List View داشته باشیم که با زدن روی آن، اطلاعات Product به سرور ارسال شود و جزئیات بیشتری دریافت و در قالب یک Alert نمایش داده شود.  برای این کار، ابتدا به Data Template که Flex Layout است، یک دکمه اضافه می‌کنیم. سپس Command آن دکمه را به View Model بایند می‌کنیم. در آن Command البته احتیاج داریم بدانیم درخواست نمایش جزئیات بیشتر، برای کدام Product داده شده. این مهم با Command Parameter شدنی است.

برای پیاده سازی این مثال، در سمت View Model داریم:

 
public BitDelegateCommand<Product> ShowProductDetailsCommand { get; set; }
public IUserDialogs UserDialogs { get; set; } async Task ShowProductDetails(Product product) { string productDetail = $"Product: {product.Name}'s more info: ..."; // get more info from server. await UserDialogs.AlertAsync(productDetail, "Product Detail"); }

کامند ShowProductDetailCommand یک پارامتر را از جنس Product می‌گیرد و آن Product ای است که روی دکمه آن کلیک شده‌است. با Clone کردن آخرین نسخه XamApp و درخواست نمایش صفحه‌ی Products در App.xaml.cs به صورت زیر و اجرای برنامه، می‌توانید درک بهتری از عملکرد آن داشته باشید:

await NavigationService.NavigateAsync("/Nav/Products", animated: false);

سپس در View مربوطه داریم:

 
...
<Button Command="{Binding ShowProductDetailsCommand}" CommandParameter="{Binding .}" Text="Detail..." /> </FlexLayout> </DataTemplate>

CommandParameter اگر برابر با Binding Id می‌بود، به Command در سمت View Model، بجای کل Product، فقط Id آن ارسال می‌شد. ولی Show Product Detail Command منتظر یک Product کامل است، نه فقط Id آن. با نوشتن 

CommandParameter="{Binding .}"

کل Product با کلیک روی دکمه به Command ارسال می‌شود.

اکنون اگر پروژه را Build کنید، خطایی را از x:DataType خواهید گرفت که منطقی است. اگر Binding Name و Binding Price دو Property با نام‌های Name و Price را از کلاس Product جستجو می‌کنند، پس قاعدتا ShowProductDetailCommand نیز در همان کلاس مدل، یعنی Product جستجو می‌شود! ولی می‌دانیم که این Command در View Model ما یعنی ProductsViewModel است. برای حل این مشکل، به جای Binding از bit:ViewModelBinding استفاده می‌کنیم:

Command="{bit:ViewModelBinding ShowProductDetailsCommand}"

در این صورت، بجای جستجو کردن ShowProductDetailCommand در کلاس Product، این را در ProductsViewModel جستجو می‌کند که منجر به خروجی درست می‌شود.

این List View دارای امکاناتی چون Infinite loading، Pull to refresh و Grouping-Sorting-Filtering و ... است که می‌توانید از روی مستندات خوب Syncfusion، آنها را راه اندازی کنید و اگر به مشکلی برخوردید نیز اینجا بپرسید. همچنین نگاهی به لیست 129 کنترل دیگر بیاندازید و ببینید که در برنامه‌های خود از کدام یک از آنها می‌توانید استفاده کنید.

مطالب
ایجاد «خواص الحاقی» با استفاده از امکانات TypeDescriptor و یک TypeDescriptionProvider سفارشی

برای ایجاد «خواص الحاقی» قبلا در سایت مطلب ایجاد «خواص الحاقی» تهیه شده‌است. در این مطلب قصد داریم راه حل ارائه شده‌ی در مطلب مذکور را با یک TypeDescriptionProvider سفارشی ترکیب کرده تا به صورت یکدست، از طریق TypeDescriptor بتوان به آن خواص نیز دسترسی داشته باشیم. 

فرض کنید در یک سیستم Modular Monolith، نیاز جدیدی به دست شما رسیده است که به شرح زیر می‌باشد:

نیاز داریم در گریدی از صفحه‌ی X مربوط به «مؤلفه 1»، ستونی جدید را اضافه کنید و دیتای مربوط به این ستون، توسط «مؤلفه 2» مهیا خواهد شد.

شرایط زیر می‌تواند در سیستم حاکم باشد:
  • قبلا «مؤلفه 2» ارجاعی را به «مؤلفه 1» داده است؛ لذا امکان ارجاع معکوس را در این حالت، نداریم.
  • «مؤلفه 1» باید بتواند مستقل از «مؤلفه 2» نیز توزیع شده و کار کند؛ لذا این نیاز برای زمانی است که «مؤلفه 2» برای توزیع در Component Model ما وجود داشته باشد.
  • نمی‌خواهیم در آینده برای نیازهای مشابه در همان صفحه‌ی X، تغییر جدیدی را در «مؤلفه 1» داشته باشیم (اضافه کردن خصوصیت مورد نظر به مدل نمایشی یا اصطلاحا ویو-مدل متناظر با گرید در در زمان طراحی، جواب مساله نمی‌باشد)
  • می‌‌خواهیم به یک طراحی با Loose Coupling (اتصال سست و ضعیف، وابستگی ضعیف) دست پیدا کنیم.

راه حل چیست؟
با توجه به شرایط حاکم، بدون شک برای مهیا کردن دیتای ستون مذکور نمی‌توان به «مؤلفه 2» مستقیما ارجاع داده و «مؤلفه 1» را به «مؤلفه 2» وابسته کنیم. از طرفی چه بسا در نیاز‌های آتی نیز لازم باشد ستون جدید دیگری برای نمایش دیتای خاصی در گرید مذکور، اضافه شود. راه حل پیشنهادی، معکوس سازی این وابستگی می‌باشد. به عنوان مثال با استفاده از Expose کردن یک Interface توسط «مؤلفه 1» و پیاده سازی آن توسط سایر مؤلفه‌ها و استفاده از این پیاده سازی‌ها در زمان اجرا، می‌تواند راه حلی برای این معکوس سازی باشد. 

نمودار UML بالا، نشان دهنده‌ی راه حل پیشنهادی میباشد.

در این حالت «مؤلفه 1» بدون آگاهی از سایر مؤلفه‌ها، همه‌ی پیاده سازی‌های IExtraColumnConenvtion را در زمان اجرا یافته و از آنها برای ایجاد ستون‌های جدید، استفاده خواهد کرد.

واسط مذکور به شکل زیر می‌باشد: 

public interface IConvention
{
}

public interface IExtraColumnConvention<T> : IConvention
{
   string Name { get; }
 
   string Title { get; }
 
   void Populate(IEnumerable<T> list);
}

البته این واسط می‌تواند جزئیات بیشتری را هم شامل شود.


گام اول: طراحی TypeDescriptionProvider


در ‎.NET به دو طریق میتوان به متادیتا‌ی یک Type دسترسی داشت:

  • استفاده از API Reflection موجود در فضای نام System.Reflection 
  • کلاس TypeDescriptor 

به طور کلی هدف از این کلاس در دات نت، ارائه اطلاعاتی در خصوص یک وهله از جمله: Attributeها، Propertyها، Event‌های آن و غیره، می‌باشد. هنگام استفاده از Reflection، اطلاعات بدست آمده از Type، به دلیل اینکه بعد از کامپایل نمی‌توانند تغییر کنند، لذا قابلیت توسعه پذیری را هم ندارند. در مقابل، با استفاده از کلاس TypeDescriptor این توسعه پذیری را برای وهله‌های مختلف می‌توانید داشته باشید.

برای مهیا کردن متادیتای سفارشی (در اینجا اطلاعات مرتبط با خصوصیات الحاقی) برای TypeDescriptor، نیاز است یک TypeDescriptionProvider سفارشی را طراحی کنیم. 

/// <summary>
/// Use this provider when you need access ExtraProperties with TypeDescriptor.GetProperties(instance)
/// </summary>
public class ExtraPropertyTypeDescriptionProvider<T> : TypeDescriptionProvider where T : class
{
    private static readonly TypeDescriptionProvider Default =
        TypeDescriptor.GetProvider(typeof(T));

    public ExtraPropertyTypeDescriptionProvider() : base(Default)
    {
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type instanceType, object instance)
    {
        var descriptor = base.GetTypeDescriptor(instanceType, instance);
        return instance == null ? descriptor : new ExtraPropertyCustomTypeDescriptor(descriptor, instance);
    }

    private sealed class ExtraPropertyCustomTypeDescriptor : CustomTypeDescriptor
    {
      //...
    }
}

  در تکه کد بالا، ابتدا تامین کننده‌ی پیش‌فرض مرتبط با نوع جنریک مورد نظر را یافته و به عنوان تامین کننده‌ی پایه معرفی کرده‌ایم. سپس برای معرفی CustomTypeDescritpr باید متد GetTypeDescriptor را بازنویسی کنیم. در اینجا لازم است برای معرفی متادیتا مرتبط با یک نوع، یک پیاده سازی از واسط ICustomTypeDescriptor را ارائه کنیم:
private sealed class ExtraPropertyCustomTypeDescriptor : CustomTypeDescriptor
{
    private readonly IEnumerable<ExtraPropertyDescriptor<T>> _instanceExtraProperties;

    public ExtraPropertyCustomTypeDescriptor(ICustomTypeDescriptor defaultDescriptor, object instance)
        : base(defaultDescriptor)
    {
        _instanceExtraProperties = instance.ExtraPropertyList<T>();
    }

    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = new PropertyDescriptorCollection(null);

        foreach (PropertyDescriptor property in base.GetProperties(attributes))
        {
            properties.Add(property);
        }

        foreach (var property in _instanceExtraProperties)
        {
            properties.Add(property);
        }

        return properties;
    }

    public override PropertyDescriptorCollection GetProperties()
    {
        return GetProperties(null);
    }
}
در سازنده این کلاس، لیست خصوصیات الحاقی وهله جاری، در قالب لیستی از ExtraPropertyDescriptor‌ها دریافت شده و با بازنویسی دو متد GetProperties، لیست بدست آماده را به لیست خصوصیات فعلی آن وهله اضافه کرده‌ایم.
متد الحاقی ExtraPropertList به شکل زیر پیاده‌سازی شده‌است:
public static class ExtraProperties
{
    //...

    public static IEnumerable<ExtraPropertyDescriptor<T>> ExtraPropertyList<T>(this object instance) where T : class
    {
        if (!PropertyCache.TryGetValue(instance, out var properties))
            throw new KeyNotFoundException($"key: {instance.GetType().Name} was not found in dictionary");

        return properties.Select(p =>
            new ExtraPropertyDescriptor<T>(p.PropertyName, p.PropertyValueFunc, p.SetPropertyValueFunc,
                p.PropertyType,
                p.Attributes));
    }
}

در اینجا از همان مکانیزم افزودن خواص الحاقی که در ابتدای مطلب اشاره شد، استفاده شده است. 
ExtraPropertyDescriptor به شکل زیر طراحی شده است:
public sealed class ExtraPropertyDescriptor<T> : PropertyDescriptor where T : class
{
    private readonly Func<object, object> _propertyValueFunc;
    private readonly Action<object, object> _setPropertyValueFunc;
    private readonly Type _propertyType;

    public ExtraPropertyDescriptor(
        string propertyName,
        Func<object, object> propertyValueFunc,
        Action<object, object> setPropertyValueFunc,
        Type propertyType,
        Attribute[] attributes) : base(propertyName, attributes)
    {
        _propertyValueFunc = propertyValueFunc;
        _setPropertyValueFunc = setPropertyValueFunc;
        _propertyType = propertyType;
    }

    public override void ResetValue(object component)
    {
    }

    public override bool CanResetValue(object component) => true;

    public override object GetValue(object component) => _propertyValueFunc(component);

    public override void SetValue(object component, object value) => _setPropertyValueFunc(component, value);

    public override bool ShouldSerializeValue(object component) => true;
    public override Type ComponentType => typeof(T);
    public override bool IsReadOnly => _setPropertyValueFunc == null;
    public override Type PropertyType => _propertyType;
}
در نهایت برای استفاده از تامین کننده‌ی طراحی شده، می‌توان به شکل زیر عمل کرد:
[TypeDescriptionProvider(typeof(ExtraPropertyTypeDescriptionProvider<Person>))]
private class Person
{
    public string Name { get; set; }
    public string Family { get; set; }
}
در اینصورت با آزمایش زیر مشخص است که امکان دسترسی به این خصوصیات الحاقی نیز از طریق TypeDescriptor مهیا می‌باشد:
[Test]
public void Should_TypeDescriptor_GetProperties_Returns_ExtraProperties_And_PredefinedProperties()
{
    //Arrange
    var rabbal = new Person {Name = "GholamReza", Family = "Rabbal"};
    const string propertyName = "Title";
    const string propertyValue = "Software Engineer";

    //Act
    rabbal.ExtraProperty(propertyName, propertyValue);
    var title = TypeDescriptor.GetProperties(rabbal).Find(propertyName, true);

    //Assert
    rabbal.ExtraProperty<string>(propertyName).ShouldBe(propertyValue);
    title.ShouldNotBeNull();
    title.GetValue(rabbal).ShouldBe(propertyValue);
}

گام دوم: استفاده از IExtraColumnConvention برای نمایش ستون‌های الحاقی


فرض کنیم 3 پیاده‌سازی از واسط IExtraColumnConvention را توسط مؤلفه‌های مختلف، به شکل داشته باشیم:
public class Column4Convention : IExtraColumnConvention<Product>
{
   public string Name => "Column4";
 
   public string Title => "Column 4"
 
   public void Populate(IEnumerable<Product> list)
   {
      //TODO: forEach on list and set ExtraProperty
      // item.ExtraProperty(Name,value)
      // item.ExtraProperty(Name,(obj)=> value)
      // item.ExtraProperty(Name,(obj)=> value, (obj,value)=>)
   }
}

public class Column2Convention : IExtraColumnConvention<Product>
{
   public string Name => "Column2";
 
   public string Title => "Column 2"
 
   public void Populate(IEnumerable<Product> list)
   {
      //TODO: forEach on list and set ExtraProperty
   }
}

public class Column3Convention : IExtraColumnConvention<Product>
{
   public string Name => "Column3";
 
   public string Title => "Column 3"
 
   public void Populate(IEnumerable<Product> list)
   {
      //TODO: forEach on list and set ExtraProperty
   }
}

سپس این پیاده‌سازی‌ها از طریق مکانیزمی مانند معرفی آنها به یک IoC Container، توسط میزبان (مؤلفه 1) قابل دسترسی خواهد بود. در نهایت میزبان، قبل از نمایش محصولات، به شکل زیر عمل خواهد کرد:
var products = _productService.PagedList(page:1, pageSize:10);
var columns = _provider.GetServices<IExtraColumnConvention<Product>>();
foreach(var column in columns)
{
  column.Populate(products);
}
از این پس خصوصیات الحاقی اضافه شده‌ی توسط مؤلفه‌های دیگر نیز جزئی از خصوصیات محصولات بوده و از طریق TypeDescriptor.GetProperties قابل دسترسی می‌باشد. البته مشخص است راهکاری که در اینجا مطرح شد، وابستگی خیلی زیادی را به مکانیزم استفاده شده در لایه Presentation برای نمایش اطلاعات دارد.
نکته: امکان تهیه ContractResolver سفارشی برای کتابخانه JSON.NET به منظور Serialize خواص الحاقی اضافه شده در زمان اجرا، نیز وجود دارد.

تامین کننده طراحی شده‌ی در این مطلب، به زیرساخت DNTFrameworkCore اضافه شد.
نظرات اشتراک‌ها
قرار دادن سامانه دانشگاه بروی یک Private IP Address
اگه صحبت اینترانت باشه خب وبسایت رو هم روی اینترانتشون راه بندازن. فکر نمیکنم منطقی باشه که بیان یه لینک به اینترانت خودشون بروی اینترنت قرار بدن در حالی که نصف ملت روی اون کلیک میکنن و سردر گم میشن. یا اینکه لینک رو برای دسترسی‌های داخل شبکه ای نمایش بدن.
نظرات اشتراک‌ها
فرق بین TFS ،SVN و GIT در چیست؟
سلام جناب نصیری - بنده قسمتی از کتاب شما در مورد SVN رو مطالعه کردم ، همچنین مقالات دوستان در مورد Git رو هم خودم .
چیزی که متوجه شدم اینه که اگر چند نفر قرار باشه خارج از شبکه با این سیتم‌ها کار کنند ، باید یک سرور ( یک سیستم با Static IP ) داشته باشیم ، آیا این تصور بنده صحیح است ؟

اشتراک‌ها
شبکه های عصبی Auto Encoder از خانواده Deep Learning

امروز الگورتیم‌های زیر مجموعه Deep Learning توانسته اند قدرت پردازشی و آنالیز هوشمند خود را در بسیاری از امور روزمره به اثبات برسانند. یکی از انواع الگوریتم‌های Deep Learning که دارای کاربردی خاص می‌باشد شبکه‌های عصبی AutoEncoder می‌باشند. این شبکه‌های عصبی از دو شبکه کد گذار و کدگشا تشکیل شده اند.

شبکه های عصبی Auto Encoder از خانواده Deep Learning
اشتراک‌ها
پروژه کدباز برای پشتیبانی از ده‌ها سایت جهت احراز هویت در Asp.net Core
Owin OAuth Providers  پروژه کدبازی برای استفاده در Asp.net Core که با استفاده از آن میتوانید امکان لاگین و احراز هویت سایت خود را با دهها سایت و شبکه اجتماعی همسان سازی کنید. 
از جمله ورود با اکانت Google+، Instagram ، GitHub ، LinkedIn ,... 
پروژه کدباز برای پشتیبانی از ده‌ها سایت جهت احراز هویت در Asp.net Core
اشتراک‌ها
یک ابزار رایگان تجمیع پیامهای (لاگهای) شبکه

WhatsUp Gold Syslog Server 

  • یک ابزار غنی تجمیع پیام‌های (لاگهای) شبکه ای با کمک پروتکلهای SNMP و Syslog
  • مشاهده پیام‌های شبکه در زمان واقعی همراه با فیلتر اطلاعات
  • قابل پردازش تا 6 میلیون پیام در ساعت (حدودا 100000 تراکنش در دقیقه)
  • ارسال پیامهای شبکه به طور مستقیم و آنلاین به فایل ویندوز
  • دریافت پیام از دستگاه‌های مختلف از طریق پروتکل‌های UDP و TCP
  • ارسال پیامهای شبکه به صورت آنلاین به سرورهای دیگر با استفاده از پروتکل UDP و TCP
  • حفظ آدرس منبع در هنگام ارسال پیام به یک syslog Server دیگر
  • اجرا به عنوان یک سرویس ویندوزی (Windows Service) و یا در حالت Windows Application
  • فیلتر و مرتب سازی پیام‌های syslog برای نمایش داده شدن
  • همه این قابلیتها در این نرم افزار تحت دات نت فریم ورک 4 پیاده سازی شده است


File size: 5.14MB
 
Operating system: Windows XP/Vista/7/Server2008

Additional Requirements: Microsoft .NET Framework 4.0

یک ابزار رایگان تجمیع پیامهای (لاگهای) شبکه
اشتراک‌ها
کاهش حجم تصاویر وب سایت با فرمت WebP
Stephen Konig در کنفرانس Google I/O اعلام کرد، ما با استفاده از فرمت Webp برای تصاویر نرم‌افزار موبایل Google Plus روزانه چندین ترابایت در ترافیک شبکه صرفه جویی می‌کنیم. با این کار کاربران نیز هزینهٔ کمتری برای استفاده از خدمات ما می‌پردازند.
کاهش حجم تصاویر وب سایت با فرمت WebP