Generic Repository ve Unit of Work Pattern, Entity Framework, Unit Testing, Autofac IoC Container ve ASP.NET MVC [Part 3]

Son 2 makalemdeki yaptığımız şeylere göz gezdirelim:

Entity Framework ile Generic Repository ve Unit Of Work İmplementasyonu

Autofac ve CRUD İşlemleri ile MVC Dependency Injection

Unit Testing:

SampleArch.Test projesini oluşturmuştuk. Şimdi mocking işlemleri için Moq kütüphanesini kullanacağız. Moq kullanmak için Package Manager Console ekranına Install-Package Moq satırını ekleyin ve çalıştırın. Bunu diğer tüm projelere referans olarak ekleyin.

Controller Test:

Controller test için Mock servis objesi ve controller methodları oluşturacağız. Basit bir örnek olması için CountryController test edeceğiz.

[TestClass]
  public class CountryControllerTest
  {
      private Mock<ICountryService> _countryServiceMock;
      CountryController objController;
      List<Country> listCountry;
      [TestInitialize]
      public void Initialize()
      {
          _countryServiceMock = new Mock<ICountryService>();
          objController = new CountryController(_countryServiceMock.Object);
          listCountry = new List<Country>() {
           new Country() { Id = 1, Name = "US" },
           new Country() { Id = 2, Name = "India" },
           new Country() { Id = 3, Name = "Russia" }
          };
      }
      [TestMethod]
      public void Country_Get_All()
      {
          //Arrange
          _countryServiceMock.Setup(x => x.GetAll()).Returns(listCountry);
          //Act
          var result = ((objController.Index() as ViewResult).Model) as List<Country>;
          //Assert
          Assert.AreEqual(result.Count, 3);
          Assert.AreEqual("US", result[0].Name);
          Assert.AreEqual("India", result[1].Name);
          Assert.AreEqual("Russia", result[2].Name);
      }
      [TestMethod]
      public void Valid_Country_Create()
      {
          //Arrange
          Country c = new Country() { Name = "test1"};
          //Act
          var result = (RedirectToRouteResult)objController.Create(c);
          //Assert
          _countryServiceMock.Verify(m => m.Create(c), Times.Once);
          Assert.AreEqual("Index", result.RouteValues["action"]);
         
      }
      [TestMethod]
      public void Invalid_Country_Create()
      {
          // Arrange
          Country c = new Country() { Name = ""};
          objController.ModelState.AddModelError("Error", "Something went wrong");
          //Act
          var result = (ViewResult)objController.Create(c);
          //Assert
          _countryServiceMock.Verify(m => m.Create(c), Times.Never);
          Assert.AreEqual("", result.ViewName);
      }
  }

Initialize: Mock servis objesi, controller ve diğer objeleri initialize eder.
Country_Get_All: Test controllerın index methodu.
Valid_Country_Create: Create actionunun Modelstate’i valid olduğu andaki test methodu.
Invalid_Country_Create: Create actionunun Modelstate’i invalid olduğu andaki yani hata fırlattığı andaki test methodu.

Service Testing:

Bunun için Mock repository ve diğer servis methodlarını kullanan objeleri oluşturacağız.

[TestClass]
  public class CountryServiceTest
  {
      private Mock<ICountryRepository> _mockRepository;
      private ICountryService _service;
      Mock<IUnitOfWork> _mockUnitWork;
      List<Country> listCountry;
      [TestInitialize]
      public void Initialize()
      {
          _mockRepository = new Mock<ICountryRepository>();
          _mockUnitWork = new Mock<IUnitOfWork>();
          _service = new CountryService(_mockUnitWork.Object, _mockRepository.Object);
          listCountry = new List<Country>() {
           new Country() { Id = 1, Name = "US" },
           new Country() { Id = 2, Name = "India" },
           new Country() { Id = 3, Name = "Russia" }
          };
      }
      [TestMethod]
      public void Country_Get_All()
      {
          //Arrange
          _mockRepository.Setup(x => x.GetAll()).Returns(listCountry);
          //Act
          List<Country> results = _service.GetAll() as List<Country>;
          //Assert
          Assert.IsNotNull(results);
          Assert.AreEqual(3, results.Count);
      }
      [TestMethod]
      public void Can_Add_Country()
      {
          //Arrange
          int Id = 1;
          Country emp = new Country() { Name = "UK" };
          _mockRepository.Setup(m => m.Add(emp)).Returns((Country e) =>
          {
              e.Id = Id;
              return e;
          });
         
          //Act
          _service.Create(emp);
          //Assert
          Assert.AreEqual(Id, emp.Id);
          _mockUnitWork.Verify(m => m.Commit(), Times.Once);
      }
  }

Initialize: Mock repository, mock unit of work ve servis objelerini initalize eder.
Country_Get_All:  GetAll methodunu test eder.
Can_Add_Country:  Create methodunu test eder.

Repository Testing:

It is less required to test repository because EF is already well tested.

Teste başlamadan önce testcontext oluşturalım ve şu classları ve kod satırlarını ekleyelim: TestContext.cs

public class TestContext : DbContext
  {
      public TestContext()
          : base("Name=TestContext")
      {
      }
      public TestContext(bool enableLazyLoading, bool enableProxyCreation)
          : base("Name=TestContext")
      {
          Configuration.ProxyCreationEnabled = enableProxyCreation;
          Configuration.LazyLoadingEnabled = enableLazyLoading;
      }
      public TestContext(DbConnection connection)
          : base(connection, true)
      {
          Configuration.LazyLoadingEnabled = false;
      }
      public DbSet<Person> Persons { get; set; }
      public DbSet<Country> Countries { get; set; }
      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
          // Suppress code first model migration check         
          Database.SetInitializer<TestContext>(new AlwaysCreateInitializer());
          modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
          modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
          modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
          base.OnModelCreating(modelBuilder);
      }
      public void Seed(TestContext Context)
      {
          var listCountry = new List<Country>() {
           new Country() { Id = 1, Name = "US" },
           new Country() { Id = 2, Name = "India" },
           new Country() { Id = 3, Name = "Russia" }
          };
          Context.Countries.AddRange(listCountry);
          Context.SaveChanges();
      }
      public class DropCreateIfChangeInitializer : DropCreateDatabaseIfModelChanges<TestContext>
      {
          protected override void Seed(TestContext context)
          {
              context.Seed(context);
              base.Seed(context);
          }
      }
      public class CreateInitializer : CreateDatabaseIfNotExists<TestContext>
      {
          protected override void Seed(TestContext context)
          {
              context.Seed(context);
              base.Seed(context);
          }
      }
      public class AlwaysCreateInitializer : DropCreateDatabaseAlways<TestContext>
      {
          protected override void Seed(TestContext context)
          {
              context.Seed(context);
              base.Seed(context);
          }
      }
  }

