AngularJS, Asp.net Mvc 5 ve Web Api ile Crud İşlemlerinin Gerçekleştirilmesi

  • Visual studiomuzu başlatıyoruz.
  • Create a new project.
  • Web template seçiyoruz ve ardından ASP.NET Web Application seçiyoruz.
  • Projeye isim verip, projenin oluşturalacağı dizini seçiyoruz.
  • Next tıklıyoruz.
  • MVC seçiyoruz ve code referanslardan Web Apiyi seçip projemize dahil ediyoruz.

MVC template

Kolay bir örnek olması açısından education adında bir entity type oluşturuyorum.

public partial class Education
    {
        public Education()
        {
            this.Members = new List<Member>();
        }

        public int ID { get; set; }
        public string Education1 { get; set; }
        public virtual ICollection<Member> Members { get; set; }
    }

Yeni bir context class oluşturalım.

public class EducationContext : DbContext
{
     public EducationContext()
         : base("name=DefaultConnection")
     {
         base.Configuration.ProxyCreationEnabled = false;
     }
     public DbSet<Education> Education{ get; set; }
     public DbSet<Member> Member{ get; set; }
}

Şimdi ise tüm entity classlar için tek tek repository oluşturmaktansa Generic Repository oluşturacağız ve her bir poco classlar için tek bir repository yeterli olacak.

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
    {
        private readonly EducationContext _context;
        private readonly DbSet<TEntity> _dbSet;

        public GenericRepository(EducationContext context)
        {
            _context = context;
            _dbSet = context.Set<TEntity>();
        }

        /// <summary>
        /// Tüm kayıtlar.
        /// </summary>
        /// <returns></returns>
        public virtual IQueryable<TEntity> GetAll()
        {
            return _dbSet;
        }

        /// <summary>
        /// Kayıt bul.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual TEntity Find(int id)
        {
            return _dbSet.Find(id);
        }

        /// <summary>
        /// Kayıt ekle.
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Insert(TEntity entity)
        {
            _dbSet.Add(entity);
            _context.SaveChanges();
        }

        /// <summary>
        /// Kayıt güncelle.
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Update(TEntity entity)
        {
            _dbSet.Attach(entity);
            _context.Entry(entity).State = EntityState.Modified;
            _context.SaveChanges();
        }

        /// <summary>
        /// Kayıt sil.
        /// </summary>
        /// <param name="entity">Kayıt</param>
        public virtual void Delete(TEntity entity)
        {
            if (_context.Entry(entity).State == EntityState.Detached)
            {
                _dbSet.Attach(entity);
            }
            _dbSet.Remove(entity);
            _context.SaveChanges();
        }
    }

Generic Repository’i türettiğimiz IGenericRepository interfacesini oluşturalım.

public interface IGenericRepository where TEntity : class
{
/// Tüm kayıtlar.
IQueryable GetAll(); ///
/// Kayıt bul. ///
TEntity Find(int id); ///
/// Kayıt ekle. ///
void Insert(TEntity entity); ///
/// Kayıt güncelle. ///
void Update(TEntity entityToUpdate); ///
/// Kayıt sil. ///
void Delete(TEntity entityToDelete);
}

Web api controller oluşturmadan önce education bilgileriyle işlem yapabilmek için service oluşturalım.

public class EducationService : IEducationService
    {
        private readonly IGenericRepository<Education> _educationRepository;

        public EducationService(IGenericRepository<Education> educationRepository)
        {
            _educationRepository = educationRepository;
        }
        public IQueryable<Education> GetAll()
        {
            var educations = _educationRepository.GetAll();
            return educations;
        }
        public Education Find(int id)
        {
            var educations = _educationRepository.GetAll().FirstOrDefault(a => a.ID == id);
            return educations;
        }

        public void Insert(Education education)
        {
            _educationRepository.Insert(education);
        }

        public void Update(Education education)
        {
            _educationRepository.Update(education);
        }

        public void Delete(Education education)
        {
            _educationRepository.Delete(education);
        }
    }

Şimdi ise servisimizin türetildiği IEducationService interfacesini oluşturalım

public interface IEducationService
{
IQueryable GetAll(); ///
Education Find(int id); ///
void Insert(Education education);
void Update(Education education);
void Delete(Education education); 

}

Şimdi sıra geldi web apiyi oluşturmaya. Web api controllerin içerisinde tüm transactionları gerçekleştirmek için tüm http methodları kullanacağız. Bunlar: GET, POST, PUT, DELETE methodlarıdır.

