بازخوردهای دوره
تزریق وابستگی‌های AutoMapper در لایه سرویس برنامه
با سلام
ورژن AutoMapper 4.2.0.0  را از Nuget دریافت کرده ام اما بخش‌های زیر موجود نیست و یا به شکل دیگر تغییر کرده است:
 For<ConfigurationStore>().Singleton().Use<ConfigurationStore>()
                .Ctor<IEnumerable<IObjectMapper>>().Is(MapperRegistry.Mappers);

            For<IConfigurationProvider>().Use(ctx => ctx.GetInstance<ConfigurationStore>());

            For<IConfiguration>().Use(ctx => ctx.GetInstance<ConfigurationStore>());

            For<ITypeMapFactory>().Use<TypeMapFactory>();

            For<IMappingEngine>().Singleton().Use<MappingEngine>().SelectConstructor(() => new MappingEngine(null));

            this.Scan(scanner =>
            {
                scanner.AssembliesFromApplicationBaseDirectory();

                scanner.ConnectImplementationsToTypesClosing(typeof(ITypeConverter<,>))
                       .OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped());

                scanner.ConnectImplementationsToTypesClosing(typeof(ValueResolver<,>))
                    .OnAddedPluginTypes(t => t.HybridHttpOrThreadLocalScoped());
            });
تمامی بخش‌ها را خطا می‌گیرد؟ باید از ورژن 3 استفاده کنم یا می‌شود از ورژن 4 هم استفاده کرد. ممنون.
نظرات مطالب
خودکار کردن تعاریف DbSetها در EF Code first
الف)
- تمام مدل‌های شما باید از کلاس مشخصی مثلا BaseEntity مشتق شوند.
- یا در تنظیمات برنامه مشخص کنید در حین Reflection چه فضای نامی باید جستجو شود.
- و یا مثلا مانند ASP.NET MVC اگر کلاسی نامش به عبارت خاصی ختم شد و همچنین از کلاس پایه خاصی نیز مشتق شده بود آنگاه بررسی شود.
ب)
- مرحله بعد اندکی ویرایش متد loadEntities است جهت خواندن مدل‌های واقع شده مثلا در یک فضای نام خاص یا مشتق شده از یک کلاس پایه خاص و سپس افزودن خودکار آن‌ها به modelBuilder همانند چیزی که در مثال فوق پیاده سازی شده.
در مثال بالا فقط یک اسمبلی جستجو می‌شود؛ اگر نیاز است تمام اسمبلی‌های بارگذاری شده در برنامه جستجو شوند، روش کار مراجعه به AppDomain است:
foreach (Assembly currentassembly in AppDomain.CurrentDomain.GetAssemblies()) 
{
   Type t = currentassembly.GetType("typeName", false, true);
    if (t != null) {return currentassembly.FullName;}
}
نظرات مطالب
نحوه ایجاد Sequence و استفاده آن در Sql Server 2012
سلام
مشکلی بر IDENTITY وارد هست بوجود آمدن Gap بین داده هاست مثلا با حذف پی در پی سطرهای جدول بین مقادیر تولید شده Gap بوجود می‌آید ولی ما این را نمیخواهیم، نیاز داریم که همیشه مقادیر identity پشت سر هم و متوالی باشند یعنی با حذف یک سطر میانی تمام مقادیر شیفت پیدا کنند. و برای رفع این مساله هیچ راهی وجود ندارد. حتی اگر بخواهیم با تابع row_number مقادیر IDENTITY را بعد متوالی کردن بروز رسانی کنیم باز دستور set insert_identity table_name on این اجازه را نمی‌دهد یعنی این دستور

set insert_identity tbl on
update t
set id = new_id
from (select new_id = row_number() over(order by id), *) t
set insert_identity tbl off
در کتابی خواندم که این مشکل با sequence حل میشود.
میخوام بدانم چگونه؟


نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 3 - انتقال مهاجرت‌ها به یک اسمبلی دیگر
ایجاد نام جداول به صورت جمع (Pluralized) و داینامیک :
اگه از روش خودکار کردن تعاریف DbSet ها استفاده کرده باشیم چون دیگر در داخل کلاس Context برنامه، Dbset تعریف نمی‌شود معمولا برای نام گذاری جداول که به صورت جمع باشند از اتریبیوت Table استفاده می‌کنیم.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

[Table("Users")]
public class User : BaseEntity
{
    public long Id { get; set; }

    [Required]
    public string FullName { get; set; } = null!;
}
برای اینکه نام جداول به صورت خودکار به صورت جمع ایجاد شوند میتوان از این متد استفاده کرد:
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;

namespace ProjectName.Common.EfHelpers;

public static class EfToolkit
{
    /// <summary>
    /// ایجاد نام موجودیت‌ها
    /// نام موجودیت اگر اتریبیوت تیبل داشته باشد
    /// از همان نام استفاده میشود و اگر نداشته باشد
    /// نامش جمع بسته میشود
    /// </summary>
    /// <param name="builder"></param>
    public static void MakeTableNamesPluralized(this ModelBuilder builder)
    {
        var entityTypes = builder.Model.GetEntityTypes();

        foreach (var entityType in entityTypes)
        {
            // Get the CLR type of the entity
            var entityClrType = entityType.ClrType;
            var hasTableAttribute = entityClrType.GetCustomAttribute<TableAttribute>();

            // Apply the pluralized table name for the entity
            if (hasTableAttribute is null)
            {
                // Get the pluralized table name
                var pluralizedTableName = GetPluralizedTableName(entityClrType);
                builder.Entity(entityClrType).ToTable(pluralizedTableName);
            }
        }
    }