1. Burada iki tip constructor bulunmakta. Bunlardan bir tanesi connectionstring kullanırken bir tanesi de DBConnection  nesnesini kullanıyor. Biz bu iki tipi de kullanacağız.
2. Datasource tekrardan oluşturulduğunda, recreate edildiğinde her defasında tekrardan initialize etmesi için AlwaysCreateInitializer Initializer nesneni kullanılıyorum.

1
Database.SetInitializer<TestContext>(new AlwaysCreateInitializer());

İhtiyacınıza göre DropCreateIfChangeInitializer,CreateInitializer initializer nesnelerini de kullanabilirsiniz.

Test için 2 yol daha var bunlar :
1. Memory database içinde kullanma
2. Diğer bir database’i kullanarak test etmek.

Biz iki yolu da kullanacağız.

1. Memory database İçinde Kullanma

Bunun için Effort kütüphanesini kullanacağız. EF6 için Effort kütüphanesini install etmek için Package Manager Console ekranına :

Install-Package Effort.EF6

kod satırını ekleyip çalıştırmamız yeterlidir.

[TestClass]
  public class CountryRepositoryTest
  {
      DbConnection connection;
      TestContext databaseContext;
      CountryRepository objRepo;
      [TestInitialize]
      public void Initialize()
      {
          connection = Effort.DbConnectionFactory.CreateTransient();
          databaseContext = new TestContext(connection);
          objRepo = new CountryRepository(databaseContext);
         
      }
      [TestMethod]
      public void Country_Repository_Get_ALL()
      {
          //Act
          var result = objRepo.GetAll().ToList();
          //Assert
          Assert.IsNotNull(result);
          Assert.AreEqual(3, result.Count);
          Assert.AreEqual("US", result[0].Name);
          Assert.AreEqual("India", result[1].Name);
          Assert.AreEqual("Russia", result[2].Name);
      }
      [TestMethod]
      public void Country_Repository_Create()
      {
          //Arrange
          Country c = new Country() {Name  = "UK" };
          //Act
          var result = objRepo.Add(c);
          databaseContext.SaveChanges();
          var lst = objRepo.GetAll().ToList();
          //Assert
          Assert.AreEqual(4, lst.Count);
          Assert.AreEqual("UK", lst.Last().Name);
      }
  }

Initialize: Effort, context ve repository nesnesini initialize etmek için.
Country_Repository_Get_ALL: Repositorynin GetAll methodunu check etmek için kullanılır.
Country_Repository_Create: Repositorynin Add methodunu check etmek için kullanılır.

2. Use another database for testing

Appconfig içerisine connectionstring ekleyelim.

1
2
3
<connectionStrings>
    <add name="TestContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=SampleArchTest;Integrated Security=SSPI;AttachDBFilename=E:\Project SA\Arch\SampleArch\SampleArch\SampleArch.Test\DB\SampleArchTest.mdf" providerName="System.Data.SqlClient"/>
  </connectionStrings>
[TestClass]
   public class CountryRepositoryTestWithDB
   {
    
       TestContext databaseContext;
       CountryRepository objRepo;
       [TestInitialize]
       public void Initialize()
       {
         
           databaseContext = new TestContext();
           objRepo = new CountryRepository(databaseContext);
          
       }
       [TestMethod]
       public void Country_Repository_Get_ALL()
       {
           //Act
           var result = objRepo.GetAll().ToList();
           //Assert
           Assert.IsNotNull(result);
           Assert.AreEqual(3, result.Count);
           Assert.AreEqual("US", result[0].Name);
           Assert.AreEqual("India", result[1].Name);
           Assert.AreEqual("Russia", result[2].Name);
       }
       [TestMethod]
       public void Country_Repository_Create()
       {
           //Arrange
           Country c = new Country() {Name  = "UK" };
           //Act
           var result = objRepo.Add(c);
           databaseContext.SaveChanges();
           var lst = objRepo.GetAll().ToList();
           //Assert
           Assert.AreEqual(4, lst.Count);
           Assert.AreEqual("UK", lst.Last().Name);
       }
   }

Burada kendi regular provider’ımız kullanıyoruz ve her defasında databasei tekrardan oluşturuyoruz.

unit-test

 

ÖZET:

Bu makalede uygulamamızın kodlarına dokunmadan farklı katmanlarının nasıl test edildiğini görmüş olduk.

İyi Çalışmalar.

Reklamlar

Generic Repository ve Unit of Work Pattern, Entity Framework, Unit Testing, Autofac IoC Container ve ASP.NET MVC [Part 2]

Bir önceki makalemde model, servis ve repository katmanları oluşturdum. Bu makalemde ise oluşturduğum katmanlarla CRUD işlemlerini yapacağız ve Autofac IoC Container kuracağım.

Setup AutoFac:

Mvc projesi içinde Package Manager Console ekranındaki command satırına şu komutu yazın:

Install-Package Autofac.Mvc5

Ayrıca bu komut Autofac’ı da install edecektir.

Autofac kurulumu ile classlarımızı da register etmeye başlayabiliriz. Modules adında bir folder oluşturalım ve içine şu dosyaları alsın:

RepositoryModule.cs