public class EducationApiController : ApiController
    {
        private readonly IEducationService service =  ServiceFactory.Get<IEducationService>();

        [HttpGet()]
        //[Route("api/educations")]
        public IEnumerable<Education> Get()
        {
                var list = service.GetAll().ToList().AsEnumerable();
                return list;
            
        }
        //[Route("api/education/{id}")]
        public Education Get(int id)
        {
            try
            {
                var education = service.GetAll().FirstOrDefault(p => p.ID == id);
                if(education == null)
                {
                    throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
                }
                return education;
            }
            catch(Exception ex)
            {
                return null;
            }
        }

        public HttpResponseMessage Post(Education education)
        {

                try
                {
                    var education1 = new Education();
                    service.Insert(education);

                    HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, education1);
                    response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = education1.ID }));
                    return response;
                }
                catch(Exception ex)
                {
                    return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
                    //unitofwork.Rollback();
                }
                
        }

        public HttpResponseMessage Put(Education education)
        {
            if (!ModelState.IsValid)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }

            try
            {
                service.Update(education);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }

            return Request.CreateResponse(HttpStatusCode.OK);
        }

        public HttpResponseMessage Delete(int id)
        {
            var education1 = new Education();
            education1 = service.Find(id);
            if (education1 == null)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            try
            {
                service.Delete(education1);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex);
            }
            return Request.CreateResponse(HttpStatusCode.OK, education1);
        }


    }

Web api controllerda dependency injection kullanamadığımız için Service Factory adında generic bir helper ekleyeceğiz ve bu helper web api controllera Education Servicein enjekte edilmesini sağlayacak.

public static class ServiceFactory
    {
        public static THelper Get<THelper>()
        {
            if (HttpContext.Current != null)
            {
                var key = string.Concat("factory-", typeof(THelper).Name);
                if (!HttpContext.Current.Items.Contains(key))
                {
                    var resolvedService = DependencyResolver.Current.GetService<THelper>();
                    HttpContext.Current.Items.Add(key, resolvedService);
                }
                return (THelper)HttpContext.Current.Items[key];
            }
            return DependencyResolver.Current.GetService<THelper>();
        }
    }

Şimdi ise Manage Nuget Package Manager kullanarak Angular JS kütüphanelerini solutiona ekleyelim.

search AngularJS

Şimdi yeni bir javascript dosyası oluşturalım ve angular functionları ekleyelim. Bu angular controllerının adına educationController koyduk. İlerde oluşturacağımız view sayfasında controllerı bu isimle çağıracağız.

var myApp = angular.module('myApp', []).controller('educationController',
['$scope', '$http', function ($scope, $http) {
    $scope.loading = true;
    $scope.addMode = false;

    function GetEducations()
    {
        $http.get('/api/EducationApi/').success(function (data) {
            $scope.educations = data;
            $scope.loading = false;
        })
            .error(function () {
                $scope.error = "An Error has occured while loading posts!";
                $scope.loading = false;
            });
    }
    //Used to display the data
    GetEducations();

    $scope.toggleEdit = function () {
        this.education.editMode = !this.education.editMode;
    };
    $scope.toggleAdd = function () {
        $scope.addMode = !$scope.addMode;
    };

    //Used to save a record after edit
    $scope.save = function () {
        $scope.loading = true;
        var educate = this.education;
        $http.put('/api/EducationApi/put',  educate).success(function (data) {
            alert("Saved Successfully!!");
            educate.editMode = false;
            $scope.loading = false;
            GetEducations();
        }).error(function (data) {
            $scope.error = "An Error has occured while Saving Friend! " + data;
            $scope.loading = false;

        });
    };

    //Used to add a new record
    $scope.add = function () {
        $scope.loading = true;
        $http.post('/api/EducationApi/post', this.neweducation).success(function (data) {
            alert("Added Successfully!!");
            $scope.addMode = false;
            $scope.educations.push(data);
            $scope.loading = false;
            GetEducations();
        }).error(function (data) {
            $scope.error = "An Error has occured while Adding Friend! " + data;
            $scope.loading = false;

        });
    };

    //Used to edit a record
    $scope.deleteeducation = function () {
        $scope.loading = true;
        var educationid = this.education.id;
        $http.delete('/api/EducationApi/delete' + educationid).success(function (data) {
            alert("Deleted Successfully!!");
            $.each($scope.educations, function (i) {
                if ($scope.educations[i].ID === educationid) {
                    $scope.educations.splice(i, 1);
                    return false;
                }
            });
            $scope.loading = false;
            GetEducations();
        }).error(function (data) {
            $scope.error = "An Error has occured while Saving Friend! " + data;
            $scope.loading = false;

        });
    };

}]);

Shared klasörü altındaki _Layout.cshtml sayfasını açıp 2 farklı scripti render etmemiz gerekiyor.

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/bundles/angularjs")
    @Scripts.Render("~/bundles/education")

