مطالب
اضافه شدن پشتیبانی از unsigned right shift به C# 11
به C# 11، عملگر جدیدی به شکل <<< و به معنای unsigned right shift اضافه شده‌است که ... در زبان جاوا از نگارش ابتدایی آن حضور داشته‌است. اما ... چرا از این لحاظ بین این دو زبان، تفاوت وجود داشته‌است؟


مفهوم عملگر شیفت در #C

عملگر شیفت به سمت راست و یا <<، عددی را به تعداد بیت مشخص شده (x >> count)، به سمت راست منتقل می‌کند و دو نوع دارد:
الف) شیفت به راست منطقی
برای مثال اگر عدد 12 را به صورت باینری نمایش دهیم، به صورت زیر خواهد بود:
00000000 00000000 00000000 00001100
و اگر آن‌را به اندازه‌ی یک بیت به سمت راست هدایت کنیم، که با 1 <<< 12  نمایش داده می‌شود:
00000000 00000000 00000000 00000110
به عدد 6 خواهیم رسید.
در این حالت همواره فرض می‌شود که عدد مدنظر، unsigned است.

ب) شیفت به راست ریاضی
شیفت به راست ریاضی، دقیقا مانند شیفت به راست منطقی است؛ مانند مثال زیر که عدد 1001  باینری را دو بیت به سمت راست منتقل می‌کند:
uint e = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(e, toBase: 2),4}");
// Before: 1001

uint f = e >> 2;
Console.WriteLine($"After:  {Convert.ToString(f, toBase: 2).PadLeft(4, '0'),4}");
// After:  0010
اما ... بجای اینکه همانند شیفت به راست منطقی، سمت چپ را با صفر پر کند، آن‌را با «با ارزش‌ترین بیت یا همان بیت علامت» پر می‌کند. یعنی در اینجا بیتی که بیانگر مثبت و منفی بودن عدد است، حفظ می‌شود. یعنی این نوع شیفت، با اعداد signed هم کار می‌کند.
برای مثال نمایش باینری عدد منفی 2,147,483,552-  به صورت زیر است:
10000000 00000000 00000000 01100000
و اگر آن‌را چهار بیت به سمت راست هدایت کنیم (یعنی 4 << 2,147,483,552 -)، به عدد 134,217,722- می‌رسیم که معادل عدد باینری زیر است:
11111000 00000000 00000000 00000110
به این ترتیب با شیفت به راست ریاضی، علامت عدد منفی حفظ شده‌است. مثالی دیگر:
int x = -8;
Console.WriteLine($"Before: {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2),32}");
// Before: -8, hex: fffffff8, binary: 11111111111111111111111111111000

int y = x >> 2;
Console.WriteLine($"After  >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2),32}");
// After  >>: -2, hex: fffffffe, binary: 11111111111111111111111111111110

int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'),32}");
// After >>>:  1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110


سؤال: در زبان جاوا، عملگر <<< به معنای unsigned right shift است؛ اما چنین عملگری در زبان #C تا نگارش 11 آن وجود ندارد؛ چرا؟!

پاسخ: چون زبان جاوا، فاقد نوع‌های داده‌ای توکار unsigned integers است (^)؛ برخلاف #C از نگارش یک آن. حتی به ظاهر، این امکان در Java 8 اضافه شده‌است، اما در حقیقت با نوع‌های int و long، فقط مانند اینکه unsigned هم می‌توانند باشند، رفتار می‌کند (^). البته نوع char در زبان Java، تنها نوع unsigned واقعی است.
همانطور که عنوان شد، در زبان #C فقط کافی است بر روی unsigned types مانند ulong, uint, ushort، عملگر << را بکار برد تا به  unsigned right shift جاوا رسید (به همین جهت عملگر اضافه‌تری برای آن ارائه نشده بود). البته باید دقت داشت که در اینجا عملگر << کار پر کردن MSB یا «با ارزش‌ترین بیت یا همان بیت علامت» را هم با صفر انجام می‌دهد؛ حتی اگر MSB مقدار دهی شده باشد (چون این کاری است که << بر روی unsigned types انجام می‌دهد).
تا پیش از C# 11 اگر نیاز به کار بر روی signed types جهت رسیدن به نتیجه‌ی عملگر <<< وجود داشته باشد (انجام شیفت منطقی؛ یعنی صرفنظر کردن از نوع علامت عدد)، می‌توان از متدهای الحاقی زیر استفاده کرد که ابتدا آن‌ها را به نمونه‌های unsigned تبدیل می‌کند و کار شیفت را انجام می‌دهد و سپس نوع اصلی را بازیابی می‌کند:
public static int UnsignedRightShift(this int signed, int places)
{
    unchecked
    {
        var unsigned = (uint)signed;
        unsigned >>= places;
        return (int)unsigned;
    }
}