public class RepositoryModule : Autofac.Module
   {
       protected override void Load(ContainerBuilder builder)
       {
           builder.RegisterAssemblyTypes(Assembly.Load("SampleArch.Repository"))
                  .Where(t => t.Name.EndsWith("Repository"))
                  .AsImplementedInterfaces()
                 .InstancePerLifetimeScope();
       }
   }

Bu işlem Autofac içindeki bütün classların sonuna “Repository” getirerek classları register edecektir.ServiceModule.cs

public class ServiceModule : Autofac.Module
  {
      protected override void Load(ContainerBuilder builder)
      {
          builder.RegisterAssemblyTypes(Assembly.Load("SampleArch.Service"))
                    .Where(t => t.Name.EndsWith("Service"))
                    .AsImplementedInterfaces()
                    .InstancePerLifetimeScope();
      }
  }

Bu işlem Autofac içindeki bütün classların sonuna “Service” getirerek classları register edecektir.

EFModule.cs

public class EFModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterModule(new RepositoryModule());
            builder.RegisterType(typeof(SampleArchContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
            builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerRequest();        
        }
    }

Global.asax dosyası içindeki Application_Start methodu içerisine bazı satırlar ekleyelim:

//Autofac Configuration
          var builder = new Autofac.ContainerBuilder();
          builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
          builder.RegisterModule(new RepositoryModule());
          builder.RegisterModule(new ServiceModule());
          builder.RegisterModule(new EFModule());
          var container = builder.Build();
          DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

Ayrıca şu namespaceleri de eklememiz gerekmektedir.

using Autofac.Integration.Mvc;
using Autofac;

Artık service objesini controller classları içerisine enjekte edebiliriz.

Country CRUD Implementasyonu:

Mvc projesinin içerisine Model ve Servis projelerinin referanslarını ekleyelim. Yeni bir controller ekleyip optionunu “MVC 5 Controller with Views using Entity Framework” seçelim. Name : CountryController,  Select Model : “Country“, DataContext:SampleArchContext, Set “Generate Views” true, Click Add deyip controller ekleme işini tamamlayalım. Böylece EF kullanarak action ve viewları implement etmiş olduk. Şimdi controllerımızı service katmanına göre modifiye edelim:

public class CountryController : Controller
   {
       //initialize service object
       ICountryService _CountryService;
       public CountryController(ICountryService CountryService)
       {
           _CountryService = CountryService;
       }
       //
       // GET: /Country/
       public ActionResult Index()
       {
           return View(_CountryService.GetAll());
       }      
       //
       // GET: /Country/Create
       public ActionResult Create()
       {
           return View();
       }
       //
       // POST: /Country/Create
       [HttpPost]
       [ValidateAntiForgeryToken]
       public ActionResult Create(Country country)
       {
           // TODO: Add insert logic here
           if (ModelState.IsValid)
           {
               _CountryService.Create(country);
               return RedirectToAction("Index");
           }
           return View(country);
       }
       //
       // GET: /Country/Edit/5
       public ActionResult Edit(int id)
       {           
           Country country = _CountryService.GetById(id);
           if (country == null)
           {
               return HttpNotFound();
           }
           return View(country);
       }
       //
       // POST: /Country/Edit
       [HttpPost]
       public ActionResult Edit(Country country)
       {
           if (ModelState.IsValid)
           {
               _CountryService.Update(country);
               return RedirectToAction("Index");
           }
           return View(country);
       }
       //
       // GET: /Country/Delete/5
       public ActionResult Delete(int id)
       {
           Country country = _CountryService.GetById(id);
           if (country == null)
           {
               return HttpNotFound();
           }
           return View(country);
       }
       //
       // POST: /Country/Delete/5
       [HttpPost, ActionName("Delete")]
       [ValidateAntiForgeryToken]
       public ActionResult Delete(int id, FormCollection data)
       {
           Country country = _CountryService.GetById(id);
           _CountryService.Delete(country);
           return RedirectToAction("Index");
       }
   }

Yukarıda gördüğünüz gibi Controller classımızın constructoruna servis katmanından gelen ICountryService objesini enjekte ettik. Constructor Dependency injection controller classımız teste uygun hale getirir.

Şimdi relationship içeren diğer CRUD işlemlerinin gözden geçirelim :

Person CRUD Implementasyonu :

CountryControllara benzer bir şekilde PersonController ekliyoruz. Aşağıda Entity Framework yapısı kullanarak Country dropdownList oluşturacağız. CountryController’da olduğu gibi servis katmanı ile PersonController classını da modifiye edelim.

public class PersonController : Controller
  {
      IPersonService _PersonService;
      ICountryService _CountryService;
      public PersonController(IPersonService PersonService, ICountryService CountryService)
      {
          _PersonService = PersonService;
          _CountryService = CountryService;
      }
      // GET: /Person/
      public ActionResult Index()
      {
          return View(_PersonService.GetAll());
      }
      // GET: /Person/Details/5
      public ActionResult Details(long? id)
      {
          if (id == null)
          {
              return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
          }
          Person person = _PersonService.GetById(id.Value);
          if (person == null)
          {
              return HttpNotFound();
          }
          return View(person);
      }
      // GET: /Person/Create
      public ActionResult Create()
      {
          ViewBag.CountryId = new SelectList(_CountryService.GetAll(), "Id", "Name");
          return View();
      }
      // POST: /Person/Create
      // To protect from overposting attacks, please enable the specific properties you want to bind to, for
      // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
      [HttpPost]
      [ValidateAntiForgeryToken]
      public ActionResult Create([Bind(Include = "Id,Name,Phone,Address,State,CountryId")] Person person)
      {
          if (ModelState.IsValid)
          {
              _PersonService.Create(person);
              return RedirectToAction("Index");
          }
          ViewBag.CountryId = new SelectList(_CountryService.GetAll(), "Id", "Name", person.CountryId);
          return View(person);
      }
      // GET: /Person/Edit/5
      public ActionResult Edit(long? id)
      {
          if (id == null)
          {
              return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
          }
          Person person = _PersonService.GetById(id.Value);
          if (person == null)
          {
              return HttpNotFound();
          }
          ViewBag.CountryId = new SelectList(_CountryService.GetAll(), "Id", "Name", person.CountryId);
          return View(person);
      }
      // POST: /Person/Edit/5
      // To protect from overposting attacks, please enable the specific properties you want to bind to, for
      // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
      [HttpPost]
      [ValidateAntiForgeryToken]
      public ActionResult Edit([Bind(Include = "Id,Name,Phone,Address,State,CountryId")] Person person)
      {
          if (ModelState.IsValid)
          {
              _PersonService.Update(person);
              return RedirectToAction("Index");
          }
          ViewBag.CountryId = new SelectList(_CountryService.GetAll(), "Id", "Name", person.CountryId);
          return View(person);
      }
      // GET: /Person/Delete/5
      public ActionResult Delete(long? id)
      {
          if (id == null)
          {
              return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
          }
          Person person = _PersonService.GetById(id.Value);
          if (person == null)
          {
              return HttpNotFound();
          }
          return View(person);
      }
      // POST: /Person/Delete/5
      [HttpPost, ActionName("Delete")]
      [ValidateAntiForgeryToken]
      public ActionResult DeleteConfirmed(long id)
      {
          Person person = _PersonService.GetById(id);
          _PersonService.Delete(person);
          return RedirectToAction("Index");
      }
  }