    /// <summary>
    /// گرفتن نام تایپ و عوض کردن نام آن از مفرد به جمع
    /// Singular to plural
    /// Category => Categories
    /// Box => Boxes
    /// Bus => Buses
    /// Computer => Computers
    /// </summary>
    /// <param name="entityClrType"></param>
    /// <returns></returns>
    private static string GetPluralizedTableName(Type entityClrType)
    {
        // Example implementation (Note: This is a simple pluralization logic and might not cover all cases)
        var typeName = entityClrType.Name;
        if (typeName.EndsWith("y"))
        {
            // Substring(0, typeName.Length - 1)
            // Range indexer
            var typeNameWithoutY = typeName[..^1];
            return typeNameWithoutY + "ies";
        }

        if (typeName.EndsWith("s") || typeName.EndsWith("x"))
        {
            return typeName + "es";
        }
        return typeName + "s";
    }
}
نحوه فراخوانی در متد OnModelCreating:
protected override void OnModelCreating(ModelBuilder builder)
{
    // it should be placed here, otherwise it will rewrite the following settings!
    base.OnModelCreating(builder);
    builder.RegisterAllEntities(typeof(BaseEntity));
    builder.MakeTableNamesPluralized();
    builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
}
هر موجودیتی که اتریبیوت Table داشته باشد از همان نام برای جدول استفاده می‌شود در غیر اینصورت نام انتیتی به صورت جمع ایجاد می‌گردد.
قبل از اینکه نام جداول نیز جمع بسته شود تمامی موجودیت‌ها به صورت خودکار اضافه می‌شوند.
public static class EfToolkit
{
    /// <summary>
    /// ثبت تمامی انتیتی‌ها
    /// <param name="builder"></param>
    /// <param name="type"></param>
    /// </summary>
    public static void RegisterAllEntities(this ModelBuilder builder, Type type)
    {
        var entities = type.Assembly.GetTypes()
            .Where(x => x.BaseType == type);
        foreach (var entity in entities)
            builder.Entity(entity);
    }
}  
هر کلاسی در لایه موجودیت‌ها از BaseEntity ارث بری کرده باشد به عنوان یک Entity شناخته می‌شود.
مطالب
ذخیره تنظیمات متغیر مربوط به یک وب اپلیکیشن ASP.NET MVC با استفاده از EF
طی این  مقاله، نحوه‌ی ذخیره سازی تنظیمات متغیر و پویای یک برنامه را به صورت Strongly Typed ارائه خواهم داد. برای این منظور، یک API را که از Lazy Loading ، Cache ، Reflection و Entity Framework بهره میگیرد، خواهیم ساخت.
برنامه‌ی هدف ما که از این API استفاده می‌کند، یک اپلیکیشن Asp.net MVC است. قبل از شروع به ساخت API مورد نظر، یک دید کلی در مورد آنچه که قرار است در نهایت توسعه یابد، در زیر مشاهده میکنید:
public SettingsController(ISettings settings)
{
  // example of saving 
  _settings.General.SiteName = "دات نت تیپس";
  _settings.Seo.HomeMetaTitle = ".Net Tips";
  _settings.Seo.HomeMetaKeywords = "َAsp.net MVC,Entity Framework,Reflection";
  _settings.Seo.HomeMetaDescription = "ذخیره تنظیمات برنامه";
  _settings.Save();
}

همانطور که در کدهای بالا مشاهده میکنید، شی setting_ ما دارای دو پراپرتی فقط خواندنی بنام‌های General و Seo است که شامل  تنظیمات مورد نظر ما هستند و این دو کلاس از کلاس پایه‌ی SettingBase ارث بری کرده‌اند. دو دلیل برای انجام این کار وجود دارد:
  1. تنظیمات به صورت گروه بندی شده در کنار  هم قرار گرفته‌اند و یافتن تنظیمات برای زمانی که نیاز به دسترسی  به آنها داریم، راحت‌تر و ساده‌تر خواهد بود. 
  2. به این شکل تنظیمات قابل دسترس در یک گروه، از دیتابیس بازیابی خواهند شد.

اصلا چرا باید این تنظیمات را در دیتابیس ذخیره کنیم؟ 

شاید فکر کنید چرا باید تنظیمات را در دیتابیس ذخیره کنیم در حالی که فایل web.config در درسترس است و می‌توان توسط کلاس ConfigurationManager به اطلاعات آن دسترسی داشت.
جواب: دلیل این است که با تغییر فایل web.config، برنامه‌ی وب شما ری استارت خواهد شد (چه زمان‌هایی یک برنامه Asp.net ری استارت میشود).
برای جلوگیری از این مساله، راه حل مناسب برای ذخیره سازی اطلاعاتی که نیاز به تغییر در زمان اجرا دارند، استفاده از از دیتابیس می‌باشد. در این مقاله از Entity Framework و پایگاه داده Sql Sever استفاده می‌کنم.

مراحل ساخت Setting API مورد نظر به شرح زیر است:
  1. ساخت یک Asp.net Web Application 
  2. ساخت مدل Setting و افزودن آن به کانتکست Entity Framework 
  3. ساخت کلاس SettingBase برای بازیابی و ذخیره سازی تنظیمات با رفلکشن
  4. ساخت کلاس GenralSettins و SeoSettings که از کلاس SettingBase ارث بری کرده‌اند.
  5. ساخت کلاس Settings به منظور مدیریت تمام انواع تنظیمات 

یک برنامه‌ی Asp.Net Web Application را از نوع MVC ایجاد کنید. تا اینجا مرحله‌ی اول ما به پایان رسید؛ چرا که ویژوال استودیو کار‌های مورد نیاز ما را انجام خواهد داد.
 لازم است مدل خود را به ApplicationDbContext موجود در فایل IdentityModels.cs معرفی کنیم. به شکل زیر:
namespace DynamicSettingAPI.Models
{
    public interface IUnitOfWork
    {
        DbSet<Setting> Settings { get; set; }
        int SaveChanges();
    }
} 

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>,IUnitOfWork
    {
        public DbSet<Setting> Settings { get; set; }
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }

        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }


namespace DynamicSettingAPI.Models
{
    public class Setting
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public string Value { get; set; }
    }
}
مدل تنظیمات ما خیلی ساده است و دارای سه پراپرتی به نام‌های Name ، Type ، Value هست که به ترتیب برای دریافت مقدار تنظیمات، نام کلاسی که از کلاس SettingBase ارث برده و نام تنظیمی که لازم داریم ذخیره کنیم، در نظر گرفته شده‌اند. 
لازم است تا متد OnModelCreating مربوط به ApplicationDbContext را نیز تحریف کنیم تا کانفیگ مربوط به مدل خود را نیز اعمال نمائیم.
 protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Setting>()
                    .HasKey(x => new { x.Name, x.Type });

            modelBuilder.Entity<Setting>()
                        .Property(x => x.Value)
                        .IsOptional();

            base.OnModelCreating(modelBuilder);
        }
ساختاری به شکل زیر مد نظر ماست:

  کلاس SettingBase ما همچین ساختاری را خواهد داشت:
namespace DynamicSettingAPI.Service
{
    public abstract class SettingsBase
    {
        //1
        private readonly string _name;
        private readonly PropertyInfo[] _properties;

        protected SettingsBase()
        {
            //2
            var type = GetType();
            _name = type.Name;
            _properties = type.GetProperties();
        }

        public virtual void Load(IUnitOfWork unitOfWork)
        {
            //3 get setting for this type name
            var settings = unitOfWork.Settings.Where(w => w.Type == _name).ToList();

            foreach (var propertyInfo in _properties)
            {
                //get the setting from setting list
                var setting = settings.SingleOrDefault(s => s.Name == propertyInfo.Name);
                if (setting != null)
                {
                    //4 set 
                    propertyInfo.SetValue(this, Convert.ChangeType(setting.Value, propertyInfo.PropertyType));
                }
            }
        }
        public virtual void Save(IUnitOfWork unitOfWork)
        {
            //5 get all setting for this type name
            var settings = unitOfWork.Settings.Where(w => w.Type == _name).ToList();

            foreach (var propertyInfo in _properties)
            {
                var propertyValue = propertyInfo.GetValue(this, null);
                var value = (propertyValue == null) ? null : propertyValue.ToString();

                var setting = settings.SingleOrDefault(s => s.Name == propertyInfo.Name);
                if (setting != null)
                {
                    // 6 update existing value
                    setting.Value = value;
                }
                else
                {
                    // 7 create new setting
                    var newSetting = new Setting()
                    {
                        Name = propertyInfo.Name,
                        Type = _name,
                        Value = value,
                    };
                    unitOfWork.Settings.Add(newSetting);
                }
            }
        }
    }
}
این کلاس قرار است توسط کلاس‌های تنظیمات ما به ارث برده شود و در واقع کارهای مربوط به رفلکشن را در این کلاس کپسوله کرده‌ایم. همانطور که مشخص است ما دو فیلد را به نام‌های name_ و properties_ به صورت فقط خواندنی در نظر گرفته ایم که نام کلاس مورد نظر ما که از این کلاس به ارث خواهد برد، به همراه پراپرتی‌های آن، در این ظرف‌ها قرار خواهند گرفت.
متد Load وظیفه‌ی واکشی تمام تنظیمات مربوط به Type و ست کردن مقادیر به دست آمده را به خصوصیات کلاس ما، برعهده دارد. کد زیر مقدار دریافتی از دیتابیس را به نوع داده پراپرتی مورد نظر تبدیل کرده و نتیجه را به عنوان Value پراپرتی ست میکند. 
propertyInfo.SetValue(this, Convert.ChangeType(setting.Value, propertyInfo.PropertyType));
متد Save نیز وظیفه‌ی ذخیره سازی مقادیر موجود در خصوصیات کلاس تنظیماتی را که از کلاس SettingBase ما به ارث برده است، به عهده دارد. 
این متد دیتا‌های موجود دردیتابیس را که متعلق به کلاس ارث برده مورد نظر ما هستند، واکشی میکند و در یک حلقه، اگر خصوصیتی در دیتابیس موجود بود، آن را ویرایش کرده وگرنه یک رکورد جدید را ثبت میکند.

  کلاس‌های تنظیمات شخصی سازی شده خود را به شکل زیر تعریف میکنیم :
  public class GeneralSettings : SettingsBase
    {
        public string SiteName { get; set; }
        public string AdminEmail { get; set; }
        public bool RegisterUsersEnabled { get; set; }
    }

 public class GeneralSettings : SettingsBase
    {
        public string SiteName { get; set; }
        public string AdminEmail { get; set; }
    }
نیازی به توضیح ندارد.
برای اینکه تنظیمات را به صورت یکجا داشته باشیم و Abstraction ای را برای استفاده از این API ارائه دهیم، یک اینترفیس و یک کلاس که اینترفیس مذکور را پیاده کرده است در نظر میگیریم: 
public interface ISettings
{
    GeneralSettings General { get; }
    SeoSettings Seo { get; }
    void Save();
}

public class Settings : ISettings
{
    // 1
    private readonly Lazy<GeneralSettings> _generalSettings;
    // 2
    public GeneralSettings General { get { return _generalSettings.Value; } }

    private readonly Lazy<SeoSettings> _seoSettings;
    public SeoSettings Seo { get { return _seoSettings.Value; } }

    private readonly IUnitOfWork _unitOfWork;
    public Settings(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        // 3
        _generalSettings = new Lazy<GeneralSettings>(CreateSettings<GeneralSettings>);
        _seoSettings = new Lazy<SeoSettings>(CreateSettings<SeoSettings>);
    }