public static long UnsignedRightShift(this long signed, int places)
{
    unchecked
    {
        var unsigned = (ulong)signed;
        unsigned >>= places;
        return (long)unsigned;
    }
}
نظرات مطالب
بخش دوم - بررسی امکانات (کلاس ها و متدهای) کتابخانه Gridify
من قصد پیاده سازی یک صفحه برای جستجوی پیشرفته بین همه جداول رو دارم. برای این کار یک کلاس تعریف کردم و در ورودی تابع یک لیست از این نوع را بهش پاس میدم. این کلاس اینطور تعریف شده:
public class SearchDTO
{
    public string TableName { get; set; }
    public string ColumnName { get; set; }
    public string searchPhrase { get; set; } = string.Empty;
    public DateTimeOffset? searchDateFrom { get; set; }
    public DateTimeOffset? searchDateTo { get; set; }
    public int Include { get; set; } = 1;
}
کاربر اسم جدول و اون ستونی که میخواد شرط رو براش اعمال کنه رو انتخاب میکنه و بعدش عبارت یا محدوده تاریخ رو وارد میکنه. این که شامل بشه یا نشه رو هم با Include میتونه مشخص کنه. 
من هر چی داکومنت رو خوندم توی همشون اسم جدول رو نمیشد به صورت string وارد کرد. اگه اشتباه میکنم لطفا اصلاح کنید.
رویه ای که مدنظرم هست اینه که داخل یه حلقه for یا foreach یک کوئری بنویسم که همه‌ی جداولی که کاربر انتخاب کرده رو با هم join کنه. بعدش توی یه حلقه دیگه شرط‌ها رو روی ستون هایی که انتخاب کرده اعمال کنم.
در نهایت ستون‌های نتیحه نهایی رو Select کنم تا اون ستون هایی که مجاز هستند به سمت کلاینت برگشت داده بشه. اسامی این ستون‌ها رو توی یه فایل .resx ذخیره کردم.
یعنی یه چیزی شبیه به این کد:(ولی این کد درست و قابل اجرا نیست)
// Join the tables dynamically based on the table names
for (int i = 1; i < filterList.Count; i++)
{
    var joinEntityType = entityTypes.FirstOrDefault(t => t.ClrType.Name == filterList[0].TableName)?.ClrType;
    if (entityType == null)
    {
        return (null, 0, 0);
    }
    var joinEntityQuery = (IQueryable<object>)Activator.CreateInstance(typeof(DbSet<>).MakeGenericType(joinEntityType), _dbContext);
    query = query.Join(joinEntityQuery.ToList(),
                                    x => x.GetType().GetProperty($"{filterList[i - 1].TableName}.{filterList[i - 1].TableName}Id").GetValue(x),
                                    y => y.GetType().GetProperty($"{filterList[i].TableName}.{filterList[i].TableName}Id").GetValue(y),
                                    (x, y) => x);
}

// Apply the conditions dynamically based on the column names and conditions
for (int i = 0; i < filterList.Count; i++)
{
    if (!string.IsNullOrEmpty(filterList[i].searchPhrase))
    {
        var parameter = Expression.Parameter(entityType, "x");
        var condition = Expression.Call(
            typeof(string).GetMethod("Contains", new[] { typeof(string) }),
            Expression.PropertyOrField(parameter, filterList[i].ColumnName),
            Expression.Constant(filterList[i].searchPhrase)
        );
        var lambda = Expression.Lambda<Func<object, bool>>(condition, parameter);

        query = query.Where(lambda);
    }
    if (filterList[i].searchDateFrom.HasValue)
    {
       //must write expression for date constraint
    }
}