Yukarda da gördüğümüz bu controllerımızın constructoruna üzere hem CountryService , hem de PersonService nesnesini enjekte ettik. Çünkü controllerımızda her iki servisimize de erişmemiz gerekti. CountryService, ülkelerin dropdown seçimi için, PersonService ise CRUD işlemleri için gerekliydi.

Sonuç olarak view ekranımız şu şekilde olacaktır:

crud-autofac

ÖZET:

Bu makalemde Autofac kurulumunu ve Entity Framework ve Service Katmanları ile Generic Repository ve Unit of Work Pattern üzerinden base alarak CRUD işlemlerinin nasıl yapıldığını öğrendik. Diğer makalemde farklı katmanların Unit Test implementasyonunu anlatacağım.

İyi Çalışmalar.

Generic Repository ve Unit of Work Pattern, Entity Framework, Unit Testing, Autofac IoC Container ve ASP.NET MVC [Part 1]

Bu makalemde Asp.Net MVC içinde Entity Framework, IOC Container, Dependency Injection ile Generic Repository Pattern’ine base olmuş ayrılmış, Unit Test edilebilir, N katmanlı mimarili bir uygulamanın nasıl implement edildiğini göstereceğim.

Bu Uygulama İçin Kullanılan Teknoloji ve Toollar : 

1. .NET Framework 4.5
2. ASP.NET MVC 5.1
3. Entity Framework 6.0
4. Sql Server LocalDB v11
5. Autofac 3.4
5. Moq
6. Effort
7. Visual Studio 2012

Amaç :

  1.  Unit Test uygulamak ve test işlemini kolayca entegre edebilmek.
  2. IOC ve Unit Test ile Repository ve Unit of Work üzerinde örnek uygulamayı tamamlamak.

Repository Pattern: Proje için abstract bir data erişim katmanı oluşturmak, tüm veri erişim mantığının tek bir yerde toplamasını sağlar. Generic özellik ile ise ortak senaryolar için kod miktarını azaltabilir, tek bir yerden yönetebiliriz.

Unit of Work pattern: Etkileşimleri birleştirmek ve bir kez işlem kullanarak onları bağlamak için kullanılan desendir.

Daha fazla bilgi için aşağıdaki linklere göz gezdirebilirsiniz:

Repository Pattern.

Design pattern – Inversion of control and Dependency injection

Uygulamamıza farklı katmanlar da içerebilir:

1. Database
2. Data Access Katmanı (ORM Araçlarını (EF, Linq), Modelleri ve DataContext içeren katman)
3. Servis Katmanı (Business ve Domain Mantığı)
4. MVC Web Katmanı (UI Part)
5. Test Katmanı (Unit Test vb..)

Solution’u Hazırlamak:

1. Visual Studio’yu açın ve yeni bir  “ASP.NET MVC 5 Empty Project” oluşturun ve ismine “SampleArch“ verin.
2. Solutiona sağ tıklayın ve “Class Library” Projeleri ekleyin.

SampleArch.Model
SampleArch.Repository
SampleArch.Service

Default olarak oluşturulan Class1.cs isimli classı silebilirsiniz.

3. Add project > Test > “Unit Test project” isimli bir test projesi ekleyin.

SampleArch.Test

Default olarak oluşturulan UnitTest1.cs isimli classı silin.

folder-structure

Model:

Öncelikle Entity Framework yüklemeliyiz. Bunun için Package Manager Console ekranından command satırına Install-Package EntityFramework yazarak Entity Framework’un son sürümü olan 6.1 package’ı soluitona eklenmiş olacaktır.

SampleArch.Model projemizin içine bazı base classlar ekleyelim.

IEntity.cs:

public interface IEntity<T>
{
    T Id { get; set; }
}

Entity.cs:

public abstract class BaseEntity {
}
public abstract class Entity<T> : BaseEntity, IEntity<T>
{
    public virtual T Id { get; set; }
}

IAuditableEntity.cs:

public interface IAuditableEntity
   {
       DateTime CreatedDate { get; set; }
    
       string CreatedBy { get; set; }
       DateTime UpdatedDate { get; set; }
            
       string UpdatedBy { get; set; }
   }

AuditableEntity.cs:

public abstract class AuditableEntity<T> : Entity<T>, IAuditableEntity   
   {
       [ScaffoldColumn(false)]
       public DateTime CreatedDate { get; set; }
     
       [MaxLength(256)]
       [ScaffoldColumn(false)]
       public string CreatedBy { get; set; }
       [ScaffoldColumn(false)]
       public DateTime UpdatedDate { get; set; }
       [MaxLength(256)]
       [ScaffoldColumn(false)]
       public string UpdatedBy { get; set; }
   }

