A Completely Generic C.R.U.D. Repository built on top of Entity Framework. This project provides an abstraction over data access layers, promoting decoupling and simplifying Create, Read, Update, and Delete (CRUD) operations. It is designed to be used with Dependency Injection, making it easier to manage dependencies and test your application.
- Synchronous Operations:
GetAll(params Expression<Func<T, object>>[] navigationProperties): Retrieve all entities, optionally including related entities.GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties): Retrieve entities matching a predicate, optionally including related entities.GetSingle(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties): Retrieve a single entity matching a predicate, optionally including related entities.GetById(long? id, params Expression<Func<T, object>>[] navigationProperties): Retrieve an entity by its ID, optionally including related entities.Add(params T[] items): Add one or more new entities.Update(params T[] items): Update one or more existing entities.Remove(params T[] items): Remove one or more entities.Commit(): Save all pending changes to the database.
- Asynchronous Operations:
GetByIdAsync(long? id, params Expression<Func<T, object>>[] navigationProperties): Asynchronously retrieve an entity by its ID, optionally including related entities.GetSingleAsync(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties): Asynchronously retrieve a single entity matching a predicate, optionally including related entities.CommitAsync(): Asynchronously save all pending changes to the database.
- Eager Loading: Efficiently load related entities (navigation properties) by passing them as arguments to methods like
GetAll,GetList,GetSingle,GetById,GetByIdAsync, andGetSingleAsync. - Unit of Work: The
Commit()andCommitAsync()methods facilitate the Unit of Work pattern by saving all tracked changes within the currentDbContextinstance. - Dependency Injection Ready: Designed to be easily integrated with any Dependency Injection container.
IEntityService<T>: This is the core interface that defines the contract for all repository operations.Trepresents an entity type. It is expected thatTwill be a class that inherits from a common baseEntityclass (e.g.,BLL.Entities.Entity), which typically provides anIdproperty (e.g.,public long? Id { get; set; }).
EntityService<T>: The concrete implementation ofIEntityService<T>. It uses Entity Framework'sDbContextto interact with the database and perform the defined operations.GetDb(Static Class): A helper class. ItsRepository<TEntity>()method can be used to get a repository instance, though it's often preferable to use dependency injection forIEntityService<T>. TheContext()method provides aDbContextinstance, which might be used in scenarios where DI is not fully set up for the DbContext itself or for specific direct context access.- Base
EntityClass: Your domain entities are expected to inherit from a baseEntityclass (e.g.,BLL.Entities.Entity). This base class typically includes a common primary key property, such aspublic virtual long? Id { get; set; }.
- .NET Framework: .NET 4.5 or later (recommended due to
async/awaitand Entity Framework features used). - Entity Framework: EF 6.x.
- A C# project/solution.
Here's a conceptual example of how to use the generic repository pattern in your project.
a. Define your Entity:
Your entities should inherit from a base Entity class.
// Assuming a BLL.Entities namespace and a base Entity class like this:
// namespace BLL.Entities {
// public abstract class Entity { public virtual long? Id { get; set; } /* other common properties */ }
// }
// Example Entity:
// namespace YourProject.Entities // Or BLL.Entities
// {
public class Product : BLL.Entities.Entity // Or your specific base entity if different
{
public string Name { get; set; }
public decimal Price { get; set; }
// Other properties
}
// }b. Define your DbContext:
Create or adapt your existing Entity Framework DbContext.
using System.Data.Entity;
// using YourProject.Entities; // Import your entity namespace
// using BLL.Entities.UserAccounts; // If ApplicationUser is needed (as seen in GetDb.cs)
// using Microsoft.AspNet.Identity.EntityFramework; // For IdentityDbContext if using ASP.NET Identity
// Example DbContext (e.g., SCMSContext from the project or YourProjectDbContext)
public class YourProjectDbContext : DbContext // Or IdentityDbContext<ApplicationUser> if using ASP.NET Identity
{
// Connection string name from App.config/Web.config
public YourProjectDbContext() : base("name=YourConnectionStringName")
{
// Optional: Disable lazy loading if you prefer explicit loading
// this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Product> Products { get; set; }
// Add other DbSets for your entities here
// If not using IdentityDbContext but your GetDb.cs references UserAccounts.ApplicationUser:
// public DbSet<ApplicationUser> Users { get; set; } // Or the specific user type
// Optional: If you are using SCMSContext specific features referenced in GetDb.cs like IdentityDbContext<ApplicationUser>
// and its static Create() method, you might want to replicate that pattern if needed:
// public static YourProjectDbContext Create()
// {
// return new YourProjectDbContext();
// }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Fluent API configurations can go here
}
}c. Configure Dependency Injection (Conceptual Example):
Register IEntityService<T> and your DbContext with your preferred DI container. This example uses generic DI container syntax.
// In your DI container setup (e.g., Startup.cs, Global.asax, or a dedicated DI config class)
// Example using a hypothetical DI container:
// var containerBuilder = new ContainerBuilder();
// Register your DbContext. The lifetime should suit your application (e.g., PerRequest in web apps).
// containerBuilder.RegisterType<YourProjectDbContext>().AsSelf().InstancePerRequest();
// For ASP.NET Core, it might look like:
// services.AddDbContext<YourProjectDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("YourConnectionStringName")));
// Register the Generic Repository
// containerBuilder.RegisterGeneric(typeof(EntityService<>)).As(typeof(IEntityService<>)).InstancePerRequest();
// For ASP.NET Core:
// services.AddScoped(typeof(IEntityService<>), typeof(EntityService<>));
// -- Or if you plan to use the GetDb.Context() for DbContext creation (less common with DI) --
// containerBuilder.Register(c => GetDb.Context()).As<SCMSContext>().InstancePerRequest(); // Adjust SCMSContext to YourProjectDbContext
// containerBuilder.RegisterGeneric(typeof(EntityService<>)).As(typeof(IEntityService<>)).InstancePerRequest();
// var container = containerBuilder.Build();
// DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); // Example for MVC 5 Autofacd. Use in a Service or Controller:
Inject IEntityService<T> into your services or controllers.
// Example ProductService class
// using YourProject.Entities; // Or BLL.Entities
// using System.Threading.Tasks;
// using System.Collections.Generic;
public class ProductService
{
private readonly IEntityService<Product> _productRepository;
// Constructor injection
public ProductService(IEntityService<Product> productRepository)
{
_productRepository = productRepository;
}
public async Task<Product> GetProductByIdAsync(long id)
{
// Example: Asynchronously fetch a product by ID.
// For eager loading, add navigation properties:
// return await _productRepository.GetByIdAsync(id, p => p.Category);
return await _productRepository.GetByIdAsync(id);
}
public async Task<Product> GetProductByNameAsync(string name)
{
// Example: Asynchronously fetch a single product by name.
// For eager loading, add navigation properties:
// return await _productRepository.GetSingleAsync(p => p.Name == name, p => p.Category);
return await _productRepository.GetSingleAsync(p => p.Name == name);
}
// Example: Get all products (synchronous).
public IList<Product> GetAllProducts()
{
// This is a synchronous call. It fetches all products.
// To eager load related entities (e.g., Category), pass them as parameters:
// return _productRepository.GetAll(p => p.Category);
return _productRepository.GetAll();
}
// Example: Get all products with their categories (synchronous eager loading).
public IList<Product> GetAllProductsWithCategories()
{
// This is a synchronous call that fetches all products and includes their Category property.
return _productRepository.GetAll(p => p.Category);
}
// Example: Get products of a specific category (synchronous eager loading).
public IList<Product> GetProductsOfCategory(string categoryName)
{
// This is a synchronous call.
// Fetches products matching the criteria and includes their Category property.
// Assumes Product has a Category navigation property, and Category has a Name property.
return _productRepository.GetList(p => p.Category.Name == categoryName, p => p.Category);
}
// Example: Get a product by ID with its category (asynchronous eager loading).
public async Task<Product> GetProductWithCategoryByIdAsync(long id)
{
// This is an asynchronous call that fetches a product and includes its Category property.
return await _productRepository.GetByIdAsync(id, p => p.Category);
}
public async Task AddProductAsync(Product product)
{
// The Add method can take multiple items: _productRepository.Add(product1, product2);
_productRepository.Add(product);
await _productRepository.CommitAsync(); // Save changes
}
public async Task AddMultipleProductsAsync(Product[] products)
{
_productRepository.Add(products);
await _productRepository.CommitAsync(); // Save changes
}
public async Task UpdateProductAsync(Product product)
{
// The Update method can take multiple items: _productRepository.Update(product1, product2);
_productRepository.Update(product);
await _productRepository.CommitAsync();
}
public async Task DeleteProductAsync(long id)
{
var product = await _productRepository.GetByIdAsync(id);
if (product != null)
{
// The Remove method can take multiple items: _productRepository.Remove(product1, product2);
_productRepository.Remove(product);
await _productRepository.CommitAsync();
}
}
}- Clone this repository:
(Replace
git clone https://github.com/your-username/Generic-Repository-Pattern.git
your-usernamewith the actual repository owner's username or organization). - Integrate into your project:
- Copy the relevant files (
IEntityService.cs,EntityService.cs,GetDb.cs, and the baseEntity.csif you adopt it) into your solution. - Adapt the
DbContext: TheEntityService<T>andGetDb.cs(if used directly for context) rely on aDbContext. The original project usesSCMSContext. You will need to change this to your application'sDbContext(e.g.,YourProjectDbContextas shown in the examples). - Ensure Entities Inherit Base Class: Make sure your domain entities inherit from a common base class (e.g.,
BLL.Entities.Entityor your own equivalent) that provides at least anIdproperty (typicallylong? Id).
- Copy the relevant files (
Contributions are welcome! Please feel free to open an issue to discuss potential changes or submit a pull request.
- Fork the repository.
- Create your feature branch (
git checkout -b feature/AmazingFeature). - Commit your changes (
git commit -m 'Add some AmazingFeature'). - Push to the branch (
git push origin feature/AmazingFeature). - Open a Pull Request.
This project is licensed under the terms of the License file. (Please ensure a License file exists in the root of the repository, e.g., LICENSE or LICENSE.md, containing the terms of use, like MIT, Apache 2.0, etc.)
Note: The BLL.Entities.Entity and SCMSContext are placeholders derived from the structure of the provided code. You'll need to replace them with your actual entity base class and DbContext implementation.