// Select the specified columns dynamically
ResourceManager resourceManager = new ResourceManager(typeof(TablePropertiesResources));
var columnNames = resourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true)
    .OfType<DictionaryEntry>()
    .Select(entry => entry.Key.ToString())
    .ToList();
var selectColumns = columnNames.ToArray();
var selectedData = query
    .Select(x => new
    {
        // Dynamically select the desired properties
        Result = selectColumns.ToDictionary(column => column, column => x.GetType().GetProperty(column).GetValue(x))
    })
    .ToList();
آیا اینکار با gridify امکان پذیر می‌باشد؟
مطالب
مرتب سازی صحیح حروف فارسی در بانک اطلاعاتی SQLite
فرض کنید لیست حروف الفبای فارسی را در یک بانک اطلاعاتی SQLite ذخیره کرده‌اید:
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();

var createCommand = connection.CreateCommand();
createCommand.CommandText =
            @"
                CREATE TABLE persian_letter (
                    value TEXT
                );

                INSERT INTO persian_letter
                VALUES ('ا'),('ب'),('پ'),('ت'),('ث'),('ج'),('چ'),('ح'),('خ'),('د'),('ذ'),('ر'),('ز'),('ژ'),('س'),('ش'),
                       ('ص'),('ض'),('ط'),('ظ'),('ع'),('غ'),('ف'),('ق'),('ک'),('گ'),('ل'),('م'),('ن'),('و'),('ه'),('ی');
            ";
createCommand.ExecuteNonQuery();
اگر از این لیست کوئری گرفته و آن‌ها‌را مرتب کنیم:
var queryCommand = connection.CreateCommand();
queryCommand.CommandText =
            @"
                SELECT value
                FROM persian_letter
                order by value
            ";
var reader = queryCommand.ExecuteReader();
var sortedDbItems = new List<string>();
while (reader.Read())
{
    sortedDbItems.Add($"{reader["value"]}");
}
یک چنین خروجی حاصل می‌شود:


همانطور که ملاحظه می‌کنید، مرتب سازی حروف فارسی در اینجا به صورت پیش‌فرض کار نمی‌کند. علت اینجا است که روش پیش‌فرض مرتب سازی حروف در SQLite، بر اساس کد اسکی حروف است و فقط در مورد حروف ASCII از A تا Z درست کار می‌کند.


امکان تعریف Collation سفارشی در SQLite

در بانک‌های اطلاعاتی، قابلیتی که مستقیما بر روی نحوه‌ی جستجو و همچنین مرتب سازی حروف تاثیر می‌گذارد، Collation نام دارد و در SQLite برخلاف بسیاری از بانک‌های اطلاعاتی دیگر، امکان تعریف Collation سفارشی نیز وجود دارد و برای این منظور باید یک function pointer را در اختیار آن قرار داد تا از آن در سمت بانک اطلاعاتی جهت مرتب سازی و جستجوی حروف استفاده کند.
خوشبختانه پروژه‌ی Microsoft.Data.Sqlite امکان تبدیل یک managed delegate دات نتی را به یک function pointer مخصوص SQLite، میسر می‌کند. به عبارتی SQLite کدهای دات نتی را در حین انجام محاسبات خود اجرا خواهد کرد و این اجرا به صورتی نیست که ابتدا کل اطلاعات، به سمت برنامه‌ی کلاینت منتقل شود و سپس در این سمت، در حافظه، عملیاتی بر روی آن صورت گیرد. کل عملیات در سمت بانک اطلاعاتی مدیریت می‌شود.
روش تعریف یک Collation جدید هم در اینجا بسیار ساده‌است:
connection.CreateCollation("PersianCollationNoCase", (x, y) => string.Compare(x, y, ignoreCase: true));
فقط کافی است بر روی اتصال باز شده، متد CreateCollation فراخوانی و نحوه‌ی مقایسه‌ی دو رشته مشخص شود. سپس این Collation نامدار، به صورت زیر در کوئری‌ها قابل استفاده خواهد بود:
SELECT value
FROM persian_letter
order by value COLLATE PersianCollationNoCase
اینبار اگر خروجی برنامه را بررسی کنیم، مشاهده خواهیم کرد که مرتب سازی حروف فارسی در SQLite به درستی کار می‌کند:



تعریف Collation سفارشی غیرحساس به «ی و ک» !

این مورد شاید یکی از آرزوهای توسعه دهندگان SQL Server باشد! اما با SQLite به سادگی زیر قابل تعریف و مدیریت است:
connection.CreateCollation("PersianCollationNoCaseYekeInsensitive",
(x, y) => string.Compare(x.ApplyCorrectYeKe(), y.ApplyCorrectYeKe(), ignoreCase: true));
متد ApplyCorrectYeKe فوق از بسته‌ی نیوگت DNTPersianUtils.Core دریافت شده و کار آن یک‌دست کردن «ی و ک» فارسی و عربی است.
در یک چنین حالتی اگر اطلاعاتی را به همراه «ی و ک» فارسی و یا عربی ثبت کنیم:
CREATE TABLE persian_letter (
value TEXT
);
INSERT INTO persian_letter
VALUES ('ی'),('ک');
جستجوی بر روی آن‌ها دیگر وابسته‌ی به مقدار «ی و ک» وارد شده نبوده و چه «ی و ک» فارسی وارد شود و چه عربی، این کوئری همواره کار می‌کند:
SELECT count()
FROM persian_letter
WHERE value = 'ی' COLLATE PersianCollationNoCaseYekeInsensitive


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید: SQLitePersianCollation.zip
مطالب دوره‌ها
مثال - نمایش بلادرنگ تعداد کاربران آنلاین توسط SignalR
راه حل‌های زیادی برای محاسبه و نمایش تعداد کاربران آنلاین یک برنامه وب وجود دارند و عموما مبتنی بر کار با متغیرهای سشن یا Application و امثال آن هستند. این روش‌ها عموما دقیق نبوده و خصوصا قسمت قطع اتصال کاربر را نمی‌توانند دقیقا تشخیص دهند. به همین جهت نیاز به یک تایمر دارند که مثلا اگر در 5 دقیقه قبل، کاربری درخواست مشاهده آدرسی را به سرور ارسال نکرده بود، از لیست کاربران آنلاین حذف شود.
در ادامه بجای این روش‌ها، از SignalR برای محاسبه تعداد کاربران آنلاین و همچنین به روز رسانی بلادرنگ این عدد در سمت کاربر، استفاده خواهیم کرد.

تشخیص اتصال و قطع اتصال کاربران در SignalR

زیر ساخت‌های کلاس Hub موجود در SignalR، دارای متدهای ردیابی اتصال (OnConnected)، قطع اتصال (OnDisconnected) و یا برقراری مجدد اتصال کاربران (OnReconnected) هستند. با بازنویسی این متدها می‌توان به تخمین بسیار دقیقی از تعداد کاربران آنلاین یک سایت رسید.


پیشنیازهای بحث
پیشنیازهای این بحث با مطلب «مثال - نمایش درصد پیشرفت عملیات توسط SignalR» یکی است. برای مثال نحوه دریافت وابستگی‌ها، تنظیمات فایل global.asax و افزودن اسکریپت‌ها، تفاوتی با مثال یاد شده ندارند.


تعریف هاب کاربران آنلاین برنامه

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;

namespace SignalR05.Common
{
    public class OnlineUsersHub : Hub
    {
        public static readonly ConcurrentDictionary<string, string> OnlineUsers = new ConcurrentDictionary<string, string>();

        public void UpdateUsersOnlineCount()
        {
            // آی پی معرف یک کاربر است
            // اما کانکشن آی دی معرف یک برگه جدید در مرورگر او است
            // هر کاربر می‌تواند چندین برگه را به یک سایت گشوده یا ببندد
            var ipsCount = OnlineUsers.Select(x => x.Value).Distinct().Count();
            this.Clients.All.updateUsersOnlineCount(ipsCount);
        }