ScaffoldColumn(false) ise şu şekilde kullanılır : ASP.NET MVC Scaffolding bu propertyleri View içinde generate etmeyecektir. Biz bu propertyleri context içindeki SaveChanges methodunda ele almış olacağız.

Person and Country Modellerini Oluşturalım :

[Table("Person")]
   public class Person : AuditableEntity<long>
   {      
       [Required]
       [MaxLength(50)]
       public string Name { get; set; }
       [Required]
       [MaxLength(20)]
       public string Phone { get; set; }
       [Required]
       [MaxLength(100)]
       public string Address { get; set; }
       [Required]
       [MaxLength(50)]
       public string State { get; set; }
       [Display(Name="Country")]
       public int CountryId { get; set;  }
       [ForeignKey("CountryId")]
       public virtual Country Country { get; set; }
   }
public class Country : Entity<int>
  {     
      [Required]
      [MaxLength(100)]
      [Display(Name="Country Name")]
      public string Name { get; set; }
      public virtual IEnumerable<Person> Persons { get; set; }
  }
DB Context Ekleyelim : 
public class SampleArchContext : DbContext
  {
      public SampleArchContext()
          : base("Name=SampleArchContext")
      {
      }
      public DbSet<Person> Persons { get; set; }
      public DbSet<Country> Countries { get; set; }
     
      public override int SaveChanges()
      {
          var modifiedEntries = ChangeTracker.Entries()
              .Where(x => x.Entity is IAuditableEntity
                  && (x.State == System.Data.Entity.EntityState.Added || x.State == System.Data.Entity.EntityState.Modified));
          foreach (var entry in modifiedEntries)
          {
              IAuditableEntity entity = entry.Entity as IAuditableEntity;
              if (entity != null)
              {
                  string identityName = Thread.CurrentPrincipal.Identity.Name;
                  DateTime now = DateTime.UtcNow;
                  if (entry.State == System.Data.Entity.EntityState.Added)
                  {
                      entity.CreatedBy = identityName;
                      entity.CreatedDate = now;
                  }
                  else {
                      base.Entry(entity).Property(x => x.CreatedBy).IsModified = false;
                      base.Entry(entity).Property(x => x.CreatedDate).IsModified = false;                  
                  }
                  entity.UpdatedBy = identityName;
                  entity.UpdatedDate = now;
              }
          }
          return base.SaveChanges();
      }
  }

Burada SaveChanges yöntemi override ederek denetimi yapılan sütun değerlerini set ediyoruz.

Şimdi ise web config içerisinde SampleArchContext isminde bir ConnectionString ekliyoruz.

<connectionStrings>
  <add name="SampleArchContext"
    connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=SampleArch;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\SampleArch.mdf"
    providerName="System.Data.SqlClient"/>
</connectionStrings>

class-model

Repository Ekleme:

SampleArch.Repository” projesinin içerisine, “SampleArch.Model” projesinin referansını eklemeliyiz. Aşağıdaki base classlarımı projemize ekleyelim:

IGenericRepository.cs

public interface IGenericRepository<T> where T : BaseEntity
  {
      IEnumerable<T> GetAll();
      IEnumerable<T> FindBy(Expression<Func<T, bool>> predicate);
      T Add(T entity);
      T Delete(T entity);
      void Edit(T entity);
      void Save();
  }

GenericRepository.cs

public abstract class GenericRepository<T> : IGenericRepository<T>
      where T : BaseEntity
   {
       protected DbContext _entities;
       protected readonly IDbSet<T> _dbset;
       public GenericRepository(DbContext context)
       {
           _entities = context;
           _dbset = context.Set<T>();
       }
       public virtual IEnumerable<T> GetAll()
       {
           return _dbset.AsEnumerable<T>();
       }
       public IEnumerable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
       {
           IEnumerable<T> query = _dbset.Where(predicate).AsEnumerable();
           return query;
       }
       public virtual T Add(T entity)
       {
           return _dbset.Add(entity);
       }
       public virtual T Delete(T entity)
       {
           return _dbset.Remove(entity);
       }
       public virtual void Edit(T entity)
       {
           _entities.Entry(entity).State = System.Data.Entity.EntityState.Modified;
       }
       public virtual void Save()
       {
           _entities.SaveChanges();
       }
   }

Person ve Country Repositorylerini ekleyelim:

IPersonRepository.cs

public interface IPersonRepository : IGenericRepository<Person>
{
    Person GetById(long id);
}

PersonRepository.cs

public class PersonRepository : GenericRepository<Person>, IPersonRepository
   {
       public PersonRepository(DbContext context)
           : base(context)
       {
          
       }
       public override IEnumerable<Person> GetAll()
       {
           return _entities.Set<Person>().Include(x=>x.Country).AsEnumerable();
       }
       public Person GetById(long id)
       {
           return _dbset.Include(x=>x.Country).Where(x => x.Id == id).FirstOrDefault();           
       }
   }

Burada GetAll() methodunu Country classı içerisinde override ettik.

ICountryRepository.cs

public interface ICountryRepository : IGenericRepository<Country>
   {
       Country GetById(int id);
   }

CountryRepository.cs

public class CountryRepository : GenericRepository<Country>, ICountryRepository
    {
      public CountryRepository(DbContext context)
            : base(context)
        {
           
        }
        public Country GetById(int id)
        {
            return FindBy(x => x.Id == id).FirstOrDefault();           
        }
    }

Unit Of Work:

IUnitOfWork.cs

public interface IUnitOfWork : IDisposable
   {
       /// <summary>
       /// Saves all pending changes
       /// </summary>
       /// <returns>The number of objects in an Added, Modified, or Deleted state</returns>
       int Commit();
   }

UnitOfWork.cs

