مطالب
Angular CLI - قسمت ششم - استفاده از کتابخانه‌های ثالث
در قسمت قبل با نحوه‌ی ساخت و توزیع برنامه‌های Angular، توسط Angular CLI آشنا شدیم. یکی از فایل‌هایی که توسط سیستم build آن تولید می‌شود، فایل vendor.bundle.js است که شامل کدهای اصلی Angular و همچنین کتابخانه‌های ثالث مورد استفاده‌است و با توجه به اینکه در حالت پیش فرض کار با Angular CLI قرار نیست فایل تنظیمات webpack آن‌را استخراج و ویرایش کنیم، چگونه باید سایر کتابخانه‌های ثالث مورد نیاز را به این سیستم build معرفی کرد؟


استفاده از کتابخانه‌های جاوا اسکریپتی ثالث

برای استفاده از کتابخانه‌های جاوا اسکریپتی ثالث، نیاز است آن‌ها را به فایل angular-cli.json. معرفی کنیم:
  "apps": [
    {
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "styles": [
        "styles.css"
      ],
      "scripts": [],
در اینجا امکان معرفی فایل‌های اسکریپت و همچنین شیوه‌نامه‌های اضافی بیشتری (علاوه بر فایل src\styles.css پیش فرض پروژه) جهت معرفی آن‌ها به سیستم build برنامه موجود است.
به علاوه تعریف پوشه‌ی src\assets را نیز در اینجا مشاهده می‌کنید؛ به همراه فایل‌های اضافی دیگری مانند src\favicon.ico که ذیل آن ذکر شده‌است.


یک مثال: معرفی کتابخانه‌ی ng2-bootstrap به Angular CLI

دریافت و نصب بسته‌های مورد نیاز
مرحله‌ی اول کار با یک کتابخانه‌ی ثالث نوشته شده‌ی برای Angular مانند ngx-bootstrap، دریافت و نصب بسته‌ی npm آن می‌باشد. به همین جهت به ریشه‌ی پروژه وارد شده و دستورات ذیل را صادر کنید تا بوت استرپ و همچنین کامپوننت‌های +Angular 2.0 آن نصب شوند:
> npm install bootstrap --save
> npm install ngx-bootstrap --save

پرچم save در اینجا سبب به روز رسانی خودکار فایل package.json می‌شود:
"dependencies": {
   "bootstrap": "^3.3.7",
   "ngx-bootstrap": "^1.6.6",

معرفی بسته‌های نصب شده به تنظیمات Angular CLI
پس از آن، همانطور که عنوان شد نیاز است به فایل angular-cli.json. مراجعه کرده و شیوه‌نامه‌ی بوت استرپ را تعریف کنیم:
  "apps": [
    {
      "styles": [
    "../node_modules/bootstrap/dist/css/bootstrap.min.css",
        "styles.css"
      ],

چون از ngx-bootstrap استفاده می‌کنیم، نیازی به مقدار دهی مستقیم []:"scripts" فایل angular-cli.json. نیست. ولی اگر خواستید اینکار را انجام دهید، روش آن به صورت ذیل است (که البته نیاز به نصب بسته‌ی jQuery را نیز خواهد داشت):
"scripts": [
   "../node_modules/jquery/dist/jquery.js",
   "../node_modules/bootstrap/dist/js/bootstrap.js"
],

بنابراین تا اینجا بسته‌های بوت استرپ و همچنین ngx-bootstarp نصب شدند و شیوه‌نامه‌ی بوت استرپ به فایل angular-cli.json اضافه گردید (نیازی هم به تکمیل قسمت scripts نیست).


استفاده از ماژول‌های مختلف بسته‌ی نصب شده در برنامه
در ادامه نیاز است تا ماژولی را از ngx-bootstarp را به قسمت imports فایل src\app\app.component.ts اضافه کرد. هرکدام از کامپوننت‌های این بسته به صورت یک ماژول مجزا تعریف شده‌اند. بنابراین برای استفاده‌ی از آن‌ها نیاز است برنامه را از وجودشان مطلع کرد. برای مثال روش استفاده‌ی از AlertModule آن به صورت ذیل است:
import { AlertModule } from 'ngx-bootstrap';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,

    AlertModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
در اینجا ابتدا AlertModule از ngx-bootstrap دریافت شده و سپس به قسمت imports فایل src\app\app.component.ts اضافه گردیده‌است.

آزمایش برنامه و اجرای آن
برای آزمایش مراحل فوق، فایل src/app/app.component.html را گشوده و به صورت ذیل تغییر دهید:
<h1>
  {{title}}
</h1>

<button class="btn btn-primary">Hello!</button>

<alert type="success">Alert success!</alert>
در اینجا یک دکمه‌ی جدید با شیوه‌نامه‌های بوت استرپ اضافه شده‌اند (جهت بررسی عملکرد بوت استرپ) و همچنین یک Alert نیز از مجموعه کامپوننت‌های ngx-bootstrap به صفحه اضافه شده‌است.
اکنون اگر دستور ng serve -o را اجرا کنیم، خروجی ذیل حاصل خواهد شد:



مستندات و مثال‌های بیشتری را از ماژول‌های ngx-bootstarp، در اینجا می‌توانید بررسی کنید.
مطالب
بررسی نحوه‌ی راه اندازی پروژه‌ی Decision
پروژه‌ی Decision را می‌توان چکیده‌ی تمام مطالب سایت دانست که در آن جمع آوری نکات ASP.NET MVC 5.x، EF Code First 6.x، مباحث تزریق وابستگی‌ها، کار با AutoMapper، بوت استرپ 3 و غیره لحاظ شده‌اند. به همین جهت درک آن بدون مطالعه‌ی « تمام » مطالب سایت میسر نیست و همچنین راه اندازی آن.
در این مطلب با توجه به سؤالات زیادی که در مورد صرفا نحوه‌ی اجرای بدون خطای آن وجود داشت، ریز مراحل آن‌را بررسی می‌کنیم.


پیشنیازهای توسعه‌ی برنامه
- با توجه به استفاده از ویژگی‌های C# 6 در این پروژه، حتما نیاز است برای کار و اجرای آن از VS 2015 استفاده کنید.
- همچنین این پروژه از قابلیت «فایل استریم» SQL Server استفاده می‌کند. بنابراین نیاز است نگارش متناسبی از SQL Server را پیشتر نصب کرده باشید (هر نگارشی بالاتر از SQL Server 2005).
- اگر از ReSharper استفاده می‌کنید، به صورت موقت آن‌را به حالت تعلیق درآورید (منوی tools، گرینه‌ی options و انتخاب resharper و سپس suspend کردن آن). این مورد سرعت بازیابی بسته‌های نیوگت را به شدت افزایش می‌دهد.


بازیابی وابستگی‌های نیوگت پروژه

مرسوم نیست چند 10 مگابایت وابستگی‌های پروژه را به صورت فایل‌های باینری، به مخزن کدها ارسال کرد. از این جهت که نیوگت بر اساس مداخل فایل‌های packages.config، قابلیت بازیابی و نصب خودکار آن‌ها را دارد. بنابراین ابتدا package manger console را باز کنید؛ از طریق منوی Tools -> NuGet Package Manager -> Package Manager Console


همانطور که در تصویر مشاهده می‌کنید، نیوگت تشخیص داده‌است که بسته‌هایی برای نصب وجود دارند. بنابراین بر روی دکمه‌ی restore کلیک کنید تا کار دریافت و نصب خودکار این بسته‌ها از اینترنت شروع شود. البته اگر پیشتر این بسته‌ها را در پروژه‌های دیگری نصب کرده باشید، نیوگت از کش موجود در سیستم استفاده خواهد کرد و برای دریافت آن‌ها به اینترنت مراجعه نمی‌کند. ولی در هر حال اتصال به اینترنت ضروری است.

پس از پایان کار بازیابی بسته‌ها، یکبار کل Solution را Build کنید تا مطمئن شوید که تمام بسته‌های مورد نیاز به درستی بازیابی و نصب شده‌اند (Ctrl+Shift+B و یا همان منوی Build و انتخاب گزینه‌ی Build Solution).



تنظیمات رشته اتصالی بانک اطلاعاتی برنامه

پس از Build موفق کل Solution در مرحله‌ی قبل، اکنون نوبت به برپایی تنظیمات بانک اطلاعاتی برنامه است. برای این منظور فایل web.config ذیل را باز کنید:
Decision\src\Decision.Web\Web.config
یک چنین تنظیمی را مشاهده می‌کنید:
  <connectionStrings>
    <clear />
    <add name="DefaultConnection" connectionString="Data Source=.\sqlexpress;Initial Catalog=DecisionDb;Integrated Security = true;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
  </connectionStrings>
از آنجائیکه بر روی سیستم من SQL Server نگارش Developer نصب است و از SQL Server Express استفاده نمی‌کنم، تنظیمات فوق را به نحو ذیل تغییر خواهم داد:
  <connectionStrings>
    <clear />
    <add name="DefaultConnection" connectionString="Data Source=(local);Initial Catalog=DecisionDb;Integrated Security = true;MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
  </connectionStrings>
تنها تغییر صورت گرفته، تنظیم data source است. مابقی موارد یکی است و تفاوتی نمی‌کند.

در این حالت نیاز است بانک اطلاعاتی خالی DecisionDb را خودتان ایجاد کنید. علت آن به AutomaticMigrationsEnabled = false بر می‌گردد؛ که در ادامه توضیح داده شده‌است و همچنین وجود تنظیم ذیل در فایل Decision\src\Decision.Web\App_Start\ApplicationStart.cs
 Database.SetInitializer<ApplicationDbContext>(null);
این تنظیم و نال بودن پارامتر ورودی آن به این معنا است که اولا برنامه یک بانک اطلاعاتی جدید را به صورت خودکار ایجاد نمی‌کند و همچنین کار Migrations خودکار نیست.


ایجاد بانک اطلاعاتی برنامه و تنظیمات آن

پس از آن، نوبت به ایجاد بانک اطلاعاتی برنامه است. چون این برنامه از EF Code first استفاده می‌کند، قادر است بانک اطلاعاتی ذکر شده‌ی در Initial Catalog فوق را به صورت خودکار ایجاد کند (با تمام جداول، روابط و تنظیمات آن‌ها). این اطلاعات هم از پروژه‌ی Decision.DataLayer و پوشه‌ی Migrations آن تامین می‌شوند.
اگر به فایل Decision\src\Decision.DataLayer\Migrations\201602072159421_Initial.cs مراجعه کنید، یکسری تنظیمات دستی را هم علاوه بر کدهای خودکار EF، مشاهده خواهید کرد:
 //. . .
Sql("EXEC sp_configure filestream_access_level, 2");
Sql("RECONFIGURE", true);

Sql("alter database DecisionDb Add FileGroup FileGroupApplicant contains FileStream", true);
Sql("alter database DecisionDb add file ( name = 'ApplicantDocuements'  ,  filename = 'C:\\FileStream\\ApplicantDocuements') to filegroup FileGroupApplicant", true);
//. . .
این‌ها مواردی هستند که کار تنظیمات فایل استریم را به صورت خودکار انجام می‌دهند.
بنابراین نیاز است در درایور C، پوشه‌ی خالی FileStream از پیش تهیه شده باشد (نیازی به ایجاد پوشه‌ی ApplicantDocuements نیست و این پوشه به صورت خودکار ایجاد می‌شود).

و در فایل Decision\src\Decision.DataLayer\Migrations\Configuration.cs مشخص شده‌است که AutomaticMigrationsEnabled = false. به این معنا که تنظیمات فوق به صورت خودکار به بانک اطلاعاتی اعمال نشده و باید چند دستور ذیل را به صورت دستی صادر کنیم:
الف) ابتدا package manager console را مجددا باز کنید و در اینجا default project را بر روی Decision.DataLayer قرار دهید. از این جهت که قرار است اطلاعات migration را از این پروژه دریافت کنیم:


در غیراینصورت پیام خطای No migrations configuration type was found in the assembly را دریافت خواهید کرد.

ب) سپس دستور ذیل را صادر کنید (با این فرض که بانک اطلاعاتی خالی DecisionDb ذکر شده‌ی در قسمت قبل را پیشتر ایجاد کرده‌اید):
 PM> Update-Database -Verbose -ConnectionStringName "DefaultConnection" -StartUpProjectName "Decision.Web"
این تنظیمات به این معنا است که Update-Database را بر اساس اطلاعات پروژه‌ی Decision.DataLayer انجام بده (همان انتخاب default project)؛ اما رشته‌ی اتصالی را از پروژه‌ی Decision.Web و تنظیمات DefaultConnection آن دریافت کن.

من در این حالت پیام خطای Update-Database : The term 'Update-Database' is not recognized as the name of a cmdlet را دریافت کردم.
راه حل: یکبار ویژوال استودیو را بسته و مجددا باز کنید تا کار نصب بسته‌ها و بارگذاری تمام وابستگی‌های آن‌ها به درستی صورت گیرد. این خطا به این معنا است که هرچند NuGet کار نصب EF را انجام داده‌است، اما هنوز اسکریپت‌های پاورشل آن که دستوراتی مانند Update-Database را اجرا می‌کنند، بارگذاری نشده‌اند. راه حل آن بستن و اجرای مجدد ویژوال استودیو است.
پس از اجرای مجدد ویژوال استودیو و انتخاب default project صحیح (مطابق تصویر فوق)، مجددا دستور Update-Database  فوق را صادر کنید (با پارامترهای ویژه‌ی آن).
با صدور این دستور، پیام خطای ذیل را دریافت کردم:
 The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework'
registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded.
برای رفع آن نیاز است EF را یکبار دیگر نصب کنید:
 PM> Update-Package -Reinstall "EntityFramework" -ProjectName "Decision.DataLayer"
در ادامه مجددا کل Solution را Build کنید؛ چون Migrations بر اساس اطلاعات اسمبلی‌های کامپایل شده‌ی پروژه کار می‌کند.
اینبار دستور update-database فوق (با پارامترهای ویژه‌ی آن) بدون مشکل اجرا شد و بانک اطلاعاتی مربوطه تشکیل گردید.




اکنون برنامه قابل اجرا است و در این حالت است که می‌توان دکمه‌ی F5 را جهت اجرای برنامه فشرد. البته در این حالت بر روی پروژه‌ی Decision.Web کلیک راست کرده و گزینه‌ی set as startup project را نیز انتخاب کنید و سپس F5:



لطفا سؤالاتی را که مرتبط با «راه اندازی» این پروژه نیستند، در قسمت بازخوردهای اختصاصی آن مطرح کنید.
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 11 - بررسی رابطه‌ی Self Referencing
- شما در هر دو حالت، دارای لیست منوهایی با 5 آیتم هستید. یعنی خروجی نهایی هر دو کوئری یکی هست و متد Cacheable درست عمل کرده‌است.
- اینکه در کوئری بعدی LINQ to Objects شما، در حالت بدون Cacheable، «امکانات debug visualizer ویژوال استودیو» موفق به تشکیل روابط شده‌است، صرفا به عدم فراخوانی متد AsNoTracking در کوئری غیر کش شده‌ی شما بر می‌گردد. متد AsNoTracking جهت کاهش سربار کوئری‌های EF و حذف پروکسی‌های آن به صورت خودکار توسط متد  Cacheable اعمال می‌شود؛ چون این نوع کوئری‌های کش شده صرفا مختص به گزارشگیری هستند.
- اگر AsNoTracking فراخوانی شود، برای تشکیل روابط صرفا باید از متدهای Include و ThenInclude برای واکشی سطوح دیگر به هم مرتبط استفاده کنید. همچنین هر Include یا ThenInclude فقط یک سطح را واکشی می‌کند.
«...
بدیهی است در اینجا هنوز روش‌های Include و ThenInclue هم جواب می‌دهند؛ اما چون Lazy loading فعال نیست، عملا نمی‌توان تمام زیر ریشه‌ها را یافت ...»

در کل، کوئری دوم شما (حاصل نهایی واکشی تمام عناصر یک جدول) متصل به Context نیست و LINQ to Objects است. بنابراین زمانیکه لیست کامل عناصر را در حافظه‌ی سمت کلاینت دارید (و در اینجا هر عملی بر روی این لیست دوم، نیازی به رفت و برگشت به بانک اطلاعاتی را ندارد)، تشکیل درخت آن‌ها کار مشکلی نیست؛ چون هر آیتم، توسط خاصیت MenuId، به قبلی متصل است و یا خیر. در مثال زیر، لیست نهایی، بر اساس ReplyIdها (دقیقا همان اطلاعات اصلی که در بانک اطلاعاتی ذخیره می‌شود)، حاوی لیست Children چند سطحی خواهد شد. زمانی هم که این لیست را داشتید، از کلاس TreeViewHelper برای نمایش آن استفاده کنید.
        void buildTreeLinqToObjects()
        {
            var comment1 = new BlogComment { Id = 1, Body = "نظر من این است که" };
            var comment12 = new BlogComment { Id = 2, Body = "پاسخی به نظر اول", ReplyId = 1 };
            var comment121 = new BlogComment { Id = 3, Body = "پاسخی به پاسخ به نظر اول", ReplyId = 2 };

            var comment2 = new BlogComment { Id = 4, Body = "نظر من این بود که" };
            var comment22 = new BlogComment { Id = 5, Body = "پاسخی به نظر قبلی", ReplyId = 4 };
            var comment221 = new BlogComment { Id = 6, Body = "پاسخی به پاسخ به نظر من اول", ReplyId = 5 };

            var list = new List<BlogComment>
            {
                comment1, comment12, comment121,
                comment2, comment22, comment221
            };

            foreach (var item in list)
            {
                if (item.ReplyId == null)
                {
                    continue;
                }

                var parent = list.First(x => x.Id == item.ReplyId.Value);
                parent.Children.Add(item);
            }

        }
مطالب
پیاده سازی INotifyPropertyChanged با استفاده از Unity Container
AOP یکی از فناوری‌های مرتبط با توسعه نرم افزار محسوب می‌شود که توسط آن می‌توان اعمال مشترک و متداول موجود در برنامه را در یک یا چند ماژول مختلف قرار داد (که به آن‌ها Aspects نیز گفته می‌شود) و سپس آن‌ها را به مکان‌های مختلفی در برنامه متصل ساخت. عموما Aspects، قابلیت‌هایی را که قسمت عمده‌ای از برنامه را تحت پوشش قرار می‌دهند، کپسوله می‌کنند. اصطلاحا به این نوع قابلیت‌های مشترک، تکراری و پراکنده مورد نیاز در قسمت‌های مختلف برنامه، Cross cutting concerns نیز گفته می‌شود؛ مانند اعمال ثبت وقایع سیستم، امنیت، مدیریت تراکنش‌ها و امثال آن. با قرار دادن این نیازها در Aspects مجزا، می‌توان برنامه‌ای را تشکیل داد که از کدهای تکراری عاری است.

پیاده سازی INotifyPropertyChanged یکی از این مسائل می‌باشد که می‌توان آن را در یک Aspect محصور و در ماژول‌های مختلف استفاده کرد.

مسئله:
کلاس زیر مفروض است:
public class Foo
{
        public virtual int Id { get; set; }

        public virtual string Name { get; set; }
}
اکنون می‌خواهیم  کلاس Foo را به INotifyPropertyChanged مزین، و  یک Subscriber به قسمت set پراپرتی‌های کلاس‌ تزریق کنیم.

راه حل:
ابتدا پکیچ‌های Unity را از Nuget دریافت کنید:
PM> Install-Package Unity.Interception
این پکیچ وابستگی‌های خود را که Unity و CommonServiceLocator هستند نیز دریافت می‌کند.

حال یک Interceptor که اینترفیس IInterceptionBehavior را پیاده سازی می‌کند، می‌نویسیم:
namespace NotifyPropertyChangedInterceptor.Interceptions
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Reflection;
    using Microsoft.Practices.Unity.InterceptionExtension;

    class NotifyPropertyChangedBehavior : IInterceptionBehavior
    {
        private event PropertyChangedEventHandler PropertyChanged;

        private readonly MethodInfo _addEventMethodInfo =
            typeof(INotifyPropertyChanged).GetEvent("PropertyChanged").GetAddMethod();

        private readonly MethodInfo _removeEventMethodInfo =
            typeof(INotifyPropertyChanged).GetEvent("PropertyChanged").GetRemoveMethod();

        
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            if (input.MethodBase == _addEventMethodInfo)
            {
                return AddEventSubscription(input);
            }

            if (input.MethodBase == _removeEventMethodInfo)
            {
                return RemoveEventSubscription(input);
            }
            
            if (IsPropertySetter(input))
            {
                return InterceptPropertySet(input, getNext);
            }
            
            return getNext()(input, getNext);
        }

        public bool WillExecute
        {
            get { return true; }
        }

        public IEnumerable<Type> GetRequiredInterfaces()
        {
            yield return typeof(INotifyPropertyChanged);
        }

        private IMethodReturn AddEventSubscription(IMethodInvocation input)
        {
            var subscriber = (PropertyChangedEventHandler)input.Arguments[0];
            PropertyChanged += subscriber;

            return input.CreateMethodReturn(null);
        }

        private IMethodReturn RemoveEventSubscription(IMethodInvocation input)
        {
            var subscriber = (PropertyChangedEventHandler)input.Arguments[0];
            PropertyChanged -= subscriber;

            return input.CreateMethodReturn(null);
        }

        private bool IsPropertySetter(IMethodInvocation input)
        {
            return input.MethodBase.IsSpecialName && input.MethodBase.Name.StartsWith("set_");
        }

        private IMethodReturn InterceptPropertySet(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            var propertyName = input.MethodBase.Name.Substring(4);

            var subscribers = PropertyChanged;
            if (subscribers != null)
            {
                subscribers(input.Target, new PropertyChangedEventArgs(propertyName));
            }

            return getNext()(input, getNext);
        }
    }
}

متد Invoke : این متد Behavior مورد نظر را پردازش می‌کند (در اینجا، تزریق یک Subscriber در قسمت set پراپرتی ها).
متد GetRequiredInterfaces : یک روش است برای یافتن کلاس هایی که با اینترفیس IInterceptionBehavior مزین شده‌اند.
پراپرتی WillExecute : ابن پراپرتی به Unity می‌گوید که این Behavior اعمال شود یا نه. اگر مقدار برگشتی آن false باشد، متد Invoke اجرا نخواهد شد.
همانطور که در متد Invoke مشاهد می‌کنید، شرط هایی برای افزودن و حذف یک  Subscriber و چک کردن متد set نوشته شده و در غیر این صورت کنترل به متد بعدی داده می‌شود.

اتصال Interceptor به کلاس ها
در ادامه Unity را برای ساخت یک نمونه از کلاس پیکربندی می‌کنیم:
var container = new UnityContainer();

container.RegisterType<Foo, Foo>(
                new AdditionalInterface<INotifyPropertyChanged>(),
                new Interceptor<VirtualMethodInterceptor>(),
                new InterceptionBehavior<NotifyPropertyChangedBehavior>())
                .AddNewExtension<Interception>();
توسط متد RegisterType یک Type را با پیکربندی دلخواه به Unity معرفی می‌کنیم. در اینجا به ازای درخواست Foo (اولین پارامتر جنریک)، یک Foo (دومین پارامتر جنریک ) برگشت داده می‌شود. این متد تعدادی InjetctionMember (بصورت params) دریافت می‌کند که در این مثال سه InjetctionMember  به آن پاس داه شده است:
  • Interceptor : اطلاعاتی در مورد IInterceptor و نحوه‌ی Intercept یک شیء را نگه داری می‌کند. در اینجا از  VirtualMethodInterceptor برای تزریق کد استفاده شده.
  • InterceptionBehavior : این کلاس Behavior مورد نظر را به کلاس تزریق می‌کند.
  • AddintionalInterface  : کلاس target را مجبور به پیاده سازی اینترفیس دریافتی از پارامتر می‌کند.  اگر کلاس behavior، متد  GetRequiredInterfaces  اینترفیس INotifyPropertyChanged را برمی گرداند، نیازی نیست از AddintionalInterface در پارامتر متد فوق استفاده کنید. 

نکته :
کلاس VirtualMethodInterceptor فقط اعضای virtual را تحت تاثیر قرار می‌دهد.
اکنون نحوه‌ی ساخت یک نمونه از کلاس Foo به شکل زیر است:
var foo = container.Resolve<Foo>();
(foo as INotifyPropertyChanged).PropertyChanged += FooPropertyChanged;
private void FooPropertyChanged (object sender, PropertyChangedEventArgs e)
 {
      // Do some things.......
 }

نکته‌ی تکمیلی
طبق مستندات MSDN، کلاس VirtualMethodInterceptor  یک کلاس جدید مشتق شده از کلاس target (در اینجا Foo) می‌سازد. بنابراین اگر کلاس‌های شما دارای Data annotation و یا در کلاس‌های Mapper یک ORM استفاده شده‌اند (مانند کلاس‌های لایه Domain)، بجای  VirtualMethodInterceptor  از TransparentProxyInterceptor استفاده کنید.
سرعت اجرای VirtualMethodInterceptor سریعتر است ؛ اما به یاد داشته که برای استفاده از  TransparentProxyInterceptor  باید کلاس target از کلاس MarshalByRefObject ارث بری کند.
اشتراک‌ها
پروژه CRUD دات نت 7 با Angular و Web API

.NET 7 💥 - CRUD with Angular & .NET Web API, EF Core & MongoDb

In this video we will be going to be exploring creating a full application from frontend with Angular and backend with WebApi and database as MongoDb
 

پروژه CRUD دات نت 7 با Angular و Web API
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 6 - تعیین نوع‌های داده و ویژگی‌های آن‌ها
از EF Core 1.1 با استفاده از دستور IsRowVersion  نیز می‌توان برای مسائل همزمانی به عنوان یک Fluent API به صورت زیر استفاده کرد
public byte[] RowVersion { get; set; }
modelBuilder.Entity<Blog>()
   .Property(p => p.RowVersion)
   .IsRowVersion();

که معادل است با اجرای هر دو متد روش Timestamp/row version:
public virtual PropertyBuilder IsRowVersion()
{
   Builder.ValueGenerated(ValueGenerated.OnAddOrUpdate, ConfigurationSource.Explicit);
   Builder.IsConcurrencyToken(true, ConfigurationSource.Explicit);

   return this;
}
نظرات مطالب
ارتقاء به ASP.NET Core 2.0 - معرفی بسته‌ی Microsoft.AspNetCore.All
 ارتقاء به ASP.NET Core 2.1 - معرفی بسته‌ی Microsoft.AspNetCore.App 
Microsoft.AspNetCore.All شامل یکسری بسته‌ی پشتیبانی نشده‌ی توسط تیم ASP.NET Core مایکروسافت هم هست. در نگارش 2.1 این بسته‌های مستقیما پشتیبانی نشده حذف شده و بجای آن meta-package جدیدی به نام Microsoft.AspNetCore.App معرفی شده‌است. این meta-package شامل بسته‌های زیر نیست:
• Microsoft.Data.Sqlite
• Microsoft.Data.Sqlite.Core
• Microsoft.EntityFrameworkCore.Sqlite
• Microsoft.EntityFrameworkCore.Sqlite.Core
• Microsoft.Extensions.Caching.Redis
• Microsoft.AspNetCore.DataProtection.AzureStorage
• Microsoft.Extensions.Configuration.AzureKeyVault
• Microsoft.AspNetCore.DataProtection.AzureKeyVault
• Microsoft.AspNetCore.Identity.Service.AzureKeyVault
• Microsoft.AspNetCore.AzureKeyVault.HostingStartup
• Microsoft.AspNetCore.ApplicationInsights.HostingStartup
اگر به آن‌ها نیاز داشتید، باید مستقیما ارجاعات آن‌ها را تعریف کنید.
meta-package قبلی صرفا تا پایان طول عمر سری 2x پشتیبانی خواهد شد.
مسیر جدید فایل‌های آن‌ها: C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App