        /// <summary>
        /// اگر کاربران اعتبار سنجی شده‌اند بهتر است از
        /// this.Context.User.Identity.Name
        /// بجای آی پی استفاده شود
        /// </summary>        
        protected string GetUserIpAddress()
        {
            object environment;
            if (!Context.Request.Items.TryGetValue("owin.environment", out environment))
                return null;

            object serverRemoteIpAddress;
            if (!((IDictionary<string, object>)environment).TryGetValue("server.RemoteIpAddress", out serverRemoteIpAddress))
                return null;

            return serverRemoteIpAddress.ToString();
        }

        public override Task OnConnected()
        {
            var ip = GetUserIpAddress();
            OnlineUsers.TryAdd(this.Context.ConnectionId, ip);
            UpdateUsersOnlineCount();

            return base.OnConnected();
        }

        public override Task OnReconnected()
        {
            var ip = GetUserIpAddress();
            OnlineUsers.TryAdd(this.Context.ConnectionId, ip);
            UpdateUsersOnlineCount();

            return base.OnReconnected();
        }

        public override Task OnDisconnected()
        {
            // در این حالت ممکن است مرورگر کاملا بسته شده باشد
            // یا حتی صرفا یک برگه مرورگر از چندین برگه متصل به سایت بسته شده باشند
            string ip;
            OnlineUsers.TryRemove(this.Context.ConnectionId, out ip);
            UpdateUsersOnlineCount();

            return base.OnDisconnected();
        }
    }
}
کدهای کامل هاب شمارش کاربران آنلاین را در اینجا ملاحظه می‌کنید؛ به همراه نکته‌ی نحوه‌ی دریافت IP کاربر متصل شده به سایت، در یک هاب. کار افزودن یا حذف این کاربران به ConcurrentDictionary تعریف شده، در روال‌های بازنویسی شده اتصال، قطع اتصال و اتصال مجدد یک کاربر، انجام شده است.
در اینجا، هم به IP کاربر و هم به ConnectionId او نیاز است. از این جهت که هر ConnectionId، معرف یک برگه جدید باز شده در مرورگر کاربر است. اگر صرفا IPها را پردازش کنیم، با بسته شدن یکی از چندین برگه مرورگر او که اکنون به سایت متصل هستند، آمار او را از دست خواهیم داد. این کاربر هنوز چندین برگه باز دیگر را دارد که با سایت در ارتباط هستند، اما چون IP او را از لیست حذف کرده‌ایم (در نتیجه بسته شدن یکی از برگه‌ها)، آمار کلی شخص را نیز از دست خواهیم داد. بنابراین هر دوی IP و ConnectionIdها باید پردازش شوند.
اگر برنامه شما دارای اعتبارسنجی است (یک صفحه لاگین دارد)، بهتر است بجای IP از this.Context.User.Identity.Name استفاده کنید.


کدهای سمت کلاینت نمایش آمار کاربران

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.1.3.min.js" type="text/javascript"></script>
    <script type="text/javascript" src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>
</head>
<body>
    <form id="form1" runat="server">
    online users count: <span id="usersCount"></span>
    </form>
    <script type="text/javascript">
        $(function () {
            $.connection.hub.logging = true;
            var onlineUsersHub = $.connection.onlineUsersHub;
            onlineUsersHub.client.updateUsersOnlineCount = function (count) {
                $('#usersCount').text(count);
            };
            $.connection.hub.start();
        });
    </script>
</body>
</html>
با توجه به اینکه در هاب تعریف شده، متد پویای updateUsersOnlineCount، آمار تعداد کاربران متصل را (تعداد آی پی‌های منحصربفرد متصل را) به کلاینت‌ها ارسال می‌کند، بنابراین در سمت کلاینت نیز با تعریف callback ایی به همین نام، می‌توان این آمار دریافتی را به کاربران سایت نمایش داد. آماری که به صورت خودکار با کم و زیاد شدن کاربران به روز شده و نیازی نیست کاربر به صورت دستی، صفحه را به روز کند.


کدهای کامل این مثال را از اینجا نیز می‌توانید دریافت کنید:
SignalR05.zip
 
مطالب
یکسان سازی "ی" و "ک" دریافتی در حین استفاده از Entity framework

در مورد یکسان سازی ی و ک در حین استفاده از WCF RIA Services پیشتر مطلبی را در این سایت خوانده بودید. جهت تکمیل این بحث، بسط این روش به Entity framework به صورت زیر خواهد بود:

using System.Data;
using System.Data.Objects;
using System.Linq;
using System.Reflection;

namespace EfExt
{
public static class CorrectYeKe
{
public static void ApplyCorrectYeKe(this ObjectContext ctx)
{
if (ctx == null)
return;

//پیدا کردن موجودیت‌های تغییر کرده
var changedEntities = ctx.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified
);

foreach (var entity in changedEntities)
{
if (entity.Entity == null) continue;

//یافتن خواص قابل تنظیم و رشته‌ای این موجودیت‌ها
var propertyInfos = entity.Entity.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance
).Where(p => p.CanRead && p.CanWrite && p.PropertyType == typeof(string));

var pr = new PropertyReflector();

//اعمال یکپارچگی نهایی
foreach (var propertyInfo in propertyInfos)
{
var propName = propertyInfo.Name;
var val = pr.GetValue(entity.Entity, propName);
if (val != null)
{
pr.SetValue(
entity.Entity,
propName,
val.ToString().ApplyUnifiedYeKe());
}
}
}
}
}
}

ابتدا موجودیت‌های تغییر کرده یافت خواهند شد (اگر از self tracking entities استفاده می‌کنید استفاده از Context.DetectChanges پیش از فراخوانی این متد ضروری خواهد بود)، سپس در این لیست در مورد تک تک اشیاء، خواص رشته‌ای که readonly نیستند یافت شده و ی و ک آن‌ها یک دست می‌شوند.
محل اعمال آن هم باید پیش از فراخوانی Context.SaveChanges باشد.

سورس این کتابخانه را از اینجا می‌توانید دریافت کنید.

مطالب
جایگزین کردن StructureMap با سیستم توکار تزریق وابستگی‌ها در ASP.NET Core 1.0
مدل برنامه زیر را در نظر بگیرید:
 public class Service
    {
        public int ServiceId { get; set; }
        public string ServiceName { get; set; }
    }
اینترفیس ICoreService عمل بازیابی اطلاعات کلاس بالا را بر عهده دارد:
 public interface ICoreService
    {
        Service LoadDefaultService();
    }
نتیجه تزریق وابستگی ICoreService برای کنترلر Home در یک پروژه ASP.NET Core 1.0/Asp.Net Mvc 6 چنین استثنایی بود:
An unhandled exception occurred while processing the request
  InvalidOperationException: Unable to resolve service for type 'WebApplication1.Models.ICoreService' while attempting to activate 'WebApplication1.Controllers.HomeController'
Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
 
یعنی زمانیکه Activator میخواست کنترلر Home را فعالسازی کند، نتوانسته وابستگی ICoreService را برای کنترلر فراهم کند. این استثناء در Microsoft.Extensions.Internal.ActivatorUtilities.GetService اتفاق افتاده‌است.

در نسخه‌های قدیمی MVC (منظور نسخه‌های قبل از 6)، برای تزریق وابستگی‌ها از یک Controller Factory یا Dependency Resolver سفارشی استفاده می‌شد. اما در نسخه جدید MVC دیگری خبری از روشهای قدیمی نیست. چونکه یک سیستم تزریق وابستگی توکار، همراه با MVC یکپارچه شده‌است که عملیات تزریق وابستگی‌ها را انجام می‌دهد. سیستم تزریق وابستگی پیش فرض، تنها از 4 حالت عملیاتی پشتیبانی می‌کند:
1- Instance : در همه حال ، تنها یک نمونه خاص ارائه شده و شما مسئول وهله سازی آن هستید.
2- Transient : در هر بار (مثلا در هر درخواست) یک نمونه جدید ساخته میشود.
3- Singleton
4- Scoped : تنها یک نمونه در Scope فعلی ساخته می‌شود.

 تیم Asp.Net برای فراهم آوردن امکان تزریق وابستگی‌ها، تصمیم به انتزاعی کردن ویژگی‌های مشترک محبوبترین Ioc Containerها و اجازه دادن به میان افزارها، جهت ارتباط با این اینترفیس‌ها برای دستیابی به تزریق وابستگی بود.
