نظرات مطالب
ASP.NET Web API - قسمت اول
دقت داشته باشید که Web API عرضه نشده تا WCF رو منسوخ کنه. برنامه هایی که صرفاً از بستر پروتوکل HTTP به عنوان یک سرویس برای رد و بدل کردن داده‌ها استفاده می‌کنند، بهتره که از این به بعد از Web API استفاده کنند. ضمن سادگی و مفاهیم آشنای ASP.NET MVC، روش یکپارچه ای برای ایجاد وب سرویس‌های HTTP نیز به وجود اومده که مشکلات استفاده از WCF رو از بین می‌بره. WCF ذاتاً برای پیغام‌های SOAP محور طراحی شده و به کار گرفتن اون برای وب سرویس‌های HTTP یا به زور خوراندن HTTP به اون بی معنیه. در WCF راه‌های مختلفی برای ایجاد وب سرویس‌های HTTP وجود داره که باعث گمراهی و سردرگمی توسعه گر میشه و حتی فریمورک‌های مختلفی مانند OpenRasta و ServiceStack نیز بدین منظور وجود دارند. بنابراین پشتیبانی WCF از HTTP به یک پروژه‌ی دیگه تحت نام ASP.NET Web API منتقل شده و WCF Web API دیگه پشتیبانی نمیشه. کمی تغییر نام و کمی جابجایی مفاهیم دراینجا صورت گرفته. WCF همچنان قدرتمنده و نباید Web API به هیچ وجه به عنوان جایگزینی برای اون تصور بشه. ایجاد بسترهایی برای ارتباطات دو طرفه یا صفی از پیغام‌ها یا سویچ بین کانال‌ها در هنگام فعال نبودن یک کانال، اینها همه از قابلیت‌هایی هست که Web API هرگز جایگزینی برای اونها نخواهد بود و مختص WCF هستند.
مطالب
آماده سازی زیرساخت تهیه Integration Tests برای ServiceLayer

پیشنیاز

در این مطلب قصد داریم تست ServiceLayer را به جای تست درون حافظه‌ای که با ابزارهای Mocking در قالب Unit Testing انجام میگیرد، به کمک یک دیتابیس واقعی سبک وزن در قالب Integration Testing انجام دهیم.


قدم اول

یک پروژه تست را ایجاد کنید؛ بهتر است برای نظم دهی به ساختار Solution، پروژه‌های تست را در پوشه ای به نام Tests نگهداری کنید.



قدم دوم

بسته‌های نیوگت زیر را نصب کنید:

PM> install-package NUnit
PM> install-package Shouldly
PM> install-package EntityFramework
PM> install-package FakeHttpContext


قدم سوم

نسخه دیتابیس انتخابی برای تست خودکار، LocalDB می باشد. لازم است در ابتدای اجرای تست‌ها دیتابیس مربوط به Integration Test ایجاد شده و بعد از اتمام نیز دیتابیس مورد نظر حذف شود؛ برای این منظور از کلاس TestSetup استفاده خواهیم کرد.

[SetUpFixture]
public class TestSetup
{
    [OneTimeSetUp]
    public void SetUpDatabase()
    {
        DestroyDatabase();
        CreateDatabase();
    }

    [OneTimeTearDown]
    public void TearDownDatabase()
    {
        DestroyDatabase();
    }

   //...
}

با توجه به اینکه کلاس TestSetup با [SetUpFixture] تزئین شده است، Nunit قبل از اجرای تست‌ها سراغ این کلاس آمده و متد SetUpDatebase را به دلیل تزئین شدن با [OneTimeSetUp]، قبل از اجرای تست‌ها و متد TearDownDatabase را بدلیل تزئین شدن با [OneTimeTearDown]  بعد از اجرای تمام تست‌ها، اجرا خواهد کرد.


متد CreateDatabase

private static void CreateDatabase()
{
    ExecuteSqlCommand(Master, string.Format(SqlResource.DatabaseScript, FileName));

    //Use T-Sql Scripts For Create Database
    //ExecuteSqlCommand(MyAppTest, SqlResources.V1_0_0);

    var migration =
        new MigrateDatabaseToLatestVersion<ApplicationDbContext, DataLayer.Migrations.Configuration>();
    migration.InitializeDatabase(new ApplicationDbContext());

}

private static SqlConnectionStringBuilder Master =>
    new SqlConnectionStringBuilder
    {
        DataSource = @"(LocalDB)\MSSQLLocalDB",
        InitialCatalog = "master",
        IntegratedSecurity = true
    };

private static string FileName => Path.Combine(
    Path.GetDirectoryName(
        Assembly.GetExecutingAssembly().Location),
    "MyAppTest.mdf");

برای مدیریت محل ذخیره سازی فایل‌های دیتابیس، ابتدا دستورات ایجاد «دیتابیس تست» را برروی دیتابیس master اجرا می‌کنیم و در ادامه برای ساخت جداول از مکانیزم Migration خود EF استفاده شده است.

لازم است رشته اتصال به این دیتابیس ایجاد شده را در فایل App.config پروژه تست قرار دهید:

<connectionStrings>
  <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=(LocalDB)\MSSQLLocalDb;Initial Catalog=MyAppTest;Integrated Security=True;" />
</connectionStrings>


متد DestroyDatabase 

private static void DestroyDatabase()
{
    var fileNames = ExecuteSqlQuery(Master, SqlResource.SelecDatabaseFileNames,
        row => (string)row["physical_name"]);

    if (!fileNames.Any()) return;

    ExecuteSqlCommand(Master, SqlResource.DetachDatabase);

    fileNames.ForEach(File.Delete);
}