Sıradaki işlemimiz view oluşturup bunun üzerinde çalışmak.



@{
    ViewBag.Title = "EducationIndex";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Education </h2>
<style>
    #mydiv {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 1000;
        background-color: grey;
        opacity: .8;
    }

    .ajax-loader {
        position: absolute;
        left: 50%;
        top: 50%;
        margin-left: -32px; /* -1 * image width / 2 */
        margin-top: -32px; /* -1 * image height / 2 */
        display: block;
    }
</style>
@*<script src="~/Scripts/EducationController.js"></script>*@
<div data-ng-app="myApp">
<div data-ng-controller="educationController" class="container">
    <strong class="error">{{ error }}</strong>
    <div id="mydiv" data-ng-show="loading">
        <img src="~/Images/ajax-loader.gif" class="ajax-loader" />
    </div>
    <p data-ng-hide="addMode"><a data-ng-click="toggleAdd()" href="javascript:;" class="btn btn-primary">Add New</a></p>
    <form name="addEducation" data-ng-show="addMode" style="width:600px;margin:0px auto;">
        <label>Education:</label><input type="text" data-ng-model="neweducation.Education1" required />

        <br />
        <br />
        <input type="submit" value="Add" data-ng-click="add()" data-ng-disabled="!addEducation.$valid" class="btn btn-primary" />
        <input type="button" value="Cancel" data-ng-click="toggleAdd()" class="btn btn-primary" />
        <br /><br />
    </form>
    <table class="table table-bordered table-hover" style="width:800px">
        <tr>
            <th>#</th>
            <td>Education</td>

        </tr>

        <tr data-ng-repeat="education in educations track by $index">
            <td><strong data-ng-hide="education.editMode">{{ education.id }}</strong></td>
            <td>
                <p data-ng-hide="education.editMode">{{ education.education1 }}</p>
                <input data-ng-show="education.editMode" type="text" data-ng-model="education.education1" />
            </td>
            <td>
                <p data-ng-hide="education.editMode"><a data-ng-click="toggleEdit(education)" href="javascript:;">Edit</a> | <a data-ng-click="deleteeducation(education)" href="javascript:;">Delete</a></p>
                <p data-ng-show="education.editMode"><a data-ng-click="save(education)" href="javascript:;">Save</a> | <a data-ng-click="toggleEdit(education)" href="javascript:;">Cancel</a></p>
            </td>
        </tr>
    </table>
    <hr />


</div>
</div>

Şimdi uygulamamızı çalıştıralım.

Ekran Alıntısı1

Ekran Alıntısı2

Ekran Alıntısı3

Ekran Alıntısı4

Bu makalede MVC 5 ile AngularJS ve Web Api ile CRUD işlemleri nasıl yapılır onu öğrendik. Bir dahaki makalede görüşmek dileğiyle..

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.

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

[Blog] Web Geliştiricilerin Visual Studio 2013 Yenilikleri Hakkında Bilmesi Gerekenler

Web Geliştiricilerin Visual Studio 2013 Yenilikleri Hakkında Bilmesi Gerekenler

Mahmut Can Sozeri

Merhaba Arkadaşlar,

Dün Visual Studio 2013 release oldu ve indirilmeye hazır. İşte Visual Studio 2013 için öne çıkan bazı notlar:

  • ASP.NET MVC 5, Razor 3, ASP.NET Web API 2, Entity Framework 6 ve SignalR 2.0 i içeren ASP.NET’in yeni versiyonu
  • Yeni ASP.NET, tek ASP.NET olmasına odaklanıldı yani temel özellikler ve web araçları tüm ASP.NET template leri arasında kullanılabilir. Örneğin ASP.NET MVC Controller’i, Web Forms Application a eklenebilecek
  • Yeni template lerin temeli Visual Studio 2013 Preview’da olduğu ve beklenildiği gibi Bootstrap oldu
  • Yeni Scaffolding sistemi getirildi
  • Yeni identity sistemi getirildi ki bu konu ile ilgili yazmak istediğim oldukça fazla yazı var

Yükleme Sırasında

vs2013_Ultimate

Eğer sadece Web Geliştirecekseniz, yukarıdaki gibi hızlı yükleme için “Microsoft Web Developer Tools” seçebilirsiniz. Diğer geliştirme araçlarınıda kullanacaksanız seçmeniz yeterlidir. Ek olarak eğer “Windows Phone 8.0 SDK” yükleyecekseniz, Windows 8.1 yüklemek zorundasınız.

Yükleme sırasında dikkat edeceksiniz, eğer hızlı bir internetiniz varsa yüklemenizde oldukça…

View original post 745 kelime daha