    public void Save()
    {
        // only save changes to settings that have been loaded
        if (_generalSettings.IsValueCreated)
            _generalSettings.Value.Save(_unitOfWork);

        if (_seoSettings.IsValueCreated)
            _seoSettings.Value.Save(_unitOfWork);

        _unitOfWork.SaveChanges();
    }
    // 4
    private T CreateSettings<T>() where T : SettingsBase, new()
    {
        var settings = new T();
        settings.Load(_unitOfWork);
        return settings;
    }
}
این اینترفیس مشخص می‌کند که ما به چه نوع تنظیماتی، دسترسی داریم و متد Save آن برای آپدیت کردن تنظیمات، در نظر گرفته شده است. هر کلاسی که از کلاس SettingBase ارث بری کرده را به صورت فیلد فقط خواندنی و با استفاده از کلاس Lazy درون آن ذکر میکنیم و به این صورت کلاس تنظیمات ما زمانی ساخته خواهد شد که برای اولین بار به آن دسترسی داشته باشیم.
متد CreateSetting وظیفه‌ی لود دیتا را از دیتابیس، بر عهده دارد که برای این منظور، متد لود Type مورد نظر را فراخوانی میکند. این متد وقتی به کلاس تنظیمات مورد نظر برای اولین بار دسترسی پیدا کنیم، فراخوانی خواهد شد.

 حتما امکان این وجود دارد که شما از امکان Caching هم بهره ببرید برای مثال همچین متد و سازنده‌ای را در کلاس Settings در نظر بگیرید:
private readonly ICache _cache;
public Settings(IUnitOfWork unitOfWork, ICache cache)
{
    // ARGUMENT CHECKING SKIPPED FOR BREVITY
    _unitOfWork = unitOfWork;
    _cache = cache;
    _generalSettings = new Lazy<GeneralSettings>(CreateSettingsWithCache<GeneralSettings>);
    _seoSettings = new Lazy<SeoSettings>(CreateSettingsWithCache<SeoSettings>);
}

private T CreateSettingsWithCache<T>() where T : SettingsBase, new()
{
    // this is where you would implement loading from ICache
    throw new NotImplementedException();
}
در آخر هم به شکل زیر میتوان (به عنوان دمو فقط ) از این API استفاده کرد.
   public ActionResult Index()
        {
            using (var uow = new ApplicationDbContext())
            {
                var _settings = new Settings(uow);
                _settings.General.SiteName = "دات نت تیپس";
                _settings.General.AdminEmail = "admin@gmail.com";
                _settings.General.RegisterUsersEnabled = true;
                _settings.Seo.HomeMetaTitle = ".Net Tips";
                _settings.Seo.MetaKeywords = "Asp.net MVC,Entity Framework,Reflection";
                _settings.Seo.HomeMetaDescription = "ذخیره تنظیمات برنامه";

                var settings2 = new Settings(uow);
                var output = string.Format("SiteName: {0} HomeMetaDescription: {1}  MetaKeywords:  {2}  MetaTitle:  {3}  RegisterEnable:  {4}",
                    settings2.General.SiteName,
                    settings2.Seo.HomeMetaDescription,
                    settings2.Seo.MetaKeywords,
                    settings2.Seo.HomeMetaTitle,
                    settings2.General.RegisterUsersEnabled.ToString()
                    );
                return Content(output);
            }

        }

خروجی :

نکته: در پروژه ای که جدیدا در سایت ارائه داده‌ام و در حال تکمیل آن هستم، از بهبود یافته‌ی این مقاله استفاده می‌شود. حتی برای اسلاید شو‌های سایت هم میشود از این روش استفاده کرد و از فرمت json بهره برد برای این منظور. حتما در پروژه‌ی مذکور همچین امکانی را هم در نظر خواهم گرفتم.
پیشنها میکنم سورس SmartStore را بررسی کنید. آن هم به شکل مشابهی ولی پیشرفته‌تر از این مقاله، همچین امکانی را دارد.
مطالب
به روز رسانی فیلدهای XML در SQL Server

از SQL server 2005 به بعد، پشتیبانی کاملی از XML توسط این محصول صورت می‌گیرد. در ادامه مروری خواهیم داشت بر نحوه‌ی به روز رسانی مقادیر فیلدهایی از نوع XML در SQL Server .
در ابتدا جدول موقتی زیر را که شامل یک رکورد از نوع XML است، در نظر بگیرید:

DECLARE @tblTest AS TABLE (xmlField XML)

INSERT INTO @tblTest
(
xmlField
)
VALUES
(
'<Sample>
<Node1>Value1</Node1>
<Node2>Value2</Node2>
<Node3>OldValue</Node3>
</Sample>'
)
می‌خواهیم OldValue را به مقداری دیگر تغییر دهیم.

سعی اول:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'
UPDATE @tblTest
SET xmlField.modify('replace value of (/Sample/Node3)[1] with ' + @newValue)
این سعی با خطای زیر متوقف می‌شود:

The argument 1 of the XML data type method "modify" must be a string literal.
بنابراین از روش String concatenation برای معرفی مقدار متغیر مورد نظر در اینجا نمی‌شود استفاده کرد.

سعی دوم:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3)[1] with sql:variable("@newValue")'
)
روش معرفی صحیح یک متغیر را در اینجا می‌توان مشاهده کرد. اما این سعی نیز با خطای زیر متوقف می‌شود:

XQuery [@tblTest.xmlField.modify()]: The target of 'replace value of' must be a non-metadata attribute or an element with simple typed content, found 'element(NodeThree,xdt:untyped) ?'

سعی سوم:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

و بله. کار می‌کنه!
XML ایی را که در ابتدا استفاده کردیم از نوع un-typed XML محسوب شده و هیچ schema ایی را برای آن در نظر نگرفته‌ایم، به همین جهت باید دقیقا مشخص کنیم که قصد داریم text این node را ویرایش نمائیم.

مشکل بعدی!
در ابتدا مثال زیر را در نظر بگیرید:

DECLARE @tblTest AS TABLE (xmlField XML)

INSERT INTO @tblTest
(
xmlField
)
VALUES
(
'<Sample>
<Node1>Value1</Node1>
<Node2>Value2</Node2>
<Node3></Node3>
</Sample>'
)

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