/// <summary>
   /// The Entity Framework implementation of IUnitOfWork
   /// </summary>
   public sealed class UnitOfWork : IUnitOfWork
   {
       /// <summary>
       /// The DbContext
       /// </summary>
       private DbContext _dbContext;
       /// <summary>
       /// Initializes a new instance of the UnitOfWork class.
       /// </summary>
       /// <param name="context">The object context</param>
       public UnitOfWork(DbContext context)
       {        
           _dbContext = context;
       }
       /// <summary>
       /// Saves all pending changes
       /// </summary>
       /// <returns>The number of objects in an Added, Modified, or Deleted state</returns>
       public int Commit()
       {
           // Save changes with the default options
           return _dbContext.SaveChanges();
       }
       /// <summary>
       /// Disposes the current object
       /// </summary>
       public void Dispose()
       {
           Dispose(true);
           GC.SuppressFinalize(this);
       }
       /// <summary>
       /// Disposes all external resources.
       /// </summary>
       /// <param name="disposing">The dispose indicator.</param>
       private void Dispose(bool disposing)
       {
           if (disposing)
           {
               if (_dbContext != null)
               {
                   _dbContext.Dispose();
                   _dbContext = null;
               }
           }
       }
   }

Service Layer:

SampleArch.Service projesi içine, SampleArch.Model ve SampleArch.Repository projelerinin referanslarını ekleyelim, ve projeye şu base classları ekleyelim.

IService.cs

public interface IService
    {
    }

IEntityService.cs

public interface IEntityService<T> : IService
    where T : BaseEntity
   {
       void Create(T entity);
       void Delete(T entity);
       IEnumerable<T> GetAll();     
       void Update(T entity);
   }

EntityService.cs

public abstract class EntityService<T> : IEntityService<T> where T : BaseEntity
   {
       IUnitOfWork _unitOfWork;
       IGenericRepository<T> _repository;
       public EntityService(IUnitOfWork unitOfWork, IGenericRepository<T> repository)
       {
           _unitOfWork = unitOfWork;
           _repository = repository;
       }    
       public virtual void Create(T entity)
       {
           if (entity == null)
           {
               throw new ArgumentNullException("entity");
           }
           _repository.Add(entity);
           _unitOfWork.Commit();        
       }
       public virtual void Update(T entity)
       {
           if (entity == null) throw new ArgumentNullException("entity");
           _repository.Edit(entity);
           _unitOfWork.Commit();
       }
       public virtual void Delete(T entity)
       {
           if (entity == null) throw new ArgumentNullException("entity");
           _repository.Delete(entity);
           _unitOfWork.Commit();
       }
       public virtual IEnumerable<T> GetAll()
       {
           return _repository.GetAll();
       }
   }

Person ve Country modelleri için Servis classlarını ekleyelim.

ICountryService.cs

public interface ICountryService : IEntityService<Country>
  {
      Country GetById(int Id);
  }

CountryService.cs

public class CountryService : EntityService<Country>, ICountryService
  {
      IUnitOfWork _unitOfWork;
      ICountryRepository _countryRepository;
      
      public CountryService(IUnitOfWork unitOfWork, ICountryRepository countryRepository)
          : base(unitOfWork, countryRepository)
      {
          _unitOfWork = unitOfWork;
          _countryRepository = countryRepository;
      }
      public Country GetById(int Id) {
          return _countryRepository.GetById(Id);
      }
  }

IPersonService.cs

public interface IPersonService : IEntityService<Person>
   {
       Person GetById(long Id);
   }

PersonService.cs

public class PersonService : EntityService<Person>, IPersonService
   {
       IUnitOfWork _unitOfWork;
       IPersonRepository _personRepository;
       
       public PersonService(IUnitOfWork unitOfWork, IPersonRepository personRepository)
           : base(unitOfWork, personRepository)
       {
           _unitOfWork = unitOfWork;
           _personRepository = personRepository;
       }
       public Person GetById(long Id) {
           return _personRepository.GetById(Id);
       }
   }

class-service

Bir sonraki makalemde autofac kurulumunu ve CRUD işlemlerini ele alacağım.

İyi Çalışmalar diliyorum..

Kaynak : http://techbrij.com/service-layer-entity-framework-asp-net-mvc-unit-testing

C# Ile SCOPE_IDENTITY (Son Eklenen Kayıdın ID Sini Almak)

Yeni bir kayıt girdiniz ve bu kaydın rowID sine ihtiyacınız var ise SCOPE_IDENTITY i kullanabilirsiniz.
Aşağıdaki örnek kullanımını açıklamak için yeterli olacaktır.
SQL Query

cmdInsert.CommandText =
"INSERT INTO CrmEmail (CustomerID, EmailAddress, EmailSource) VALUES (@CustomerID, @EmailAddress, @EmailSource) SET @ID = SCOPE_IDENTITY()";
 
cmdInsert.Parameters.ADD("CustomerID", SqlDbType.NVarChar, 10).Value = CariHesapKodu;
cmdInsert.Parameters.ADD("EmailAddress", SqlDbType.NVarChar, 50).Value = EmailAdresi;
cmdInsert.Parameters.ADD("EmailSource", SqlDbType.NVarChar, 30).Value = LinkKaynagi;
cmdInsert.Parameters.ADD("@ID", SqlDbType.Int).Direction = ParameterDirection.Output;
 
conInsert.Open();
cmdInsert.ExecuteNonQuery();
conInsert.Close();
 
MessageBox.SHOW(cmdInsert.Parameters["@ID"].Value.ToString());

SQL Server’da Transaction Kullanımı/Yönetimi

Veritabanı sistemlerini yüzeysel kullanan geliştiriciler için çok basit gelen işlemler aslında mimari olarak geri planda bir çok iş sürecinin doğru yürütülmesiyle meydana gelmektedir. Bu iş süreçleri veritabanı motoru tarafından yönetilir. Ancak, depoladığımız veriyle ilgili işlemlerin bütünlüğü tamamen bize, yani geliştiriciye aittir.