حال بیایم نگاهی به این اینترفیس‌ها بیندازیم.
اگر به استثنای فوق نگاهی بیندازیم، می‌بینیم که متد GetService یک پارامتر از نوع IServiceProvider را میگیرد. پس اولین اینترفیس IServiceProvider می باشد. همانطور که از اسمش پیداست، کارش فرآهم آوردن سرویس می‌باشد. این اینترفیس فقط یک متد دارد، متد GetService. متد GetService مانند Container.GetInstance در StructureMap می‌باشد. تمام میان افزارها به 2 روش می‌توانند به نمونه‌ای از IServiceProvider دسترسی داشته باشند:
1- Application-Level : از طریق HttpContext.ApplicationServices برای میان افزار قابل دسترسی خواهد بود.
2- Request-Level : از طریق HttpContext.RequestServices. این Service Scope Provider توسط میان افزار در شروع هر Request Pipeline، برای هر درخواست ایجاد و در پایان درخواست توسط همان میان افزار نابود می‌گردد.
اینترفیس بعدی IServiceScope می‌باشد. همان طور که قبلا گفتیم RequestServices یک Scope Container را برای هر درخواست ساخته و در پایان همان درخواست، آن را نابود میکند. اما این کار چگونه مدیریت می‌شود؟ پاسخ، اینترفیس IServiceScope می باشد. این اینترفیس مانند یک Wrapper حول Scope Container عمل میکند و در پایان هر درخواست آن را نابود می‌کند. حال سوال اینجاست که چه کسی مسئول ساخت IServiceScope می‌باشد؟ پاسخ اینترفیس IServiceScopeFactory می‌باشد. این اینترفیس توسط متد CreateScope اقدام به ساخت یک نمونه از اینترفیس IserviceScope می‌نماید.
مورد بعدی ServiceLifeTime می‌باشد. یک Enum که حاوی سه مقدار زیر می‌باشد:
namespace Microsoft.Extensions.DependencyInjection
{
    //
    // Summary:
    //     Specifies the lifetime of a service in an Microsoft.Extensions.DependencyInjection.IServiceCollection.
    public enum ServiceLifetime
    {
        //
        // Summary:
        //     Specifies that a single instance of the service will be created.
        Singleton = 0,
        //
        // Summary:
        //     Specifies that a new instance of the service will be created for each scope.
        //
        // Remarks:
        //     In ASP.NET Core applications a scope is created around each server request.
        Scoped = 1,
        //
        // Summary:
        //     Specifies that a new instance of the service will be created every time it is
        //     requested.
        Transient = 2
    }
}
آخرین مورد کلاس ServiceDescriptor می‌باشد.  این کلاس اطلاعاتی را که Container برای رجیستر کردن سرویس به آنها نیاز دارد، در خود نگهداری می‌کند. این جمله را در نظر بگیرید : " هی Container، وقتی میخواهی این سرویس را رجیستر کنی، اطمینان حاصل کن که Singleton باشد و یک نمونه از نوع X را پیاده سازی کند." تمامی اطلاعات جمله قبل در ServiceDescriptor نگهداری می‌شود.
خوب! حال بیایم تا سرویس خود را رجیستر کنیم. در کلاس StartUp پروژه در متد ConfigurationServices خط زیر را اضافه می‌کنیم:
public void ConfigureServices(IServiceCollection services)
        {                        
            ServiceDescriptor descriptor = new ServiceDescriptor(typeof(ICoreService),typeof(CoreServise),ServiceLifetime.Transient);
            services.Add(descriptor);
            services.AddMvc();          
        }
حال اگر برنامه را اجرا کنیم مشکل برطرف شده است.

ساخت یک Service Descriptor و اضافه کردن آن به سرویسها، فلسفه وجودی میان افزارها را زیر سوال می‌برد. پس بجای ایجاد یک Service Descriptor، از متدهای الحاقی تدارک دیده شده استفاده میکنیم. مثلا بجای دو خط کد بالا می‌توان از کد زیر استفاده نمود:

services.AddTransient<ICoreService,CoreServise>();

حال که یک دید کلی از نحوه کار مکانیزم تزریق وابستگی بدست آوردیم، میخواهیم این مکانیزم را با StructureMap جایگزین کنیم. بدین منظور ابتدا پکیج StructureMap را نصب میکنم.

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