این عبارات T-SQL ، خلاصه بحث ما تا به اینجا هستند اما با یک تفاوت. نود 3 در اینجا خالی است.
اگر اسکریپت را اجرا کنید، هیچ تغییری را مشاهده نخواهید کرد. به عبارت دیگر به روز رسانی صورت نمی‌گیرد. در اینجا چون text این نود خالی است ، فرض SQL Server بر این خواهد بود که وجود ندارد، بنابراین این نود را به روز رسانی نخواهد کرد. به همین منظور باید برای به روز رسانی این نود، عبارت جدید را در جایی که text ندارد insert‌ کرد (و نه replace).

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

UPDATE @tblTest
SET xmlField.modify(
'insert text{sql:variable("@newValue")} into
(/Sample/Node3)[1] [not(text())]'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

مطالب
پیاده سازی پروژه‌های React با TypeScript - قسمت سوم - تعریف نوع رویدادها و children props
در قسمت قبل، نوع توابع ارسالی از طریق props را تعیین کردیم. فرض کنید در همان مثال می‌خواهیم بجای ارسال یک رشته به فراخوان کامپوننت تعریف شده، اصل رخ‌داد واقع شده را ارسال کنیم. به همین جهت onClick دریافتی را مستقیما به رخ‌داد onClick، نسبت می‌دهیم:
export const Button = ({ onClick }: Props) => {
  return <button onClick={onClick}>Click Me</button>;
};
در این حالت بلافاصله با خطای زیر مواجه خواهیم شد که عنوان می‌کند امضای تابع onClick، با امضای رویداد مدنظر یکی نیست:


برای رفع این مشکل، می‌توان رخ‌داد کلیک ماوس بر روی یک دکمه را از نوع بسیار عمومی React.MouseEvent تعریف کرد:
import React from "react";

type Props = {
  onClick: (e: React.MouseEvent) => void;
};

export const Button = ({ onClick }: Props) => {
  return <button onClick={onClick}>Click Me</button>;
};
پس از آن می‌توان تعریف المان کامپوننت Button را در فایل src\App.tsx به صورت زیر تغییر داد؛ چون از دیدگاه تایپ‌اسکریپت، اکنون نوع e به صورت <e: React.MouseEvent<Element, MouseEvent تعریف شده‌است و به تمام خواص و متدهای MouseEvent، با کمک intellisense کامل مرتبط با آن‌ها، دسترسی وجود دارد:
<Button
  onClick={(e) => {
    e.preventDefault();
    console.log(e);
  }}
/>


محدود کردن دامنه‌ی رویدادها


زمانیکه نوع یک رویداد به صورت کلی e: React.MouseEvent تعریف می‌شود، اگر به تصویر فوق دقت کنیم، تایپ‌اسکریپت آن‌را قابل اعمال به هر نوع Element ای می‌داند. اما اگر بخواهیم دامنه‌ی دید آن‌را صرفا به المان استاندارد button تعریف شده محدود کنیم، اشاره‌گر ماوس را در فایل src\components\Button.tsx بر روی رویداد onClick المان button تعریف شده قرار می‌دهیم:


همانطور که مشخص است، نوع این المان را به صورت HTMLButtonElement ذکر کرده‌است. بنابراین می‌توان تعریف کلی قبلی را به صورت زیر محدود کرد:
import React from "react";

type Props = {
  onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
};

export const Button = ({ onClick }: Props) => {
  return <button onClick={onClick}>Click Me</button>;
};
در اینجا توسط generics، نوع React.MouseEvent به HTMLButtonElement محدود شده‌است و دیگر اینبار بازه‌ی دید آن یک Element کلی نیست. این تعریف عنوان می‌کند که رخ‌داد واقع شده، فقط از یک button صادر شده‌است و نه از المان نوع دیگری. به این ترتیب در حین استفاده‌ی از رخ‌داد رسیده در کامپوننت App در برگیرنده‌ی آن، به متدها و رخ‌دادهای ویژه‌تری نیز دسترسی خواهیم یافت.


تعیین نوع رخ‌داد onChange

در ادامه فرض کنید می‌خواهیم اطلاعات رویداد onChange را نیز صادر کنیم. روش عمومی تشخیص نوع آن، قرار دادن اشاره‌گر ماوس بر روی رویداد مدنظر و سپس استفاده از همان نوعی است که نمایش می‌دهد؛ مانند تصویر زیر:


import React from "react";

type Props = {
  onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
  onChange?: (e: React.FormEvent<HTMLInputElement>) => void;
};

export const Button = ({ onClick, onChange }: Props) => {
  return (
    <>
      <input onChange={onChange} />
      <button onClick={onClick}>Click Me</button>
    </>
  );
};
در اینجا یا می‌توان از رویداد اختصاصی React.ChangeEvent از نوع HTMLInputElement، استفاده کرد و یا از حالت عمومی‌تر React.FormEvent.


سؤال: اطلاعات نمایش داده شده‌ی نوع‌های متناظر با رویدادهای مختلف در تصاویر فوق، از کجا تامین می‌شوند؟

اگر به فایل package.json پروژه‌ی تایپ‌اسکریپتی ایجاد شده مراجعه کنید، می‌توانید حداقل دو مدخل جدید زیر را نیز در آن مشاهده کنید:
{ 
  "dependencies": {
    "@types/react": "^16.9.35",
    "@types/react-dom": "^16.9.8",
  },
این بسته‌های جدید، حاوی تعاریف تمام نوع‌های تایپ‌اسکریپتی مرتبط با اشیاء یک پروژه‌ی React هستند. برای مثال در فایل src\components\Button.tsx، اشاره‌گر ماوس را بر روی MouseEvent تعریف شده قرار دهید و سپس دکمه‌ی ctrl را نگه دارید. مشاهده خواهید کرد که تعریف این نوع، تبدیل به یک لینک قابل کلیک خواهد شد. اگر بر روی آن کلیک کنید، به فایل node_modules\@types\react\index.d.ts پروژه‌ی خود هدایت می‌شوید که دقیقا محل تعریف این رویداد و موارد مشابه است.


سؤال: آیا بسته‌های @types دار موجود در فایل package.json، حجم فایل‌های نهایی برنامه را افزایش می‌دهند؟ آیا برنامه‌های React مبتنی بر TypeScript، حجم بیشتری را نسبت به نمونه‌ها‌ی ES6 آن دارند؟

اکثر این تعاریف @types به صورت اینترفیس‌های تایپ‌اسکریپتی تعریف شده‌اند که صرفا در زمان کامپایل برنامه، کمک حال کامپایلر آن خواهند بود و پس از کامپایل، تبخیر شده و در کدهای نهایی تولیدی، حضور نخواهند داشت. به عبارتی استفاده‌ی از تایپ‌اسکریپت، حجم نهایی پروژه‌ها را افزایش نمی‌دهد؛ چون اینترفیس‌های تایپ‌اسکریپت، معادلی را در جاوااسکریپت استاندارد ندارند و فقط به عنوان راهنمای کامپایلر تایپ‌اسکریپت عمل می‌کنند.


یک نکته: تعریف رویدادهای مدنظر را می‌توان در importها نیز قرار داد:
import React, { FormEvent } from "react";

type Props = {
  onChange?: (e: FormEvent<HTMLInputElement>) => void;
};
که در این حالت دیگر نیازی به ذکر کامل React.FormEvent نیست. اما در کل، روش تعریف React.xyz، مرسوم‌تر است.


تعریف نوع Children Props

زمانیکه داخل تعریف المان یک کامپوننت، کامپوننت دیگری ذکر می‌شود، به عنوان فرزند او در React درنظر گرفته می‌شود. برای نمونه تعریف کامپوننت Button را در فایل src\App.tsx به صورت زیر تغییر می‌دهیم تا فرزندی را به صورت «Hello world» بپذیرد:
<Button
  onClick={(e) => {
    e.preventDefault();
    console.log(e);
  }}
>
  Hello world!
</Button>
 اکنون سؤال اینجا است که چگونه می‌توان نوع آن‌را توسط تایپ‌اسکریپت مشخص کرد؟
با توجه به اینکه این فرزند، از نوع رشته‌ای است، فقط کافی است خاصیت children را با همین نوع، به Props اضافه کرده و از آن استفاده کنیم:
import React, { FormEvent } from "react";

type Props = {
  onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
  onChange?: (e: FormEvent<HTMLInputElement>) => void;
  children: string;
};

export const Button = ({ onClick, onChange, children }: Props) => {
  return (
    <>
      <input onChange={onChange} />
      <button onClick={onClick}>{children}</button>
    </>
  );
};
در ادامه، مثال فوق را اندکی مشکل‌تر می‌کنیم. فرض کنید اینبار بجای یک رشته، یک المان image، به عنوان فرزند کامپوننت دکمه معرفی شود:
<Button
  onClick={(e) => {
    e.preventDefault();
    console.log(e);
  }}
>
  <img src={logo} className="App-logo" alt="logo" />
</Button>
که سبب بروز خطای زیر می‌شود:


عنوان می‌کند که با این تغییر، نوع children به Element تغییر یافته‌است؛ اما در تعریف Props آن، به صورت رشته‌ای معرفی شده‌است. در این حالت اگر به تعریف Props مراجعه کنیم و نوع string را به Element تغییر دهیم، باز هم این خطا برطرف نمی‌شود. حتی اگر children: HTMLImageElement را نیز اضافه کنیم، باز هم این خطا تغییری نمی‌کند.
روش صحیح حل این مشکل را در ادامه مشاهده می‌کنید:
import React, { FormEvent } from "react";

type Props = {
  onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
  onChange?: (e: FormEvent<HTMLInputElement>) => void;
  // children: HTMLImageElement;
};

export const Button: React.FC<Props> = ({ onClick, onChange, children }) => {
  return (
    <>
      <input onChange={onChange} />
      <button onClick={onClick}>{children}</button>
    </>
  );
};
- ابتدا خاصیت children را از تعریف نوع Props حذف می‌کنیم.
- سپس ذکر نوع Props را هم از Object Destructuring صورت گرفته، حذف می‌کنیم.
- در آخر، نوع کامپوننت جاری را به صورت <React.FC<Props معرفی می‌کنیم. در اینجا FC یعنی Functional Component و با تعریف آن، می‌توان نوع props را به صورت آرگومان جنریک آن مشخص کرد. پس از آن دیگر نیازی به ذکر خاصیت children، در Props نیست؛ چون این خاصیت جزئی از React.FC می‌باشد و به صورت خودکار شناسایی می‌شود:



بنابراین در حالت کلی اگر از خاصیت children استفاده نمی‌کنید، از همان syntax قبلی که به صورت export const Button = ({ onClick, onChange }: Props) است، استفاده کنید؛ در غیراینصورت استفاده‌ی از نوع <React.FC<T، ضروری خواهد بود.
نظرات مطالب
Blazor 5x - قسمت هشتم - مبانی Blazor - بخش 5 - تامین محتوای نمایشی کامپوننت‌های فرزند توسط کامپوننت والد
یک نکته‌ی تکمیلی: جایگزین کردن if/elseهای بررسی نال بودن با یک کامپوننت
در کدهای Razor، داشتن چنین قطعه کدی بسیار مرسوم هست:
@if(someValue is null) 
{
  <p>Loading..</p>
} 
else 
{
 // show actual content
}
@code {
  SomeClass someValue;
}
در ابتدای دریافت اطلاعات از سرویسی، مقداری که قرار است رندر شود، نال است و پس از پایان کار بارگذاری اطلاعات، قسمت else اجرا خواهد شد. می‌توان این if/else را توسط یک RenderFragment، تبدیل به کامپوننت Loading.razor با قابلیت استفاده‌ی مجدد کرد:
/* shows a loading gif/text if a value is null */
@if (Value == null)
{
    <div><img src="/img/SmallLoader.gif" alt="loading" /> Loading</div>
}
else
{
    @ChildContent
}
@code {

    [Parameter] public RenderFragment ChildContent { get; set; }

    [Parameter] public object Value { get; set; }
}
در این حالت اگر مقدار در حال بررسی نال بود، یک متن یا تصویر منتظر بمانید نمایش داده می‌شود و اگر خیر، استفاده کننده‌ی از این کامپوننت می‌تواند محتوای بدنه else را تامین کند؛ یک مثال از نحوه‌ی استفاده از کامپوننت فوق و جایگزین کردن if/else نوشته شده‌ی در ابتدای مطلب:
<Loading Value="someValue">
  <p>Content goes here</p>
</Loading>
مطالب
ASP.NET MVC #2

MVC‌ چیست و اساس کار آن چگونه است؟

الگوی MVC در سال‌های اول دهه 70 میلادی در شرکت زیراکس توسط خالقین زبان اسمال‌تاک که جزو اولین زبان‌های شیءگرا محسوب می‌شود، ارائه گردید. نام MVC از الگوی Model-View-Controller گرفته شده و چندین دهه است که در صنعت تولید نرم افزار مورد استفاده می‌باشد. هدف اصلی آن جدا سازی مسئولیت‌های اجزای تشکیل دهنده «لایه نمایشی» برنامه است.
این الگو در سال 2004 برای اولین بار در سکویی به نام Rails به کمک زبان روبی جهت ساخت یک فریم ورک وب MVC مورد استفاده قرار گرفت و پس از آن به سایر سکوها مانند جاوا، دات نت (در سال 2007)، PHP و غیره راه یافت.
تصاویری را از این تاریخچه در ادامه ملاحظه می‌کنید؛ از دکتر Trygve Reenskaug تا شرکت زیراکس و معرفی آن در Rails به عنوان اولین فریم ورک وب MVC.


C در MVC معادل Controller است. کنترلر قسمتی است که کار دریافت ورودی‌های دنیای خارج را به عهده دارد؛ مانند پردازش یک درخواست HTTP ورودی.


زمانیکه کنترلر این درخواست را دریافت می‌کند، کار وهله سازی Model را عهده دار خواهد شد و حاوی اطلاعاتی است که نهایتا در اختیار کاربر قرار خواهد گرفت تا فرآیند پردازش درخواست رسیده را تکمیل نماید. برای مثال اگر کاربری جهت دریافت آخرین اخبار به سایت شما مراجعه کرده است،‌ در اینجا کار تهیه لیست اخبار بر اساس مدل مرتبط به آن صورت خواهد گرفت. بنابراین کنترلرها، پایه اصلی و مدیر ارکستر الگوی MVC محسوب می‌شوند.
در ادامه، کنترلر یک View را جهت نمایش Model انتخاب خواهد کرد. View در الگوی MVC یک شیء ساده است. به آن می‌توان به شکل یک قالب که اطلاعاتی را از Model دریافت نموده و سپس آن‌ها را در مکان‌های مناسبی در صفحه قرار می‌دهد، نگاه کرد.
نتیجه استفاده از این الگو، ایزوله سازی سه جزء یاد شده از یکدیگر است. برای مثال View نمی‌داند و نیازی ندارد که بداند چگونه باید از لایه دسترسی به اطلاعات کوئری بگیرد. یا برای مثال کنترلر نیازی ندارد بداند که چگونه و در کجا باید خطایی را با رنگی مشخص نمایش دهد. به این ترتیب انجام تغییرات در لایه رابط کاربری برنامه در طول توسعه کلی سیستم، ساده‌تر خواهد شد.
همچنین در اینجا باید اشاره کرد که این الگو مشخص نمی‌کند که از چه نوع فناوری دسترسی به اطلاعاتی باید استفاده شود. می‌توان از بانک‌های اطلاعاتی، وب سرویس‌ها، صف‌ها و یا هر نوع دیگری از اطلاعات استفاده کرد. به علاوه در اینجا در مورد نحوه طراحی Model نیز قیدی قرار داده نشده است. این الگو تنها جهت ساخت بهتر و اصولی «رابط کاربری» طراحی شده است و بس.



تفاوت مهم پردازشی ASP.NET MVC با ASP.NET Web forms

اگر پیشتر با ASP.NET Web forms کار کرده باشید اکنون شاید این سؤال برایتان وجود داشته باشد که این سیستم جدید در مقایسه با نمونه قبلی، چگونه درخواست‌ها را پردازش می‌کند.


همانطور که مشاهده می‌کنید، در وب فرم‌ها زمانیکه درخواستی دریافت می‌شود، این درخواست به یک فایل موجود در سیستم مثلا default.aspx ارسال می‌گردد. سپس ASP.NET یک کلاس وهله سازی شده معرف آن صفحه را ایجاد کرده و آن‌را اجرا می‌کند. در اینجا چرخه طول عمر صفحه مانند page_load و غیره رخ خواهد داد. جهت انجام این وهله سازی، View به فایل code behind خود گره خورده است و جدا سازی خاصی بین این دو وجود ندارد. منطق صفحه به markup آن که معادل است با یک فایل فیزیکی بر روی سیستم، کاملا مقید است. در ادامه، این پردازش صورت گرفته و HTML نهایی تولیدی به مرورگر کاربر ارسال خواهد شد.
در ASP.NET MVC این نحوه پردازش تغییر کرده است. در اینجا ابتدا درخواست رسیده به یک کنترلر هدایت می‌شود و این کنترلر چیزی نیست جز یک کلاس مجزا و مستقل از هر نوع فایل ASPX ایی در سیستم. سپس این کنترلر کار پردازش درخواست رسیده را شروع کرده، اطلاعات مورد نیاز را جمع آوری و سپس به View ایی که انتخاب می‌کند، جهت نمایش نهایی ارسال خواهد کرد. در اینجا View این اطلاعات را دریافت کرده و نهایتا در اختیار کاربر قرار خواهد داد.



آشنایی با قرارداد یافتن کنترلرهای مرتبط

تا اینجا دریافتیم که نحوه پردازش درخواست‌ها در ASP.NET MVC بر مبنای کلاس‌ها و متدها است و نه بر مبنای فایل‌های فیزیکی موجود در سیستم. اگر درخواستی به سیستم ارسال می‌شود، در ابتدا، این درخواست جهت پردازش، به یک متد عمومی موجود در یک کلاس کنترلر هدایت خواهد شد و نه به یک فایل فیزیکی ASPX (برخلاف وب فرم‌ها).


همانطور که در تصویر مشاهده می‌کنید، در ابتدای پردازش یک درخواست، آدرسی به سیستم ارسال خواهد شد. بر مبنای این آدرس، نام کنترلر که در اینجا زیر آن خط قرمز کشیده شده است، استخراج می‌گردد (برای مثال در اینجا نام این کنترلرProducts است). سپس فریم ورک به دنبال کلاس این کنترلر خواهد گشت. اگر آن‌را در اسمبلی پروژه بیابد، از آن خواهد خواست تا درخواست رسیده را پردازش کند؛ در غیراینصورت پیغام 404 یا یافت نشد، به کاربر نمایش داده می‌شود.
اما فریم ورک چگونه این کلاس کنترلر درخواستی را پیدا می‌کند؟
در زمان اجرا، اسمبلی اصلی پروژه به همراه تمام اسمبلی‌هایی که به آن ارجاعی دارند جهت یافتن کلاسی با این مشخصات اسکن خواهند شد:
1- این کلاس باید عمومی باشد.
2- این کلاس نباید abstract باشد (تا بتوان آن‌را به صورت خودکار وهله سازی کرد).
3- این کلاس باید اینترفیس استاندارد IController را پیاده سازی کرده باشد.
4- و نام آن باید مختوم به کلمه Controller باشد (همان مبحث Convention over configuration یا کار کردن با یک سری قرار داد از پیش تعیین شده).

برای مثال در اینجا فریم ورک به دنبال کلاسی به نام ProductsController خواهد گشت.
شاید تعدادی از برنامه نویس‌های ASP.NET MVC تصور ‌کنند که فریم ورک در پوشه‌ی استانداردی به نام Controllers به دنبال این کلاس خواهد گشت؛ اما در عمل زمانیکه برنامه کامپایل می‌شود، پوشه‌ای در این اسمبلی وجود نخواهد داشت و همه چیز از طریق Reflection مدیریت خواهد شد.

نظرات مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت دوم - الگوی Service Locator
یک نکته‌ی تکمیلی: طراحی یک کلاس ServiceLocator برای NET Core.

گاهی از اوقات مجبور به کار با کتابخانه‌هایی هستید که برای کار با تزریق وابستگی‌ها طراحی نشده‌اند. برای مثال این کتابخانه‌ها کلاسی را از شما دریافت می‌کنند، این کلاس را خودشان وهله سازی کرده و در نهایت استفاده خواهند کرد. چون وهله سازی این کلاس در اختیار شما نیست و همچنین کتابخانه‌ی فراخوان نیز از تزریق وابستگی‌های در سازنده‌ی کلاس دریافتی، پشتیبانی نمی‌کند، تنها راه حل باقیمانده، استفاده از الگوی Service Locator خواهد بود. برای این منظور می‌توانید از دو کلاس زیر کمک بگیرید:
using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;

namespace Utils
{
    public static class ServiceLocatorProvider
    {
        private static readonly Lazy<IServiceProvider> _serviceProviderBuilder =
            new Lazy<IServiceProvider>(GetServiceProvider, LazyThreadSafetyMode.ExecutionAndPublication);

        /// <summary>
        /// A lazy loaded thread-safe singleton
        /// </summary>
        public static IServiceProvider Current { get; } = _serviceProviderBuilder.Value;

        private static IServiceProvider GetServiceProvider()
        {
            var services = new ServiceCollection();
            ConfigureServices(services);
            return services.BuildServiceProvider();
        }

        private static void ConfigureServices(IServiceCollection services)
        {
            // TODO: add other services here ... services.AddSingleton ....
        }
    }

    public static class ServiceLocator
    {
        public static object GetService(Type serviceType)
        {
            return ServiceLocatorProvider.Current.GetService(serviceType);
        }

        public static TService GetService<TService>()
        {
            return ServiceLocatorProvider.Current.GetService<TService>();
        }

        public static object GetRequiredService(Type serviceType)
        {
            return ServiceLocatorProvider.Current.GetRequiredService(serviceType);
        }

        public static TService GetRequiredService<TService>()
        {
            return ServiceLocatorProvider.Current.GetService<TService>();
        }

        public static void RunScopedService<T, S>(Action<S, T> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();

                callback(context, serviceScope.ServiceProvider.GetRequiredService<T>());
                if (context is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
        }

        public static void RunScopedService<S>(Action<S> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();
                callback(context);
                if (context is IDisposable disposable)
                {
                    disposable.Dispose();
                }
            }
        }

        public static T RunScopedService<T, S>(Func<S, T> callback)
        {
            using (var serviceScope = ServiceLocatorProvider.Current.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = serviceScope.ServiceProvider.GetRequiredService<S>();
                return callback(context);
            }
        }
    }
}
در اینجا باید متد ConfigureServices کلاس ServiceLocatorProvider را همانند قبل تنظیم و تعاریف سرویس‌های مدنظر خود را اضافه کنید. سپس در هر قسمتی از برنامه می‌توانید از متدهایی مانند ()<ServiceLocator.GetRequiredService<TService استفاده نمائید. در مورد متدهای RunScopedService آن در قسمت سوم بیشتر بحث شده‌است.