Örneğin, birbiriyle ilişkili bir sorgu yapısı var ve biz bu kodların tamamının doğru çalışması halinde başarılı bir işlem gerçekleştirmiş olacak isek, bu durumda herhangi bir parça işlemin başarısız olması halinde diğer yapılan tüm işlemlerin geri alınması mantıklı olacaktır. Bu işleme en gerçekçi ve bilinen örnek bankacılık işlemleri gösterilebilir. Bir havale yapmak için gerekli tüm işlemleri yaptınız ve X hesabından Y hesabına 10 tl para göndereceksiniz. Bu işlemin programsal olarak yapılabilmesi için sizin hesabınızdaki bakiyenin UPDATE ile güncellenerek azaltılması, gönderilen hesabın bakiyesinin de UPDATE ile güncellenerek alınan miktar kadar artırılması gerekir. Ancak teknolojide herşey doğru gitmeyebilir. Bu işlemler sırasında herhangi bir hata olursa sizin hesabınızdan 10 tl düşülecek, ancak karşı tarafın hesabı güncellenerek bakiye artırımı gerçekleşmeyebilir.

Veritabanı mimarisinde bu tür iş süreci(transaction) gerektiğinde uyulması gereken prensibe ACID (Atomicity,Consistency, Isolation, Durability) denir.

Şimdi ACID’i sırasıyla inceleyelim.

Bölünemezlik(Atomicity)

Transaction işleminin ana özelliği olarak açıkladığımız bölünemezlik prensibini yansıtır. Bir transaction bloğu yarım kalamaz. Yarım kalan transaction bloğu veri tutarsızlığına neden olur. Ya tüm işlemler gerçekleştirilir, ya da transaction başlangıcına geri döner. Yani transaction’ın gerçekleştirdiği tüm değişiklikler geri alınarak gerçekleşmeden önceki haline döner.

Tutarlılık(Consistency)

Bölünemezlik kuralının alt yapısını oluşturduğu bir kuraldır. Transaction veri tutarlılığı sağlamalıdır. Yani bir transaction içerisinde güncelleme işlemi gerçekleştiyse ve ya kalan tüm işlemler de gerçekleşmeli ya da güncelle işlemi de geri alınmalıdır. Bu veri tutarlılığı açısından çok önemlidir.

İzolasyon(Isolation)

Her transaction veritabanı için bir istek paketidir. Bir istek paketi (transaction) tarafından gerçekleştirilen değişiklikler tamamlanmadan bir başka transaction tarafından görülememelidir. Her transaction ayrı olarak işlenmelidir. Transaction’ın tüm işlemleri gerçekleştikten sonra bir başka transaction tarafından görülebilmelidir.

Dayanıklılık (Durability)

Transaction’lar veri üzerinde karmaşık işlemler gerçekleştirebilir. Bu işlemlerin bütününü güvence altına almak için transaction hatalara karşı dayanıklı olmalıdır. SQL Server’da meydana gelebilecek sistem sorunu, elektrik kesilmesi, işletim sisteminden ya da farklı yazılımlardan kaynaklanabilecek hatalara karşı hazırlıklı ve dayanıklı olmalıdır.

Bilgisayar bilimlerinde imkansız diye bir şey yoktur. Bir sistemin hata vermesi yüzlerce sebebe bağlı olabilir. Örneğin; geçtiğimiz yıllarda büyük bir GSM operatörünün veri merkezinin bulunduğu binaya sel basması sonucu veri kaybı yaşamışlardı. Bu durum olasılığı düşük gibi görünebilir. Ancak bahsi edilen konu önemli veriler olunca, hesaplanması gereken olasılıkların sınırı yoktur. Tüm hata olasılıklarına karşı dayanıklı bir sistem geliştirilmelidir.

Transactıon İfadeleri

Transaction yönetimi için kullanılan dört farklı ifade vardır. Bu ifadeler ile transaction başlatılabilir (BEGIN), işlemler geri alınabilir (ROLLBACK), transaction bitirilebilir (COMMIT) ya da kayıt noktaları (SAVE) oluşturulabilir.

Transactıon’ı Başlatmak: BEGIN TRAN

Transaction’ın başlangıcını belirtir. Bu kısımdan sonraki tüm işlemler transaction’ın bir parçasıdır. İşlem sırasında oluşabilecek olası sorunlarda geri alma ya da transaction’ın sonlandırılması gerçekleştirilebilir.

Söz Dizimi:

1
BEGIN TRAN[SACTION] [transaction_ismi | @transaction_degiskeni]

Transactıon’ı Tamamlamak: COMMIT TRAN

Transaction’ın tamamlandığını ve gerçekleştirilen transaction işlemlerinin kalıcı olarak veritabanına yansıtılması için kullanılır. Transaction tarafından etkilenen tüm değişiklikler, işlemlerin tamamı gerçekleşmese bile, bu işlemden sonra kalıcı hale gelir.
COMMIT işleminden sonra gerçekleşen değişikliklerin geri alınması için, bu işlemleri geri alacak yeni bir transaction oluşturulmalıdır. Örneğin; bir transaction ile, nümerik bir sütun üzerinde 10 birim azaltma işlemi yapıldı ise, bu işlemi geri almak için aynı sütun üzerinde 10 birim artırma işlemi yapacak yeni bir transaction oluşturulmalıdır.

Söz Dizimi:

1
COMMIT TRAN[SACTION] [transaction_ismi | @transaction_degiskeni]

Transactıon’ı Geri Almak : ROLLBACK TRAN

Transaction’ın gerçekleştirdiği tüm işlemleri geri almak için kullanılır. Yani, yapılan tüm işlemler transaction’ın başlangıcındaki haline geri döner. Verilerdeki değişikliklerin anında kalıcı olarak veritabanına yansıtılmadığını belirtmiştik. ROLLBACK ile gerçekleştirilen tüm işlemler geriye alınarak transaction sonucunun tutarlılığı garanti edilir.
ROLLBACK işlemi, oluşturduğunuz transaction mimarisine bağlı olarak, kayıt noktalarına (save points) geri dönüş için de kullanılabilir.

Söz Dizimi:

1
2
ROLLBACK TRAN[SACTION] [transaction_ismi | kayit_noktasi_ismi
| @transaction_degiskeni | @kayit_noktasi_degiskeni]

Sabitleme Noktaları: SAVE TRAN

