مسیرراه‌ها
ASP.NET MVC
              مطالب
              آزمایش Web APIs توسط Postman - قسمت ششم - اعتبارسنجی مبتنی بر JWT
              برای مطلب «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» و پروژه‌ی آن، یک چنین رابط کاربری آزمایشی تهیه شده‌است:


              اکنون در ادامه قصد داریم این موارد را تبدیل به چندین درخواست به هم مرتبط postman کرده و در نهایت آن‌ها را به صورت یک collection قابل آزمایش مجدد، ذخیره کنیم.

              مرحله 1: خاموش کردن بررسی مجوز SSL برنامه

              چون مجوز SSL برنامه‌های ASP.NET Core که در حالت local اجرا می‌شوند از نوع self-signed certificate است، توسط postman پردازش نخواهند شد. به همین جهت نیاز است به منوی File -> Settings آن مراجعه کرده و این بررسی را خاموش کنیم:



              مرحله 2: ایجاد درخواست login و دریافت توکن‌ها


              در اینجا این مراحل طی شده‌اند:
              - ابتدا آدرس درخواست به https://localhost:5001/api/account/login تنظیم شده‌است.
              - سپس نوع درخواست نیز به Post تغییر کرده‌است.
              - چون اکنون نوع درخواست، Post است، می‌توان بدنه‌ی آن‌را مقدار دهی کرد و چون نوع آن JSON است، گزینه‌ی raw و سپس contentType صحیح انتخاب شده‌اند. در ادامه مقدار زیر تنظیم شده‌است:
              {
                  "username": "Vahid",
                  "password": "12345"
              }
              این مقداری‌است که اکشن متد login می‌پذیرد. البته اگر برنامه را اجرا کنید، کلمه‌ی عبور پیش‌فرض آن 1234 است.
              - پس از این تنظیمات اگر بر روی دکمه‌ی send کلیک کنیم، توکن‌های دریافتی را در قسمت response می‌توان مشاهده کرد.


              مرحله‌ی 3: ذخیره سازی توکن‌های دریافتی در متغیرهای سراسری

              برای دسترسی به منابع محافظت شده‌ی سمت سرور، نیاز است access_token را به همراه هر درخواست، به سمت سرور ارسال کرد. بنابراین نیاز است در همینجا این دو توکن را در متغیرهایی برای دسترسی بعدی، ذخیره کنیم:

              var jsonData = pm.response.json();
              pm.globals.set("access_token", jsonData.access_token);
              pm.globals.set("refresh_token", jsonData.refresh_token);
              محل تعریف این کدها نیز در قسمت Tests درخواست جاری است تا پس از پایان کار درخواست، اطلاعات آن استخراج شده و ذخیره شوند.


              مرحله‌ی 3: ذخیره سازی مراحل انجام شده

              برای این منظور، بر روی دکمه‌ی Save کنار Send کلیک کرده، نام Login را وارد و سپس یک Collection جدید را با نام دلخواه JWT Sample ایجاد می‌کنیم:


              سپس این درخواست را در این مجموعه ذخیره خواهیم کرد.


              مرحله‌ی 4: دسترسی به منابع محافظت شده‌ی سمت سرور

              برای این منظور بر روی دکمه‌ی + موجود در کنار اولین برگه‌ای که مشغول به تکمیل آن هستیم، کلیک می‌کنیم تا یک برگه‌ی جدید ایجاد شود. سپس مشخصات آن را به صورت زیر تکمیل خواهیم کرد:


              ابتدا آدرس درخواست از نوع Get را به https://localhost:5001/api/MyProtectedApi تنظیم خواهیم کرد.
              سپس گزینه‌ی هدرهای این درخواست را انتخاب کرده و key/value جدیدی با مقادیر Authorization و Bearer {{access_token}} ایجاد می‌کنیم. در اینجا {{access_token}} همان متغیر سراسری است که پس از لاگین، تنظیم می‌شود. اکنون از این متغیر جهت تنظیم هدر Authorization استفاده کرده‌ایم.
              در آخر اگر بر روی دکمه‌ی Send این درخواست کلیک کنیم، response فوق را می‌توان مشاهده کرد.

              فراخوانی مسیر api/MyProtectedAdminApi نیز دقیقا به همین نحو است.

              یک نکته: روش دومی نیز برای تنظیم هدر Authorization در postman وجود دارد. برای این منظور، گزینه‌ی Authorization یک درخواست را انتخاب کنید (تصویر زیر). سپس نوع آن‌را به Bearer token تغییر دهید و مقدار آن‌را به همان متغیر سراسری {{access_token}} تنظیم کنید. به این صورت هدر مخصوص JWT را به صورت خودکار ایجاد و ارسال می‌کند و در این حالت دیگر نیازی به تنظیم دستی برگه‌ی هدرها نیست.



              مرحله‌ی 5: ارسال Refresh token و دریافت یک سری توکن جدید

              این مرحله، نیاز به ارسال anti-forgery token را هم دارد و گرنه از طرف سرور و برنامه، برگشت خواهد خورد. اگر به کوکی‌های تنظیم شده‌ی توسط برگه‌ی لاگین دقت کنیم:


              کوکی با نام XSRF-TOKEN نیز تنظیم شده‌است. بنابراین آن‌را توسط متد pm.cookies.get، در قسمت Tests برگه‌ی لاگین خوانده و در یک متغیر سراسری تنظیم می‌کنیم:
              var jsonData = pm.response.json();
              pm.globals.set("access_token", jsonData.access_token);
              pm.globals.set("refresh_token", jsonData.refresh_token);
              pm.globals.set('XSRF-TOKEN', pm.cookies.get('XSRF-TOKEN'));
              سپس برگه‌ی جدید ایجاد درخواست refresh token به صورت زیر تنظیم می‌شود:
              ابتدا در قسمت بدنه‌ی درخواست از نوع post به آدرس https://localhost:5001/api/account/RefreshToken، در قسمت raw آن، با انتخاب نوع json، این refresh token را که در قسمت لاگین خوانده و ذخیره کرده بودیم، به سمت سرور ارسال خواهیم کرد:


              همچنین دو هدر زیر را نیز باید ارسال کنیم:


              یکی هدر مخصوص Authorization است که در مورد آن بحث شد و دیگر هدر X-XSRF-TOKEN که در سمت سرور بجای anti-forgery token پردازش می‌شود. مقدار آن‌را نیز در برگه‌ی login با خواندن کوکی مرتبطی، پیشتر تنظیم کرده‌ایم که در اینجا از متغیر سراسری آن استفاده شده‌است.

              اکنون اگر بر روی دکمه‌ی send این برگه کلیک کنید، دو توکن جدید را دریافت خواهیم کرد:


              که نیاز است مجددا در برگه‌ی Tests آن، متغیرهای سراسری پیشین را بازنویسی کنند. به همین جهت دقیقا همان 4 سطری که اکنون در برگه‌ی Tests درخواست لاگین وجود دارند، باید در اینجا نیز تکرار شوند تا عملیات refresh token واقعا به تمام برگه‌های موجود، اعمال شود.


              مرحله‌ی آخر: پیاده سازی logout

              در اینجا نیاز است refreshToken را به صورت یک کوئری استرینگ به سمت سرور ارسال کرد که به کمک متغیر {{refresh_token}}، در برگه‌ی params تعریف می‌شود:


              همچنین هدر Authorization را نیز باید درج کرد:


              پس از آن اگر درخواست را رسال کنیم، یک true را باید بتوان در response مشاهده کرد:



              ذخیره سازی مجموعه‌ی جاری به صورت یک فایل JSON

              برای گرفتن خروجی از این مجموعه و به اشتراک گذاشتن آن‌، اگر اشاره‌گر ماوس را بر روی نام یک مجموعه حرکت دهیم، یک دکمه‌ی جدید با برچسب ... ظاهر می‌شود. با کلیک بر روی آن، یکی از گزینه‌های آن، export نام دارد که جزئیات تمام درخواست‌های یک مجموعه را به صورت یک فایل JSON ذخیره می‌کند. برای نمونه فایل JSON خروجی این قسمت را می‌توانید از اینجا دریافت کنید: JWT-Sample.postman_collection.json
              اشتراک‌ها
              Visual Studio 2019 version 16.6.0 منتشر شد

              Top Issues Fixed in Visual Studio 2019 version 16.6.0

              • When New Git experience feature flag is enabled, a message will appear in Team Explorer guiding users to the new Git tool window.
              • Fix for intermittent UI delay while closing VS when WinForms .NET Core designer is in open state.
              • Fixed issues creating projects using type providers, throwing missing method exception at runtime.
              • Fixed project creation for .NET framework projects.
              • New find in files experience respects options in Tools-Options-Find and Replace pane.
              • Fixed a bug where Git repository does not change when closing a Folder and opening a Solution.
              • Fixed bug when building iOS app using full debug symbols.
              • Added back browing of Mac Distribution provisioning profiles and certificates from Windows.
              • Fixed a bug causing Visual Studio 2019 to stop responding when working with Xamarin projects on certain scenarios.
              • Added keyboard shortcut for "Copy with Headers" option in SQL Script Results Grid
              • SSDT users will now be able to set and view sensitivity properties for all version above SQL Server 2008
              • Improve Connection Properties dialog for accessibility users.
              • Fixed occasional crashes when using Tested By Code Lens indicator.
              • Ensure auto population of text in Find in files is as per legacy behavior.
              • Ensure left arrow key behavior in find in files is correct.
              • An issue blocking C++ users of the C++20 Ranges library from using algorithms. 
              Visual Studio 2019 version 16.6.0 منتشر شد
              اشتراک‌ها
              اکنون می توانید توسط یک افزونه هر برنامه NET. را به آخرین نسخه دات نت در داخل ویژوال استودیو ارتقا دهید

              Now you can upgrade any .NET application to the latest version of .NET inside of Visual Studio! We are happy to introduce it as a Visual Studio extension and will upgrade your .NET Framework or .NET Core web- and desktop apps. In this video, Olia shows you how to get the extension and start to update your projects to the latest version of NET in minutes. 


              اکنون می توانید توسط یک افزونه هر برنامه NET. را به آخرین نسخه دات نت در داخل ویژوال استودیو ارتقا دهید
              مطالب
              شروع کار با GraphQL در ASP.NET Core
              در این مقاله هدف این است که GraphQL را در ASP.NET Core راه اندازی کنیم. از یک کتابخانه ثالث برای آسان‌تر کردن یکپارچگی استفاده می‌کنیم و همچنین با جزئیات، توضیح خواهیم داد که چگونه می‌توان از element‌های مربوط به GraphQL مثل (Type ،Query و Schema) برای کامل کردن فرآیند یکپارچگی در ASP.NET Core استفاده کنیم.

              GraphQL  و تفاوت‌های آن با REST
              GraphQl یک query language می‌باشد که query‌ها را با استفاده از type system‌‌‌هایی که ما برای داده‌ها تعریف می‌کنیم، اجرا می‌کند. GraphQL  به هیچ زبان یا پایگاه داده مشخصی گره نخورده است. 

              •  GraphQL نیازمند رفت و برگشت‌های کمتری به server، به منظور بازیابی داده‌ها برای template یا view است. همراه با REST ما مجبور هستیم که چندیدن endpoint مثلا  (... api/students, api/courses, api/instructors ) را برای گرفتن همه داده‌های که برای template یا view نیاز داریم ملاقات کنیم؛ ولی این شرایط در GraphQL برقرار نیست. با GraphQL ما تنها یک query را ایجاد می‌کنیم که چندین تابع (resolver) را در سمت سرور فراخوانی می‌کند و همه داده‌ها را از منابع مختلفی، در یک درخواست برگشت می‌دهد. 

              • همراه با REST، همانطور که Application ما رشد می‌کند، تعداد endpoint‌ها هم زیاد می‌شوند که این نیازمند زمان بیشتری برای نگهداری می‌باشد. اما با GraphQL ما تنها یک endpoint  داریم؛ همین! 
              • با استفاده از GraphQL، ما هرگز به مشکل گرفتن داده‌هایی کم یا زیاد از منبع روبرو نخواهیم شد. به این خاطر است که ما query‌ها را با فیلد‌هایی که چیز‌هایی را که نیاز داریم، نشان می‌دهند، تعریف می‌کنیم. در این صورت ما همیشه چیز‌هایی را که درخواست داده‌ایم، دریافت می‌کنیم.

              بنابراین اگر یک query شبیه زیر را ارسال کنیم :
              query OwnersQuery {
                owners {
                  name
                  account {
                    type
                  }
                } 
              }
              صد درصد مطمئن هستیم که خروجی زیر برگشت داده می‌شود:
              {
                "data": {
                  "owners": [
                   {
                    "name": "John Doe",
                    "accounts": [
                      {
                        "type": "Cash"
                      },
                      {
                        "type": "Savings"
                      }
                    ]
                   }
                  ]
                }
              }

              همراه با REST  این شرایط برقرار نمی‌باشد. بعضی از مواقع ما چیزی بیشتر یا کمتر از آنچه که نیاز داریم دریافت می‌کنیم؛ که این بستگی به اینکه چگونه action‌ها در یک endpoint مشخص، پیاده سازی شده‌اند، دارد.  

              شروع کار 
              یک پروژه جدید را با استفاده از دستور زیر ایجاد می‌کنیم: 
              dotnet new api -n ASPCoreGraphQL
              سپس ساختار زیر را ایجاد می‌کنیم :


              پوشه Contracts شامل واسط‌های مورد نیاز برای repository logic می‌باشد:

              namespace ASPCoreGraphQL.Contracts
              {
                  public interface IOwnerRepository
                  {
                  }
              }
              namespace ASPCoreGraphQL.Contracts
              {
                  public interface IAccountRepository
                  {
                  }
              }


              در پوشه Models، کلاس‌های مدل را نگه داری می‌کنیم؛ به همراه یک کلاس context و کلاس‌های configuration:
              public class Owner
              {
                  [Key]
                  public Guid Id { get; set; }
                  [Required(ErrorMessage = "Name is required")]
                  public string Name { get; set; }
                  public string Address { get; set; }
               
                  public ICollection<Account> Accounts { get; set; }
              }
              
              public class Account
              {
                  [Key]
                  public Guid Id { get; set; }
                  [Required(ErrorMessage = "Type is required")]
                  public TypeOfAccount Type { get; set; }
                  public string Description { get; set; }
               
                  [ForeignKey("OwnerId")]
                  public Guid OwnerId { get; set; }
                  public Owner Owner { get; set; }
              }
              
              public enum TypeOfAccount
              {
                  Cash,
                  Savings,
                  Expense,
                  Income
              }
                  public class ApplicationContext : DbContext
                  {
                      public ApplicationContext(DbContextOptions options)
                          : base(options)
                      {
                      }
              
                      protected override void OnModelCreating(ModelBuilder modelBuilder)
                      {
              
                      }
              
                      public DbSet<Owner> Owners { get; set; }
                      public DbSet<Account> Accounts { get; set; }
                  }

              در پوشه Repository، کلاس‌های مرتبط با منطق بازیابی داده‌ها را داریم:
              public class OwnerRepository : IOwnerRepository
              {
                  private readonly ApplicationContext _context;
               
                  public OwnerRepository(ApplicationContext context)
                  {
                      _context = context;
                  }
              }
              public class AccountRepository : IAccountRepository
              {
                  private readonly ApplicationContext _context;
               
                  public AccountRepository(ApplicationContext context)
                  {
                      _context = context;
                  }
              }
              repository logic، یک راه اندازی ابتدایی بدون هیچ لایه اضافه‌تری است.

              کلاس context و کلاس‌های repository، در فایل Startup.cs ثبت می‌شوند:
              public void ConfigureServices(IServiceCollection services)
              {
                  services.AddDbContext<ApplicationContext>(opt =>
                       opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 
               
                  services.AddScoped<IOwnerRepository, OwnerRepository>();
                  services.AddScoped<IAccountRepository, AccountRepository>();
               
                  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                      .AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
              }


              Integration of GraphQL in ASP.NET Core 
              برای کار کردن با GraphQL در ابتدا نیاز است که کتابخانه GraphQL را نصب کنیم. به همین منظور در ترمینال مربوط به VS Code، دستور زیر را وارد می‌کنیم: 
              dotnet add package GraphQL
              و همچنین کتابخانه‌ی زیر که به ما کمک می‌کند تا GraphQL.NET را به عنوان یک وابستگی دریافت کنیم:
              dotnet add package GraphQL.Server.Transports.AspNetCore
              و در نهایت نصب کتابخانه زیر که کمک می‌کند query‌های GraphQL را به سرور ارسال کنیم:
              dotnet add package GraphQL.Server.Ui.Playground

              (Creating GraphQL Specific Objects (Type, Query, Schema

              کار را با ایجاد کردن یک پوشه جدید به نام GraphQL و سپس در آن ایجاد یک پوشه دیگر به نام GraphQLSchema، شروع می‌کنیم. در پوشه GraphQLSchema، یک کلاس را به نام AppSchema، ایجاد می‌کنیم.
              کلاس AppSchema باید از کلاس Schema ارث بری کند تا در فضای نام GraphQL.Types قرار گیرد. در سازنده این کلاس، IDependencyResolver را تزریق می‌کنیم که قرار است به ما کمک کند تا اشیاء Query ،Mutation یا Subscription را resolve کنیم:
              public class AppSchema : Schema
              {
                  public AppSchema(IDependencyResolver resolver)
                      :base(resolver)
                  {
               
                  }
              }
              چیزی که مهم است بدانیم این است که خصوصیات schema، مثل Query ،Mutation و Subscription، واسط IObjectGraphType را پیاده سازی می‌کنند تا اشیائی را که قرار است resolve کنیم، همین type را پیاده سازی کرده باشند. در ضمن GraphQL API نمی‌تواند مدل را به عنوان نتیجه به صورت مستقیم بازگشت دهد.
              فعلا این کلاس را در همین حالت می‌گذاریم و سپس یک پوشه را به نام GraphQLTypes در پوشه GraphQL ایجاد می‌کنیم. در پوشه GraphQLTypes یک کلاس را به نام OwnerType ایجاد می‌کنیم:
              public class OwnerType : ObjectGraphType<Owner>
              {
                  public OwnerType()
                  {
                      Field(x => x.Id, type: typeof(IdGraphType)).Description("Id property from the owner object.");
                      Field(x => x.Name).Description("Name property from the owner object.");
                      Field(x => x.Address).Description("Address property from the owner object.");
                  }
              }
              از کلاس OwnerType به عنوان یک جایگزین برای مدل Owner درون یک GraphQL API استفاده می‌کنیم. این کلاس از نوع جنریک ObjectGraphType ارث بری می‌کند. با متد Field، فیلد‌هایی را که بیانگر خصوصیات مدل Owner می‌باشند، مشخص می‌کنیم.
              در ابتدا واسط IOwnerRepository و کلاس  OwnerRepository را به حالت زیر ویرایش می‌کنیم:
              public interface IOwnerRepository
              {
                  IEnumerable<Owner> GetAll();
              }
              
              
              public class OwnerRepository : IOwnerRepository
              {
                  private readonly ApplicationContext _context;
               
                  public OwnerRepository(ApplicationContext context)
                  {
                      _context = context;
                  }
               
                  public IEnumerable<Owner> GetAll() => _context.Owners.ToList();
              }

              در ادامه یک پوشه دیگر را به نام GraphQLQueries در پوشه‌ی GraphQL ایجاد و سپس در آن یک کلاس را به نام AppQuery ایجاد و آن را به حالت زیر ویرایش می‌کنیم:
              public class AppQuery : ObjectGraphType
              {
                  public AppQuery(IOwnerRepository repository)
                  {
                      Field<ListGraphType<OwnerType>>(
                         "owners",
                         resolve: context => repository.GetAll()
                     );
                  }
              }

              توضیحات AppQuery
              همانطور که می‌بینیم این کلاس از ObjectGraphType ارث بری می‌کند. در سازنده کلاس، IOwnerRepository را تزریق می‌کنیم و یک فیلد را به منظور برگشت دادن نتیجه برای یک Query مشخص، ایجاد می‌کنیم. در این کلاس، از نوع جنریک متد Field، استفاده کرده‌ایم که تعدادی type را به عنوان یک پارامتر جنریک پذیرش می‌کند که این بیانگر GraphQL.NET برای type ‌های معمول در NET. می‌باشد. علاوه بر ListGraphType،  نوع‌هایی مثل IntGraphType  و StringGraphType و ... وجود دارند (لیست کامل).
              پارامتر owners نام فیلد می‌باشد (query مربوط به کلاینت باید با این نام مطابقت داشته باشد) و پارامتر دوم نتیجه می‌باشد.
              بعد از انجام این مقدمات، اکنون کلاس AppSchema  را باز می‌کنیم و به حالت زیر آن را ویرایش می‌کنیم:
              public class AppSchema : Schema
              {
                  public AppSchema(IDependencyResolver resolver)
                      :base(resolver)
                  {
                      Query = resolver.Resolve<AppQuery>();
                  }
              }

              Libraries and Schema Registration 
               در کلاس Startup نیاز است کتابخانه‌های نصب شده و هم چنین  کلاس schema ایجاد شده را ثبت کنیم. این کار را با ویرایش کردن متد ConfigureServices  و Configure  انجام میدهیم:
              public void ConfigureServices(IServiceCollection services)
              {
                  ...
               
                  services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
                  services.AddScoped<AppSchema>();
               
                  services.AddGraphQL(o => { o.ExposeExceptions = false; })
                      .AddGraphTypes(ServiceLifetime.Scoped);
              
                 ...
              }
              در متد ConfigureServices، در ابتدا DependencyResolver و سپس کلاس schema را ثبت می‌کنیم. علاوه بر آن، GraphQL را با متد AddGraphQL ثبت می‌کنیم. سپس همه تایپ‌های GraphQL را با متد AddGraphTypes ثبت می‌کنیم. بدون این متد (AddGraphTypes)، ما مجبور بودیم که همه type ‌ها و query ‌ها را به صورت دستی در APIامان ثبت کنیم.
              در نهایت در متد schema  Configure را به خط لوله درخواست‌ها (request’s pipeline) اضافه می‌کنیم و هم چنین ابزار Playground UI:
              public void Configure(IApplicationBuilder app, IHostingEnvironment env)
              {
                 ...
                  app.UseGraphQL<AppSchema>();
                  app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions());
                  app.UseMvc();
              }

              برای آزمایش GraphQL API امان، از ابزار GraphQL.UI.Playground استفاده می‌کنیم. در ابتدا پروژه را با دستور زیر اجرا می‌کنیم:
              dotnet run
              و سپس در مرورگر به این آدرس می‌رویم: 
              https://localhost:5001/ui/playground
              اکنون یک query را با نام owners ارسال می‌کنیم (این نام باید با نام query که در AppQuery در نظر گرفتیم، مطابقت داشته باشد). سپس نتیجه لازم را دریافت می‌کنیم. همانطور که مشاهده می‌کنیم، همه چیز بر اساس انتظاری که می‌رفت کار می‌کند. 



              در قسمت بعد درابطه با  کوئری‌های پیشرفته، Error Handling و Data Loader در GraphQL  صحبت خواهیم کرد. 

              کد‌های مربوط به این قسمت را از اینجا دریافت کنید .ASPCoreGraphQL.zip
              نظرات مطالب
              امکان انجام محاسبات سمت کلاینت در EF Core
              یک نکته‌ی تکمیلی: کدامیک از متدهای کار با رشته‌ها در سمت کلاینت پردازش می‌شوند و کدام‌ها در سمت سرور؟
              در مثال‌های زیر، هر جائیکه قسمت where عبارت SQL حذف شده‌، یعنی Client-Side Evaluation رخ داده‌است:
              - با اضافه شدن نوع مقایسه، محاسبه‌ی سمت کلاینت رخ می‌دهد:
              var test1 = context.Blogs
              .Where(blog => String.Compare(blog.Url, "A", StringComparison.Ordinal) > 0)
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              - بدون مشخص سازی نوع مقایسه، محاسبه‌ی سمت سرور را خواهیم داشت:
              var test2 = context.Blogs
              .Where(blog => String.Compare(blog.Url, "B") > 0)
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              // WHERE [blog].[Url] > N'B'
              - در مورد متد Equals هم دقیقا به همین صورت است. با اضافه شدن نوع مقایسه، محاسبه‌ی سمت کلاینت رخ می‌دهد: 
              var test3 = context.Blogs
              .Where(blog => blog.Url.Equals("C", StringComparison.OrdinalIgnoreCase))
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              - بدون مشخص سازی نوع مقایسه، محاسبه‌ی سمت سرور را خواهیم داشت: 
              var test3_1 = context.Blogs
              .Where(blog => blog.Url.Equals("C_1"))
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              // WHERE [blog].[Url] = N'C_1'
              - StartsWith و EndsWith در سمت سرور محاسبه می‌شوند:
              var test4 = context.Blogs
              .Where(blog => blog.Url.StartsWith("D"))
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              // WHERE [blog].[Url] LIKE N'D' + N'%' AND (LEFT([blog].[Url], LEN(N'D')) = N'D')
              - اما اگر از عبارات SQL آن‌ها راضی نیستید، از متد EF.Functions.Like استفاده کنید:
              var test5 = context.Blogs
              .Where(blog => EF.Functions.Like(blog.Url, "S_i%"))
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              // WHERE [blog].[Url] LIKE N'S_i%'
              - متدهای ToUpper و ToLower سمت سرور محاسبه می‌شوند (هرچند اگر از SQL Server استفاده می‌کنید، Collation پیش‌فرض آن غیرحساس به حروف کوچک و بزرگ است و در اکثر مواقع نیازی به یک چنین متدهایی نیست):
              var test6 = context.Blogs
              .Where(blog => blog.Url.ToUpper() == "E")
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              // WHERE UPPER([blog].[Url]) = N'E'
              - اما حالت‌های Invariant دار آن‌ها خیر و در سمت کلاینت محاسبه می‌شوند:
              var test7 = context.Blogs
              .Where(blog => blog.Url.ToUpperInvariant() == "F")
              .ToList();
              // SELECT [blog].[BlogId], [blog].[Url]
              // FROM [Blogs] AS [blog]
              اشتراک‌ها
              کتاب رایگان NancyFX Succinctly

              In NancyFX Succinctly, you'll learn what NancyFX is all about, where it came from, and what it can do for you as a .NET developer. Author Peter Shaw will explain why NancyFX is more than just another web framework, and teach you some of the many tricks that make it as easy as possible for you to create stunning web-enabled applications on the .NET platform.  

              کتاب رایگان NancyFX Succinctly
              نظرات مطالب
              Blazor 5x - قسمت هشتم - مبانی Blazor - بخش 5 - تامین محتوای نمایشی کامپوننت‌های فرزند توسط کامپوننت والد
              یک نکته‌ی تکمیلی: روش ارسال داده‌ها به RenderFragmentها

              در مباحث اعتبارسنجی و احراز هویت Blazor در قسمت‌های بعدی، به قطعه کد context@ داری در داخل یک RenderFragment خواهیم رسید:
              <AuthorizeView>
                  <Authorized>
                              Hello, @context.User.Identity.Name!
                  </Authorized>
              </AuthorizeView>
              اکنون این سؤال مطرح است که این context@ از کجا آمده‌است و چه مفهومی را دارد؟
              برای پاسخ به این سؤال نیاز است با مفهوم «Templated components» در برنامه‌های Blazor آشنا شد. تا اینجا از RenderFragmentها صرفا جهت فراهم آوردن قسمت ثابتی از قالب کامپوننت جاری، توسط استفاده کننده‌ی از آن، کمک گرفتیم. اما در همان سمت استفاده کننده، امکان دسترسی به اطلاعات مهیای داخل آن فرگمنت نیز وجود دارد. برای نمونه به کدهای کامپوننت TableTemplate.razor دقت کنید:
              @typeparam Titem
              
              <table class="table">
                  <thead>
                      <tr>@TableHeader</tr>
                  </thead>
                  <tbody>
                      @foreach (var item in Items)
                      {
                          <tr>@RowTemplate(item)</tr>
                      }
                  </tbody>
              </table>
              
              @code {
                  [Parameter]
                  public RenderFragment TableHeader { get; set; }
              
                  [Parameter]
                  public RenderFragment<TItem> RowTemplate { get; set; }
              
                  [Parameter]
                  public IReadOnlyList<TItem> Items { get; set; }
              }
              - این کامپوننت، قالب سفارشی ثابت هدر جدول را توسط یک RenderFragment، از بکارگیرنده‌ی خود دریافت می‌کند.
              - همچنین یک RenderFragment جنریک را نیز مشاهده می‌کنید که قالب ردیف‌های جدول را تامین می‌کند. نوع جنریک قابل دسترسی در این کامپوننت، توسط دایرکتیو typeparam Titem@ تعریف شده‌ی در ابتدای آن، مشخص شده‌است.
              - بنابراین هربار که ردیفی از بدنه‌ی جدول در حال رندر است، یک شیء item از نوع TItem را به قالب سفارشی تامین شده‌ی توسط بکارگیرنده‌ی خود ارسال می‌کند.
              اکنون این سؤال مطرح می‌شود که چگونه می‌توان به شیء item، در سمت والد یا بکارگیرنده‌ی کامپوننت TableTemplate فوق دسترسی یافت؟
              برای اینکار می‌توان از پارامتر ویژه‌ای به نام context که یک implicit parameter است و به صورت پیش‌فرض تعریف شده و مهیا است، در سمت کدهای بکارگیرند‌ه‌ی کامپوننت، استفاده کرد:
              @page "/pets"
              <h1>Pets</h1>
              <TableTemplate Items="pets">
                  <TableHeader>
                      <th>ID</th>
                      <th>Name</th>
                  </TableHeader>
                  <RowTemplate>
                      <td>@context.PetId</td>
                      <td>@context.Name</td>
                  </RowTemplate>
              </TableTemplate>
              
              @code {
                  private List<Pet> pets = new()
                  {
                      new Pet { PetId = 2, Name = "Mr. Bigglesworth" },
                      new Pet { PetId = 4, Name = "Salem Saberhagen" },
                      new Pet { PetId = 7, Name = "K-9" }
                  };
              
                  private class Pet
                  {
                      public int PetId { get; set; }
                      public string Name { get; set; }
                  }
              }
              - در اینجا روش بکارگیری کامپوننت TableTemplate را در کامپوننتی دیگر مشاهده می‌کنید. توسط فرگمنت TableHeader، قالب ثابت هدر این جدول تامین شده‌است. توسط فرگمنت RowTemplate قالب پویای ردیف‌های جدول ارائه شده‌است.
              - همچنین در اینجا پارامتر ویژه‌ای به نام context@ را نیز مشاهده می‌کنید. این پارامتر همان شیء item ای است که در حین رندر هر ردیف جدول، به فرگمنت RowTemplate ارسال می‌شود. به این ترتیب کامپوننت والد می‌تواند از اطلاعات در حال رندر توسط کامپوننت فرگمنت دار، مطلع شود و از آن استفاده کند. در این مثال، نوع context@، از نوع کلاس Pet است که سعی شده بر اساس نوع پارامتر Items ارسالی به آن، توسط کامپایلر تشخیص داده شود. حتی می‌توان این نوع را به صورت صریحی نیز مشخص کرد:
              <TableTemplate Items="pets" TItem="Pet">
              این مورد زمانی مفید است که چندین پارامتر جنریک را در کامپوننت تعریف کرده باشیم:
              <SomeGenericComponent TParam1="Person" TParam2="Supplier" TItem=etc/>
              و یا می‌توان نام پارامتر ویژه‌ی context@ را در تمام RenderFragmentهای جنریک، با ذکر ویژگی Context برای مثال به pet تغییر داد:
              <TableTemplate Items="pets" Context="pet">
                  <TableHeader>
                      <th>ID</th>
                      <th>Name</th>
                  </TableHeader>
                  <RowTemplate>
                      <td>@pet.PetId</td>
                      <td>@pet.Name</td>
                  </RowTemplate>
              </TableTemplate>
              و یا حتی می‌توان این نام را به صورت اختصاصی به ازای هر RenderFragment جنریک، مشخص کرد. برای اینکار ویژگی Context را دقیقا بر روی RenderFragment مدنظر قرار می‌دهیم:
              <TableTemplate Items="pets">
                  <TableHeader>
                      <th>ID</th>
                      <th>Name</th>
                  </TableHeader>
                  <RowTemplate Context="pet">
                      <td>@pet.PetId</td>
                      <td>@pet.Name</td>
                  </RowTemplate>
              </TableTemplate>
              این تغییرنام بهتر است زمانی صورت گیرد که نام پیش‌فرض context، با نام مشابه دیگری در کامپوننت جاری، تداخل پیدا کرده‌است.
              نظرات مطالب
              ASP.NET MVC #18
              - در این تنظیمات فرض بر این است که پوشه content محل قرارگیری فایل‌های css است و همچنین این پوشه در ریشه سایت قرار دارد.
              - در MVC نیازی به استفاده از تنظیم authorization -> deny users در وب کانفیگ نیست. روش مرجح استفاده از فیلتر Authorize است که توضیح داده شد.
               
              - و بهتر است فایل‌های css و js از سیستم مسیریابی MVC حذف شوند:

              // in RegisterRoutes -> Global.asax.cs
              routes.IgnoreRoute("{*js}", new { js = @".*\.js(/.*)?"});
              routes.IgnoreRoute("{*css}", new { css = @".*\.css(/.*)?"});
              اشتراک‌ها
              نرم افزارهای selfContained در net core.

              There's two ways to deploy a .NET Core application. There's FDD and SCD. Since TLAs (three letter acronyms) are stupid, that's Framework-dependent and Self-contained. When .NET Core is installed it ends up in C:\program files\dotnet on Windows, for example. In the "Shared" folder there's a bunch of .NET stuff that is, well, shared. There may be multiple folders, as you can see in my folder below. You can have many and multiple installs of .NET Core.  

              نرم افزارهای selfContained در net core.