در این متد ابتدا آدرس فایل‌های مرتبط با «دیتابیس تست» واکشی شده و در ادامه دستورات Detach دیتابیس انجام شده و فایل‌های مرتبط حذف خواهند شد. فایل‌های دیتابیس در مسیری شبیه به آدرس نشان داده شده‌ی در شکل زیر ذخیره خواهند شد.


قدم چهارم

برای جلوگیری از تداخل بین تست‌ها لازم است تک تک تست‌ها از هم ایزوله باشند؛ یکی از راه حل‌های موجود، استفاده از تراکنش‌ها می‌باشد. برای این منظور امکان AutoRollback را به صورت خودکار به متدهای تست با استفاده از Attribute زیر اعمال خواهیم کرد:

public class AutoRollbackAttribute : Attribute, ITestAction
{
    private TransactionScope _scope;

    public void BeforeTest(ITest test)
    {
        _scope = new TransactionScope(TransactionScopeOption.RequiresNew,new TransactionOptions {IsolationLevel = IsolationLevel.Snapshot});
    }

    public void AfterTest(ITest test)
    {
        _scope?.Dispose();
        _scope = null;
    }

    public ActionTargets Targets => ActionTargets.Test;
}

متدهای BeforTest و AfterTest به ترتیب قبل و بعد از اجرای متدهای تست تزئین شده با این Attribute اجرا خواهند شد. 


در مواقعی هم که به HttpConext نیاز دارید، می‌توانید از کتابخانه FakeHttpContext بهره ببرید. برای این مورد هم میتوان Attributeای را به مانند AutoRollback در نظر گرفت.

public class HttpContextAttribute:Attribute,ITestAction
{
    private FakeHttpContext.FakeHttpContext _httpContext;

    public void BeforeTest(ITest test)
    {
        _httpContext = new FakeHttpContext.FakeHttpContext();

    }

    public void AfterTest(ITest test)
    {
        _httpContext?.Dispose();
        _httpContext = null;
    }

    public ActionTargets Targets => ActionTargets.Test;
}

کاری که FakeHttpContext انجام می‌دهد، مقدار دهی HttpContext.Current با یک پیاده سازی ساختگی می‌باشد.


قدم پنجم

به عنوان مثال اگر بخواهیم برای سرویس «گروه کاربری»، Integration Test بنویسیم، به شکل زیر عمل خواهیم کرد:

namespace MyApp.IntegrationTests.ServiceLayer
{
    [TestFixture]
    [AutoRollback]
    [HttpContext]
    public class RoleServiceTests
    {
        private IRoleApplicationService _roleService;

        [SetUp]
        public void Init()
        {
        }

        [TearDown]
        public void Clean()
        {
        }

        [OneTimeSetUp]
        public void SetUp()
        {
            _roleService = IoC.Resolve<IRoleApplicationService>();

            using (var uow = IoC.Resolve<IUnitOfWork>())
            {
                RoleInitialDataBuilder.Build(uow);
            }
        }

        [OneTimeTearDown]
        public void TearDown()
        {
        }

        [Test]
        [TestCase("Role1")]
        public void Should_Create_New_Role(string role)
        {
            var viewModel = new RoleCreateViewModel
            {
                Name = role
            };

            _roleService.Create(viewModel);

            using (var context = IoC.Resolve<IUnitOfWork>())
            {
                var user = context.Set<Role>().FirstOrDefault(a => a.Name == role);
                user.ShouldNotBeNull();
            }
        }

        [Test]
        public void Should_Not_Create_New_Role_With_Admin_Name()
        {
            var viewModel = new RoleCreateViewModel
            {
                Name = "Admin"
            };

            Assert.Throws<DbUpdateException>(() => _roleService.Create(viewModel));
        }

        [Test]
        public void Should_AdminRole_Exists()
        {
            using (var context = IoC.Resolve<IUnitOfWork>())
            {
                var user = context.Set<Role>().FirstOrDefault(a => a.Name == "Admin");
                user.ShouldNotBeNull();
            }
        }

        [Test]
        public void Should_Not_Create_New_Role_Without_Name()
        {
            Assert.Throws<ValidationException>(() => _roleService.Create(new RoleCreateViewModel { Name = null }));
        }
    }
}

با این خروجی:



کدهای کامل این قسمت را می‌توانید از اینجا دریافت کنید. 
اشتراک‌ها
کتابخانه gridstack.js

gridstack.js is a jQuery plugin for widget layout. This is drag-and-drop multi-column grid. It allows you to build draggable responsive bootstrap v3 friendly layouts. It also works great with knockout.jsangular.js and touch devices.  Demo  Demo

کتابخانه gridstack.js
اشتراک‌ها
Live Charts برای WPF

Modern, animated, responsive, easy to customize, easy to use, MVVM orientated, free, .Net charts, LiveCharts listens for any change in your data and updates the UI automatically, for now only available for WPF and WinForms 

Live Charts برای WPF
اشتراک‌ها
کتابخانه osm
It as a full-screen off-screen menu on (a)steroids.
It is an HTML5/Css3 responsive solution for using (and making) animated large off-screen menus. It uses CSS transitions that are hardware accellerated and much faster then jQuey animations.
It comes packed with presets (20 responsive presets and 30 presets for the editor), included Visual Editor and it is compatible with all modern desktop (HTML5) and mobile browsers (Android and iOS).
کتابخانه osm