ROLLBACK işlemi transaction’da en başa dönmek için kullanılır. Bazen de belirli bir noktaya kadar gerçekleşen işlemlerin geçerli kalması istenebilir. Bu işlemlerden sonra gerçekleşecek işlemler için ROLLBACK’e ihtiyaç duyulabilir. Sabitleme noktaları oluşturulması, transaction içerisinde en başa dönmek yerine, belirlenen bir işlem noktasına dönmek için kullanılır

Söz Dizimi:

1
SAVE TRAN[SACTION] [ kayit_noktasi_ismi | @kayit_noktasi_degiskeni ]

Transactıon Oluşturmak

Transaction işlemleri için en uygun örnekler bankacılık ve gsm operatörlerinin veritabanı işlemleridir. Karmaşık ve bir çok iş parçacığı ile gerçekleşen sayısız işlemden oluşan bu tür uygulamalarda iş bloklarının doğru planlanması ve kullanılması önemlidir.
Bir banka veritabanında kullanıcı hesaplarını tutan ve farklı iki banka hesabı arasında havale işlemini gerçekleştirmek için bir uygulama oluşturalım.

Banka müşterilerinin hesap bilgilerini tutan basit bir tablo oluşturalım.

1
2
3
4
5
6
7
CREATE TABLE Accounts(
    AccountID   CHAR(10) PRIMARY KEY NOT NULL,
    FirstName   VARCHAR(50),
    LastName    VARCHAR(50),
    Branch  INT,
    Balance MONEY
);

AccountID : Hesap sahibine özel benzersiz değer.
FirstName : Hesap sahibinin adı.
LastName : Hesap sahibinin soyadı.
Branch : Şube kodu.
Balance : Hesap bakiyesi.

1
2
3
4
INSERT INTO Accounts
VALUES('0000065127','Cihan','Özhan', 489, 10000),
      ('0000064219','Ali','Veli', 489, 500),
      ('0000068233','Hasan','Hseyin', 252, 5844);

Sorgulama için gerekli veriyi eklediğimize göre, artık ilgili prosedürümüzü oluşturarak transaction’ımızı yönetmeye başlayabiliriz.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
CREATE PROC sp_MoneyTransfer(
    @PurchaserID    CHAR(10),
    @SenderID       CHAR(10),
    @Amount         MONEY,
    @retVal         INT OUT
)
AS
BEGIN
    DECLARE @inControl MONEY;
    SELECT @inControl = Balance FROM Accounts WHERE AccountID = @SenderID;
    IF @inControl >= @Amount
    BEGIN
        BEGIN TRANSACTION
            UPDATE Accounts
            SET Balance = Balance - @Amount
            WHERE AccountID = @SenderID
        IF @@ERROR <> 0
        ROLLBACK
            UPDATE Accounts
            SET Balance = Balance + @Amount
            WHERE AccountID = @PurchaserID
        IF @@ERROR <> 0
        ROLLBACK
        COMMIT
    END
    ELSE
    BEGIN
        SET @retVal = -1;
        RETURN @retVal;
    END
END;

Prosedürün aldığı parametreleri açıklamak gerekirse;

PurchaserID : Alıcı hesabın ID’si
SenderID : Gönderen hesabın ID’si
Amount : Gönderilecek tutar
retVal : Prosedür içerisinden geriye değer dönmeyi sağlayan OUT parametre. İşlem başarısız olursa -1 değeri döndürecek.

Şimdi, Cihan Özhan’ın Ali Veli’ye 500 tl göndermesini sağlayacak prosedür çağrımını yapalım.

1
2
3
DECLARE @rVal INT;
EXEC sp_MoneyTransfer '0000064219','0000065127',500, @rVal out;
SELECT @rVal;

Bu tür prosedürlerde bir çok işlem kontrol edilir. Ancak biz sadece gönderen kişinin hesabında göndermek istediği kadar miktar olup olmadığını kontrol ettik.

Alintidir.

Kaynak: http://www.cihanozhan.com/sql-serverda-transaction-kullanimi-yonetimi/

Kaynak : Yazılımcılar İçin İleri Seviye T-SQL Programlama

Asp.net Mvc Highchart örneği

Json Verisi ile Dinamik Olarak HighChart Oluşturmak

ALİ ÖZDEMİR

Herkese merhaba,
Bugün sizlere asp.net mvc de highchartlar nasıl dinamik yapılır bunu göstermek istiyorum. HighChart nedir diyorsanız bu siteyi ziyaret edebilirsiniz. http://www.highcharts.com/
Sizler için 2 tane örnek hazırladım. İlki pie chart, diğeri column chart.
Öncelikle pie chart ile başlayalım.
Önce controllerda view tarafı için gerekli verimizi ayarlıyoruz. Kodlar şu şekilde,
public class Data
{
public string Name { get; set; }

public int Count { get; set; }
}

View tarafından ajax ile istekde bulunacağımız metod
public JsonResult GetData()
{
List list = new List()
{
new Data(){ Name = "Elma", Count=20 },
new Data(){ Name = "Portakal", Count=30 },
new Data(){ Name = "Ceviz", Count=10},
new Data(){ Name = "Muz", Count=10 },
new Data(){ Name = "Kahve", Count=30 },
};
return new JsonResult() { Data = list, JsonRequestBehavior = JsonRequestBehavior.AllowGet};
}

View tarafında ise ekranımıza bir tane button ve bir tane div atıyoruz. Butona basıldığı zaman pie chart ın…

View original post 102 kelime daha

Unit Testte Action’ın Modelin’inin Veri Türüne Erişim

Action methodlarından dönen modelin türü, durumlara göre farklı olabilir. Biz bu action methodundan dönen modelin tipini test etmek isteyebiliriz modelimizde. Bunun için Assert.InstanceOfType test methodu ile actiondan dönen modelin türünü karşılaştırabiliriz. Örnek action methodumuz şöyle olsun:

public ActionResult Urunler()
{
   return View(new List());
}

Test Methodumuz:

[TestMethod]
public void Urunler_Liste()
{
   HomeController controller = new HomeController();
   ViewResult sonuc = controller.Urunler() as ViewResult;
   Assert.IsInstanceOfType(sonuc.Model, typeof(List));
}