.Net Core, EF Core ve Dependency Injection ile Uçtan Uca Service ve Business Katmanı
Selamlar,
Bu makalede, uçtan uca .Net Core bir projede, service, business ve repostory katmanın nasıl olması gerektiği hakkında tavsiyelerde bulunacağım.
Entity Katmanı:
Öncelikle örnek amaçlı .Net Core Console Application aşağıdaki gibi oluşturulur. Bu yazıda Database First kullanılarak, Northwind database’inden faydalanılmıştır.
1 |
dotnet new console -o Entity |
Sıra, var olan bir SQL DB’den, DBContext ve Model sınıflarını oluşturmaya geldi. Açılan Entity projesine, EntityFrameworkCore’a ait SqlServer, SqlServer.Design ve Tools kütüphaneleri aşağıdaki gibi eklenir.
1 2 3 |
dotnet add package Microsoft.EntityFrameworkCore.SqlServer.Design; dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools |
Bu uygulamada, VM makinadaki bir Sql Server’a bağlanılıp, Northwind DB’sine ait NorthwindContext sınıfı ve Pocoları aşağıdaki komut ile otomatik olarak oluşturulur. .Net Core projesinde bu işlemin yapılabileceği bir arayüz malesef henüz yoktur. Belki 3th party toollar olabilir. Ama bu makalede, aşağıdaki komut satırılı ile yetinilmiştir. İlgili komut satırı çalıştırıldıktan sonra, proje içinde “Models/DB” şeklinde klasörler oluşturulmuş ve ilgili DBContext ve Poco dosyalar buraya konulmuştur. Bir Sql Server’a uzaktan erişmek için yapılması gerekenler, bu makaleden okunabilir.
1 |
dotnet ef dbcontext scaffold "Server=tcp:10.211.55.9,1433;Database=Northwind; User ID=*****; Password=*****;" Microsoft.EntityFrameworkCore.SqlServer --output-dir Models/DB |
Diyelimki otomatik olarak oluşturulan tüm Contexler, ortak bir “BaseEntity()” sınıfından türetilsin. Bunun için ayrı bir partial class’ın yapılması gerekmektedir. Nedeni DB’de eğer bir değişiklik olur ise, tüm DB context’in yeniden oluşturulması yani migration yapılması gerekmektedir. Bu durumda var olan pocolar üzerinde yapılan değişikliklerin silinmemesi için, partial yani harici sınıflar oluşturulmalıdır.
PartialEntity/PartialEntites: Aşağıda görüldüğü gibi tüm Pocolar aynı BaseEntity’den türetilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
namespace Entity.Models.DB { public partial class Categories : BaseEntity { } public partial class Employees : BaseEntity { } public partial class EmployeeTerritories : BaseEntity { } public partial class Orders : BaseEntity { } public partial class OrderDetails : BaseEntity { } public partial class Products : BaseEntity { } public partial class Region : BaseEntity { } public partial class Shippers : BaseEntity { } public partial class Suppliers : BaseEntity { } public partial class Territories : BaseEntity { } } |
BaseEntity: Aşağıda görüldüğü gibi tüm DbContextlere, “WriteLog()” adında bir method eklenmiştir. Ve kullanıldığı zaman, ekrana kullanım zamanı log olarak basılmaktadır. DB’de karşılığı olmayan kolonların, [NotMapped] olarak işaretlenmesi gerekmektedir. Aksi takdirde hata alınır. Yukarıdaki resimde, bir context’in kullanım anında, aşağıdaki log komut’u çağrılmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; using System.ComponentModel.DataAnnotations.Schema; namespace Entity.Models.DB { public abstract class BaseEntity { private DateTime dateTime; [NotMapped] public DateTime UsedTime { get { this.dateTime = DateTime.Now; return dateTime; } set { } } public void WriteLog() { Console.WriteLine("".PadRight(40, '*')); Console.WriteLine("ATAM SENİN AÇTIĞIN YOLDA İLERLİYORUZ. SADECE 10 KASIM DEĞİL HER ZAMAN AKLIMIZDASIN!"); Console.WriteLine($"UseTime: {UsedTime.ToLongDateString()}"); Console.WriteLine("".PadRight(40, '*')); } } } |
Dikkat edilmesi gereken bir husus da, Northwind databasein’deki Viewlar, az önce belirtilen komut ile otomatik olarak DBContext altına alınamazlar. Bunun için ilgili viewlar, NorthwindContext sınıfına partial olarak aşağıdaki gibi eklenmelidirler.
PartialNorthwindContext: Aşağıda Northwind DB’sine ait tüm viewlar değil, örnek amaçlı sadece bir kaç view eklenmiştir.
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 32 33 34 35 36 37 38 39 40 41 42 43 |
using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; namespace Entity.Models.DB { public partial class NorthwindContext : DbContext { public virtual DbSet<Alphabeticallistofproducts> Alphabeticallistofproducts { get; set; } public virtual DbSet<CurrentProductList> CurrentProductList { get; set; } public virtual DbSet<SalesbyCategory> SalesbyCategory { get; set; } } public class Alphabeticallistofproducts : BaseEntity { [Key] public int ProductID { get; set; } public string Productname { get; set; } public int? SupplierID { get; set; } public int? CategroyID { get; set; } public string QuantityPerUnit { get; set; } public decimal UnitPrice { get; set; } public int? UnitsInStock { get; set; } public int? UnitsOnOrder { get; set; } public int? ReorderLevel { get; set; } public int? OperationId { get; set; } public bool Discontinued { get; set; } public string CategoryName { get; set; } } public class CurrentProductList : BaseEntity { [Key] public int ProductID { get; set; } public string Productname { get; set; } } public class SalesbyCategory : BaseEntity { [Key] public int CategoryID { get; set; } public string Categoryname { get; set; } public string ProductName { get; set; } public decimal ProductSale { get; set; } } } |
Şimdi sıra geldi tüm sınıflar için ortak bir Repository sınıfının yapılmasına:
Öncelikle IRepository Interface’i aşağıdaki gibi oluşturulur:
IRepository: Genel bir Repostory katmanında olması gereken tüm malzemeler :), burada tanımlanmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Entity.Models.DB; namespace Northwind.Repository { public interface IRepository<T> where T : BaseEntity { IQueryable<T> Table { get; } IQueryable<T> TableNoTracking { get; } T GetById(object id); void Insert(T entity); void Insert(IEnumerable<T> entities); void Update(T entity); void Update(IEnumerable<T> entities); void Delete(T entity); void Delete(IEnumerable<T> entities); IQueryable<T> IncludeMany(params Expression<Func<T, object>>[] includes); IEnumerable<T> GetSql(string sql); } } |
GeneralRepository: Tüm sınıflarda ortak olarak yapılan işler, “Generic <T>” tipinde tanımlanmış ve işlem yapılacak entity, GeneralRepository sınıfının Constructorın’da atanmıştır. Böylece, her bir entity için yapılabilecek CRUD işlemler, tekrar tekrar yazılmamıştır.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Entity.Models.DB; using Microsoft.EntityFrameworkCore; namespace Northwind.Repository { public class GeneralRepository<T> : IRepository<T> where T : BaseEntity { private readonly NorthwindContext _context; private DbSet<T> _entities; public GeneralRepository(NorthwindContext context) { _context = context; _entities = context.Set<T>(); } public virtual T GetById(object id) { return Entities.Find(id); } /// <summary> /// Insert entity /// </summary> /// <param name="entity">Entity</param> public void Insert(T entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); _entities.Add(entity); _context.SaveChanges(); } /// <summary> /// Insert entities /// </summary> /// <param name="entities">Entities</param> public virtual void Insert(IEnumerable<T> entities) { if (entities == null) throw new ArgumentNullException(nameof(entities)); foreach (var entity in entities) Entities.Add(entity); _context.SaveChanges(); } /// <summary> /// Update entity /// </summary> /// <param name="entity">Entity</param> public void Update(T entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); _context.SaveChanges(); } /// <summary> /// Update entities /// </summary> /// <param name="entities">Entities</param> public virtual void Update(IEnumerable<T> entities) { if (entities == null) throw new ArgumentNullException(nameof(entities)); _context.SaveChanges(); } /// <summary> /// Delete entity /// </summary> /// <param name="entity">Entity</param> public void Delete(T entity) { if (entity == null) throw new ArgumentNullException(nameof(entity)); Entities.Remove(entity); _context.SaveChanges(); } /// <summary> /// Delete entities /// </summary> /// <param name="entities">Entities</param> public virtual void Delete(IEnumerable<T> entities) { if (entities == null) throw new ArgumentNullException(nameof(entities)); foreach (var entity in entities) Entities.Remove(entity); _context.SaveChanges(); } public IQueryable<T> IncludeMany(params Expression<Func<T, object>>[] includes) { return _entities.IncludeMultiple(includes); } public IEnumerable<T> GetSql(string sql) { return Entities.FromSql(sql); } /// <summary> /// Gets a table /// </summary> public virtual IQueryable<T> Table => Entities; /// <summary> /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only /// operations /// </summary> public virtual IQueryable<T> TableNoTracking => Entities.AsNoTracking(); /// <summary> /// Entities /// </summary> protected virtual DbSet<T> Entities => _entities ?? (_entities = _context.Set<T>()); } } |
Exensions: Sorgulamaya eklenebilecek birden fazla joinli tablo için, aşağıdaki include extension’ı size örnek amaçlı yazılmıştır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; public static class Extensions { public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes) where T : class { if (includes != null) { query = includes.Aggregate(query, (current, include) => current.Include(include)); } return query; } } |
Şimdi sıra geldi DBContext’in ve diğer serviceslerimizin, Dependency Injection ile .Net Core bir projede daha en baştan ayağa kaldırılmasına. .Net Core ile Dependency Injection, birlikte yerleşik olarak gelmektedir. Bu neden ile ayrıca bir kütüphane ihtiyaç duyulmamaktadır.
Startup.cs: Aşağıdaki kod ile “NorthwindContext“in, diğer Controllerların constructor’ında tanımlanarak kullanılması sağlanmıştır. Ayrıca diğer tanımlı serviceslerin, örneğin “GeneralRepository“‘inin de, burada tanımlanarak projenin diğer yerlerinde kullanılmasına olanak sağlanmıştır. Son olarak DbContext haricinde Memory Cache Manager, LogService gibi yazılabilecek custom servicesler, gene Dependency Injection ile ayağa kaldırılması için, Startup.cs altında aşağıdaki gibi tanımlanmaktadır.
1 2 3 4 |
services.AddDbContext<NorthwindContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Your Connection"), opt => opt.UseRowNumberForPaging())); services.AddScoped(typeof(IRepository<>), typeof(GeneralRepository<>)); services.AddSingleton<ICacheManager, MemoryCacheManager>(); services.AddTransient<ILogService, LogService>(); |
AddScoped,AddTransient,AddSingleton Nedir: Yukarıda görüldüğü gibi, yaşam döngülerine göre, nesnelerin tanımlamaları farklılık göstermektedir.
- AddScoped: Üretilme şekli, bir request içinde hep aynıdır. Farklı web requestler’de farklı instancelar oluşturulur. Aynı using() kullanımında olduğu gibi. Şahsen ben Repository sınıflarını bu şekilde ayağa kaldırmayı tercih ediyorum. Kısaca her istekde yaratılıp, dispose edilirler.
- AddSingleton : Tüm application zamanı boyunca bir kere yaratılıp, her requestde aynı Instance’ın kullanılmasına olanak sağlanır. Mesela herkes için ortak kullanılan nesneler, Singleton yapılar, buna çok uygundur. Örneğin Cache manager gibi.
- AddTransient : Transient nesneler, her zaman farklıdır. Her nesne çağrımında, yani her bir controller veya her bir service için yeni oluşturulurlar. En hızlısı ve thread safety açısından en güvenlisidir(no locks, pooled db connection gibi..). Örneğin LogServiceler bu şekilde kullanılabilirler. Kötü yani, çok fazla deep dive object oluştururlar. Daha çok monolitik büyük dependency ağacı olan yapılarda tercih edilmelidirler.
Services: Şimdi sıra geldi WebApi ile Entity arasındaki Bussines katmanın yazılacağı Service katmanına.
Tüm servislerden dönecek tip aynı olmalıdır. Aşağıda örnek olabilecek genel bir, dönüş tipi sınıfı tanımlanmıştır.
Service.Response.cs:
- “HasExceptionError” : Hata var mı.
- “ExceptionMessage” : Hata mesajlarının yazıldığı kısım.
- “Entity” : Tek bir Entity sonucun döndüğü kısım.
- “List”: Çoklu sonucun tanımlandığı kısım.
- “IsValid” : Update, Insert işlemler için validation sonucu.
- “IsSuccessful” : İşlem sonucunun tutulduğu boolen kısım.
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 32 33 |
using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; namespace Northwind.Repository { [Serializable] public class ServiceResponse<T> { public bool HasExceptionError { get; set; } [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string ExceptionMessage { get; set; } public IList<T> List { get; set; } [JsonProperty] public T Entity { get; set; } public int Count { get; set; } public bool IsValid => !HasExceptionError && !ValidationErrorList.Any() && string.IsNullOrEmpty(ExceptionMessage); public bool IsSuccessful { get; set; } public ServiceResponse(HttpContext context) { List = new List<T>(); } } } |
Örnek bir Service’den geri dönülecek Order ViewModel’, aşağıdaki gibi tanımlanır:
OrderModel: Ekrana basılacak ViewModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
namespace Northwind.Repository { public class OrderModel { public int Id { get; set; } public string ContactName { get; set; } public string Country { get; set; } public string City { get; set; } public string Region { get; set; } public OrderModel(){} } } |
IOrderService.cs: Önce IOrderService interface’i ile yapılacak işler aşağıdaki gibi tanımlanır.
1 2 3 4 5 6 7 8 9 10 |
namespace Northwind.Repository { public interface IOrderService { ServiceResponse<int> Insert(OrderModel model); ServiceResponse<int> Update(OrderModel model); ServiceResponse<bool> Delete(OrderModel model); ServiceResponse<List<OrderModel>> List(); } } |
OrderService: WebApi services’inden çağrılan, bussines’ın döndüğü kısımdır. Aşağıdaki resim geri dönülen yapının console’a basıldığı result değeridir.
Not: Webapi controller’a bussines konmadan, sadece services ya da GeneralRepostory katmanı buradan çağrılır.
- “public class OrderService : IOrderService” : IOrderServices’inden implemente edilerek, istenen 4 methodun tanımlanması zorunlu hale getirilir. Bu örnekte sadece “List()” methodu detaylıca kodlanmıştır.
- “public CustomerService(IRepository<Order> orderRepository)” : Dependency Injection için, tüm pocolarda kullanılacak “IRepository” burada tanımlanmıştır.
- “IRepository<Order>” : Bu tanımlama ile orderRepostory tanımlanmıştır.
- “List<>” : Methodunda Order, Order’a bağlı Customer ve Employee, Employee’ye bağlı EmployeeTerritories kayıtlarından 3 tane çekilmiştir.
- “var response = new ServiceResponse<OrderModel>(null)” : her servis için geçerli olan ServiceResponse dönüş tipi tanımlanmıştır.
- “var query= _orderRepository.Orders.Table.Include(or => or.Customer) .Include(or => or.Employee).ThenInclude(or => or.EmployeeTerritories).Take(3)”: İlgili tablolardan query çekilmiştir.
- “response.Count = query.Count()” : Toplam kayıt sayısı “ServiceResponse” dönüş tipine ait “Count” değerine atanmıştır.
- “var list = query.ToList()” : çekilen tüm kayıtlar bir list’e atanmıştır.
- “foreach (var order in list) { var viewModel = new OrderModel{}”: Gezilen her kayıt “OrderModel” tipindeki ViewModel’e atanmıştır.
- “response.List.Add(viewModel):Tüm servislerin geri dönüş tipi olan “ServiceResponse” modelnin “List” property’si “List<OrderModel>” ile doldurulmuştur.
- “order.Customer.WriteLog()” : Burada amaç ilgili modelin DB karşılığından farklı olarak BaseEntity‘den dönen “WriteLog()” methodunun çağrılabileceğidir. İlgili method tüm Entitylerde inheritancedan dolayı mevcuttur.
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
namespace Northwind.Repository { public class OrderService : IOrderService { #region Fields private readonly IRepository<Order> _orderRepository; #endregion #region Ctor public OrderService(IRepository<Order> orderRepository) { _orderRepository = orderRepository; } #endregion #region Methods public ServiceResponse<OrderModel> List(int rowCount) { var response = new ServiceResponse<OrderModel>(null); var query= _orderRepository.Orders.Table.Include(or => or.Customer) .Include(or => or.Employee).ThenInclude(or => or.EmployeeTerritories).Take(rowCount); response.Count = query.Count(); var list = query.ToList(); foreach (var order in list) { var viewModel = new OrderModel { Id = order.Customer.Id, ContactName = order.Customer.ContactName, Country = order.Employee.Country, City = order.Employee.City, Region = order.Employee.Region, }; response.List.Add(viewModel); order.Customer.WriteLog(); } return response; } //Örnek amaçlı tanımlanmadı. Interfaceden türetildiği için tanımlanması gerekmektedir. ServiceResponse<int> Insert(OrderModel model){}; ServiceResponse<int> Update(OrderModel model){}; ServiceResponse<bool> Delete(OrderModel model){}; } } |
Yukarıdaki query’nin SqlProfile karşılığı aşağıdaki gibidir:
1 2 3 4 5 6 |
SELECT TOP(3) [or.Customer].CustomerID, [or.Customer].[ContactName], [or.Employee].City, [or.Employee].Country, [or].[ShipName], [or.Employee].[Region] FROM [Orders] AS [or] LEFT JOIN [Employees] AS [or.Employee] ON [or].[EmployeeID] = [or.Employee].[EmployeeID] LEFT JOIN [Customers] AS [or.Customer] ON [or].[CustomerID] = [or.Customer].[CustomerID] ORDER BY [or.Employee].[EmployeeID] |
Dikkat: Unutulmaması gereken bir husus da, yukarıda yazılan OrderService’in Dependency Injection için Startup.cs’de aşağıdaki gibi tanıtılmasıdır.
Startup.cs (Ekleme):
1 |
services.AddSingleton<IOrderService, OrderService>(); |
Son olarak, geldik bu servisin bir WebApi service’i tarafından çağrılmasına.
OrderController: Aşağıdaki WebApi servisi, tüm makalenin özetlendiği kısımdır. WebApi servisinde, hiçbir bussines işlem yapılmamış, sadece data işlemleri için dışarısı ile sistem arasında Proxy görevi görmüştür. Sistem içinden logService, tokenService, notifyService, errorService gibi daha birçok yapı çıkarılarak, sizin esas infrastructure’ı anlamanız amaçlanmıştır.
- “IRepository<Order> _orderRepository” : Özel bir işlem olmadan sadece ID’ye göre datanın çekildiği, yani bussines’ın olmadığı “GetOrderById()” methodu için önceden yukarıda global tanımlanan Repostory kullanılmıştır.
- “GetOrders(int rowCount)” : İçinde bussines’ın olduğu Order, Customer ve Employee’nin dönüldüğü bir methoddur. Bundan dolayı “IOrderService” kullanılmıştır. Kısaca içinde bussines geçen yapılar, yukarıda yazılan custom Service katmanındaki methodları kullanmaktadır. İlgili methoddan toplu kayıt dönüldüğü için, “ServiceResponse.List” propertysi doldurulur. Ayrıca paging amaçlı “response.Count” propertysi doldurulur.
- “GetOrders()” ve “GetOrderById()” methodları makalenin başında global tanımlanan “ServiceResponse” tipinde bir sınıf dönmektedir. Böylece view her zaman hangi tipde bir model alacağını bilmektedir.
- “GetOrderById()” : Methodu tek bir kayıt döndüğü için, “Entity” propertysi doldurulur.
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 32 33 34 |
namespace Northwind.WebApi.Controllers { [Route("api/order")] public class OrderController : Controller { private readonly IRepository<Order> _orderRepository; private readonly IOrderService _orderService; public OrderController(IRepository<Order> orderRepository, IOrderService orderService) { _orderRepository = orderRepository; _orderService = orderService; } [HttpGet("GetOrder/{rowCount}")] public ServiceResponse<OrderModel> GetOrders(int rowCount) { var response = new ServiceResponse<orderModel>(HttpContext); response.List = _orderService.List(rowCount).List.ToList(); response.IsSuccessful = true; response.Count = response.List.Count; return response; } [HttpGet("GetOrderById/{id}")] public ServiceResponse<orderModel> GetOrderById(int id) { var response = new ServiceResponse<orderModel>(HttpContext); response.Entity = _orderRepository.Table.FirstOrDefault(s => s.Id == id); response.IsSuccessful = true; return response; } |
Geldik bir makalenin daha sonuna. Bu makalede tepeden tırnağa WebApi, Services ve Entity katmanlarının nasıl yaratılması gerektiği, ve dikkat edilmesi gereken bazı hususlara parmak basmak istedim. Her developer’ın farklı bir yoğurt yiyişi vardır:) Genişletilebilir, gereksiz kod tekrarlarından uzak, her bir entity için farklı dönüş tiplerinin olmadığı, Entity üzerindeki özelleştirmelerin birebir üzerinden yapılmadığı, OOP gereği tüm entitylerin base bir entity’den türetildiği kodlar dilerim.
Not: İstenir ise bu makaleden farklı olarak, herbir entity için farklı Service katmanı yaratılabilir. Bu makalede, bussines gömülmeyen entityler için, “GeneralRepository” katmanının kullanılmasına rağmen, siz isterseniz WebApi servisini sadece Service katman ile muhatap edebilirsiniz.
Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Bora abi sen adamın dibisin ya çok teşekkürler. Emeklerine eline sağlık
Hocam makele için teşekkürler.
UI katmanı olan web api katmanına orderRepository i alıp getorderbyid metodunu doldurmak bana pek doğru gelmedi. Şu sebeple ben yarın bunu mvc katmanına da vermek istersem gidip orada da getorderbyid metodunu repository den doldurmam gerekir. Aynı kodu iki defa yazmış olurum. İçinde her ne kadar business olmasa bile data katmanından datayı UI da almak doğru mu. Ben business servisinde alır UI katmanlarına business taşıyorum. Sizce bu yaklaşım nasıl olmalı.
İyi günler hocam,
Ben de projemde repository kullanıyorum. Fakat Command işlemleri T tipinde return içeriyor. Sizin yapınızda return yok.
Merak ettiğim konu, mesela ben ürün eklerken yeni bir kategori oluşturmak istiyorum aynı pencerede. DTO olarak ürün adı, fiyatı, kategori adı aldım. ProductService içindeki Add metodunda sırasıyla kategoriyi ekle, dönen kategori id’yi al ve bu kategori id’ye ürünü ekle diyorum. Void olduğunda bu işlemi nasıl yapabilirim ?
Hocam saygılar. OrderController’da liste çekerken servisten, tek bir nesne çekerken repository’dan almışsın bunun bir sebebi var mı ?
Selamlar Samet;
Valla güzel yakalamışın. Hayır özel bir nedeni yok. Amaç kod çeşitliliğini göstermek.
Hoşçakal…
İyi çalışmalar..
Hocam emeğinize sağlık çok güzel bir anlatım olmuş
Teşekkürler Oğuzhan :)
olmuyor ne yaptıysam olmuyor yoruldum ya işten patrondan yöneticilerden buktım illallah geldi teknolojininde allah belasını versin repositorininde bora abi teşekkürler
Yusuf Selam,
Ne yaptın böyle. Bence daha basitten başla. Karışıktan direk başlayınca daralmışın sakin. Önce OOP’dan başla.
Hatta bence önce bir kahve al git bir parkda otur. Öyle başla :)
Kolay Gelsin.
yav yusuf alem adamsın olum gece gece güldürdün bizi sabah ofiste çözeriz.
Örnek uygulamanın kodları mevcut mu?
IRepository scoped olarak tanımlandığı için services.AddSingleton(); kısmı da scoped olarak tanımlanmalı onun haricinde güzel yazı emeğinize sağlık :)
Teşekkürler.
Bakıp inceleyeceğim.
Bora Abi sen ne mükemmel bir insansın. Gerçekten anlatırken bile o karşı tarafı ezmeden, güzel dille, DotNet Konf’ta bahsettiğin iletişim becerisi ile ne güzel anlatıyorsun. Ellerine sağlık. Çok teşekkür ederim.
Ben teşekkür ederim :) Çok naziksiniz..
Hocam iyi günler, Repository içerisinde update metdolarını tanımlarken doğrusdan savechanges() metodunu çağırmışsınız. Ben şimdiye kadar update yaparken, “_context.Entry(entity).State = EntityState.Modified;” veya “_context.Update(entity);” komutlarını kullanıyorudm. Bu kullanımlar arasında fark var mı ya da en doğru seçenek hangisidir? Teşekkür ederim. Saygılar.
Selamlar,
Evet savechanges() methodu ile _context.SaveChanges() işlemi yapılıyor. En kestirme yolu bu. Sen işler biraz uzatıyorsun. Öncelike eğer uygulamanda Entity’de .AsNoTracking() var ise senin “EntityState.Modified” işe yaramaya bilir. Sen önce benim entitym değişti diye işaretliyorsun. Sonra verdiğin entitynin güncellenmesini istiyorsun.Ben entity’de yapılan değişikliği direk kaydet diyorum. Bende .AsNoTracking() tabi ki kapalı.
İyi çalışmalar.
Hocam Merhaba,
Makale için teşekkür ederim. Ellerinize sağlık. Genericrepository kullanarak transaction yönetimini nasıl sağlayabiliriz.
Selamlar,
Genelde Transaction yönetiminin, Servis katmanında yapılmasını tavsiye ediyorlar. Amma illaki General Repository’de yapacak iseniz, Services katmanında oluşturduğunuz transaction’ı, Repository katmanına parametre olarak gönderin :) Ben öyle çözdüm :)
İyi çalışmalar.