محتاجين نعمل Implement للـ Unit of work دا الكلاس اللي بمثابة الـ Unit بتاعنا للـ Work مع الـ Database من خلال الـ DbContext لما بشتغل مع الـ Generic Repository عملنا الـ Add, Update, Delete والمفروض نعمل SaveChanges وهو كل مرة هيعمل SaveChanges وهتبقا أكتر من مرة فأنا عايزة يعملها مرة واحدة بس انا مليش Access على الـ DbContext

Implementation

Interface

نروح في الـ Core ومش فأي فولدر ونعمل Interface بنعمل Property Signature for each and every repository بعرفهم على حسب أنا عندي كام table

public interface IUnitOfWork : IAsyncDsiposable
{
	prop IGenericRepository<Product> ProductsRepo {get; set;}
	prop IGenericRepository<ProductBrand> BrandsRepo {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Order> OrdersRepo {get; set;}
 
	Task<int> CompleteAsync();
}

خد بالك هنا انا معنديش Repository Specific لكل الحاجات دي عشان كدا بستخدم معاها الـ IGenericRepositry

بنعمل كمان الـ Method اللي بتحاكي الـ SaveChanges و الـ Dispose اللي بنورثها من IDisposable

ممكن يبقا عندي أكتر من Unit of work لو انا عندي أكتر من Repository Layer ودا بيبقا لو عندي أكتر من DbContext ودي برضو بتكون لو عندي أكتر من Database

Class

في الـ Repository نعمل الـ UnitOfWork ممكن الـ Cs Properties دي اما Full Property لو انا عايز يبقا ليا Access على الـ Attribute أو Automatic لو مش عايز يبقا ليا Access

تركاية: ممكن تاخد الجزء المتكرر وتعمل Replace لـ {get; set;}

public class UnitOfWork : IUnitOfWork
{
	prop IGenericRepository<Product> ProductsRepo {get; set;}
	prop IGenericRepository<ProductBrand> BrandsRepo {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Order> OrdersRepo {get; set;}
 
	// Ask CLR for Creating Object from DbContext Implicitly
	public UnitOfWork(storeContext dbContext)
	{
		_dbContext = dbContext;
	}
 
	Task<int> CompleteAsync(StoreContext dbContext)
	{
		
	}
 
	public ValueTask DisposeAsync()
	{
		
	}
}

الـ DbContext هو الكلاس اللي مسئول عن التعامل مع الـ Database والـ Unit of Work هو الكلاس اللي مسئول عن التعامل مع الـ Database من خلال الـ DbContext فهيكون عنده Complete اللي هي SaveChanges والـ Dispose اللي بتعمل Dispose

نروح لأي حد بيكلم الـ Generic Repository فبدل ما اعمل Ask لـ Object بيعمل Implement لـ IGenericRepository للبروداكت وواحد للميثود وواحد للأوردر هعمل Implement للـ Class بتاع الـ UnitOfWork وهتلاقي فيه الـ DbContext فيعمله برضو

// This is not in Unit of work
private readonly IBasketRepository _basketRepo;
private readonly IGenericRepository<Product> _productsRepo;
private readonly IGenericRepository<DeliveryMethod> _deliveryMethodsRepo;
private readonly IGenericRepository<Order> _ordersRepo;

وفي الـ Unit of work هيعمل من كل الـ IGenericRepository اللي فيه، هيعمل Object بس هيبقا فاضي فلازم في الكلاس نعمله Initialization

public class UnitOfWork : IUnitOfWork
{
	prop IGenericRepository<Product> ProductsRepo {get; set;}
	prop IGenericRepository<ProductBrand> BrandsRepo {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Product> ProductRepository {get; set;}
	prop IGenericRepository<Order> OrdersRepo {get; set;}
 
 
	// Ask CLR for Creating Object from DbContext Implicitly
	public UnitOfWork(storeContext dbContext)
	{
		_dbContext = dbContext;
		ProductsRepo = new GenericRepository<Product>(_dbContext);
		BrandsRepo = new GenericRepository<ProductBrand>(_dbContext);
		CategoriesRepo = new GenericRepository<ProductCategory>(_dbContext);
		DeliveryMethodsRepo = new GenericRepository<DeliveryMethod>(_dbContext);
		OrderItemsRepo = new GenericRepository<OrderItem>(_dbContext);
		OrdersRepo = new GenericRepository<Order>(_dbContext);
	}
 
	Task<int> CompleteAsync(StoreContext dbContext)
	{
		
	}
 
	public ValueTask DisposeAsync()
	{
		
	}
}

بس الحل دا غلط لأني دايما لما هكلمه هيعمل Object فيه كل الـ Repositories بس المفروض أعمل Create لكل واحدة لما يتطلب بس

فالمفروض امسح دا والحل اني اسيبها زي ما هي وهتبقا بـ Null ولما احتاج حاجة اعمل Object واحطه فيها

بس احنا مش هنعمل الحل اللي فوق دا بس دلوقتي ممكن نعمل Array برضو، زي مثلًا لو عندي 5 أرقام فالأحسن اخزنها في Array

فنروح نعدل الـ Interface بتاعنا هنعمل فيها Method لما يطلب Generic Repository من الـ Order يروح يعملهاله وهكذا

public interface IUnitOfWork : IAsyncDisposable 
{
	IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity;
	Task<int> CompleteAsync();
}

نروح نعملها Implement في الـ Class

public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity
{
	
}

المفروض لو نده على الكلاس دي مثلًا Repository<Order> فهي هترجعله object من كلاس الـ Generic Repository بتاع الـ Order جه بعدها بشوية في نفس الكود او الريكويست طلب برضو نفس الحوار فهيعمل واحد جديد، بس كدا مش منطقي فأنا محتاج أخزن الـ Object اللي يطلبه عشان لو طلبه مرة كمان أرجعهوله فالحل اني اعمل Cs Generic List & Dictionary وهو عبارة عن Key, Value الـ Key هتبقا String وهي اسم نوع الـ Repository والـ Value هتكون GenericRepository

private Dictionary<string, GenericRepository<BaseEntity>> _repositories;

والمفروض أعمل Initialize واحطه في الـ Ctor لأنه اول ما ينادي على الـ Unit of Work معناها انه عايز يعمل Repostiories

// class
private Dictionary<string, GenericRepository<BaseEntity>> _repositories;
// ctor
public UnitOfWork(StoreContext dbContext)
{
	_dbContext = dbContext;
	_repositories = new Dictionary<string, GenericRepository<BaseEntity>>();
}
 
public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity
{
	var key = typeof(TEntity).Name; // Order
	if(!_repositories.ContainsKey(key))
	{
		var repository = new GenericRepository<TEntity>(_dbContext) as GenericRepository<BaseEntity>;
		_repositories.Add(key, repository);
	}
	return _repositories[key] as GenericRepository<TEntity>;
}

ممكن استخدم Hashtable واريح نفسي من اني اعمل Casting كل شوية لأنها معمولة قبل الـ Generic فهي زي الـ Dictionary بس الفكرة ان الـ Key, Value الاتنين بيبقوا Object بس متستخدمش الـ Hashtable لو هيحصل Boxing و Unboxing

private Hashtable _repositories;
 
public UnitOfWork(StoreContext dbContext)
{
	_dbContext = dbContext;
	_repositories = new Hashtable();
}
 
public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : BaseEntity
{
	var key = typeof(TEntity).Name; // Order
	if(!_repositories.ContainsKey(key))
	{
		var repository = new GenericRepository<TEntity>(_dbContext);
		_repositories.Add(key, repository);
	}
	return _repositories[key] as GenericRepository<TEntity>;
}
 
// Continue
public async Task<int> CompleteAsync()
	=> await _dbContext.SaveChangesAsync();
 
public async ValueTask DisposeAsync()
	=> await _dbContext.DisposeAsync();