اولین مورد، کلاس StructureMapServiceProvider می‌باشد.

internal class StructureMapServiceProvider : IServiceProvider
    {
        private readonly IContainer _container;

        public StructureMapServiceProvider(IContainer container, bool scoped = false)
        {            
            _container = container;
        }

        public object GetService(Type type)
        {
            try
            {
                return _container.GetInstance(type);
            }
            catch
            {
                return null;
            }
        }
    }

مورد دوم کلاس StructureMapServiceScope می‌باشد:

internal class StructureMapServiceScope : IServiceScope
    {
        private readonly IContainer _container;
        private readonly IContainer _childContainer;
        private IServiceProvider _provider;

        public StructureMapServiceScope(IContainer container)
        {
            _container = container;
            _childContainer = _container.GetNestedContainer();
            _provider = new StructureMapServiceProvider(_childContainer, true);
        }

        public IServiceProvider ServiceProvider => _provider;

        public void Dispose()
        {
            _provider = null;
            if (_childContainer != null)
                _childContainer.Dispose();
        }
    }

مورد سوم StructureMapServiceScopeFactory می‌باشد:

internal class StructureMapServiceScopeFactory : IServiceScopeFactory
    {
        private IContainer _container;

        public StructureMapServiceScopeFactory(IContainer container)
        {
            _container = container;
        }

        public IServiceScope CreateScope()
        {
            return new StructureMapServiceScope(_container);
        }
    }

مورد بعدی کلاس StructureMapPopulator می‌باشد. وظیفه این کلاس جمع آوری اطلاعات مربوط به سرویس‌ها می‌باشد.

internal class StructureMapPopulator
    {
        private IContainer _container;

        public StructureMapPopulator(IContainer container)
        {
            _container = container;
        }

        public void Populate(IEnumerable<ServiceDescriptor> descriptors)
        {
            _container.Configure(c =>
            {
                c.For<IServiceProvider>().Use(new StructureMapServiceProvider(_container));
                c.For<IServiceScopeFactory>().Use<StructureMapServiceScopeFactory>();

                foreach (var descriptor in descriptors)
                {
                    switch (descriptor.Lifetime)
                    {
                        case ServiceLifetime.Singleton:
                            Use(c.For(descriptor.ServiceType).Singleton(), descriptor);
                            break;
                        case ServiceLifetime.Transient:
                            Use(c.For(descriptor.ServiceType), descriptor);
                            break;
                        case ServiceLifetime.Scoped:
                            Use(c.For(descriptor.ServiceType), descriptor);
                            break;
                    }
                }
            });
        }

        private static void Use(GenericFamilyExpression expression, ServiceDescriptor descriptor)
        {
            if (descriptor.ImplementationFactory != null)
            {
                expression.Use(Guid.NewGuid().ToString(), context => { return descriptor.ImplementationFactory(context.GetInstance<IServiceProvider>()); });
            }
            else if (descriptor.ImplementationInstance != null)
            {
                expression.Use(descriptor.ImplementationInstance);
            }
            else if (descriptor.ImplementationType != null)
            {
                expression.Use(descriptor.ImplementationType);
            }
            else
            {
                throw new InvalidOperationException("IServiceDescriptor is invalid");
            }
        }
    }

و در آخر کلاس StructureMapRegistration می‌باشد:

public static class StructureMapRegistration
    {
        public static void Populate(this IContainer container, IEnumerable<ServiceDescriptor> descriptors)
        {
            var populator = new StructureMapPopulator(container);
            populator.Populate(descriptors);
        }
    }

نهایتاً باید متد ConfigurationServices در کلاس StartUp را اندکی تغییر دهیم.

public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
           
            var container = new Container();
            container.Configure(configure =>
            {
                configure.For<ICoreService>().Use<CoreServise>();
            });

            container.Populate(services);

            return container.GetInstance<IServiceProvider>();
        }

در کد بالا، متد ConfigurationServices به جای آنکه Void برگرداند، نمونه‌ای از اینترفیس IServiceProvider را برمی‌گرداند. حال اگر برنامه را اجرا کنیم، وابستگی‌ها توسط StructureMap تزریق شده و برنامه بدون هیچ مشکلی اجرا می‌شود.