<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: GutsBerserker</title>
    <description>The latest articles on DEV Community by GutsBerserker (@guts_002404a47707c9c88be6).</description>
    <link>https://dev.to/guts_002404a47707c9c88be6</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2748884%2Fa935d1f5-9b3c-4ad9-a73e-1d6a25fac20c.jpg</url>
      <title>DEV Community: GutsBerserker</title>
      <link>https://dev.to/guts_002404a47707c9c88be6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/guts_002404a47707c9c88be6"/>
    <language>en</language>
    <item>
      <title>Generic Repositories in C#</title>
      <dc:creator>GutsBerserker</dc:creator>
      <pubDate>Wed, 22 Jan 2025 17:11:12 +0000</pubDate>
      <link>https://dev.to/guts_002404a47707c9c88be6/generic-repositories-in-c-2998</link>
      <guid>https://dev.to/guts_002404a47707c9c88be6/generic-repositories-in-c-2998</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In modern software development, maintaining a clean and scalable architecture is crucial. One of the most effective ways to achieve this in C# applications using Entity Framework Core is through the implementation of generic repositories. A generic repository provides a reusable and flexible way to handle data access logic while keeping the codebase maintainable.&lt;/p&gt;

&lt;p&gt;This blog will explore the concept of generic repositories in C# and demonstrate an implementation using IRepository and Repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is a Generic Repository?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A generic repository is a design pattern that abstracts database operations into a single class, making it reusable across different entity types. This approach follows the DRY (Don't Repeat Yourself) principle and ensures consistency in data access logic.&lt;/p&gt;

&lt;p&gt;A generic repository typically supports CRUD (Create, Read, Update, Delete) operations and allows querying of entities while minimizing boilerplate code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing a Generic Repository in C#&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Defining the IRepository Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The IRepository interface defines the contract that all repositories must follow. This interface ensures that every entity repository provides fundamental data access operations.&lt;br&gt;
&lt;code&gt;public interface IRepository&amp;lt;TEntity&amp;gt; where TEntity : BaseEntity, new()&lt;br&gt;
{&lt;br&gt;
    public DbSet&amp;lt;TEntity&amp;gt; Table { get; }&lt;br&gt;
    public Task&amp;lt;TEntity?&amp;gt; GetById(Guid id, params string[] includes);&lt;br&gt;
    public IQueryable&amp;lt;TEntity&amp;gt; GetAll(params string[] includes);&lt;br&gt;
    public IQueryable&amp;lt;TEntity&amp;gt; FindAll(Expression&amp;lt;Func&amp;lt;TEntity, bool&amp;gt;&amp;gt; expression = null, params string[] includes);&lt;br&gt;
    public Task&amp;lt;TEntity&amp;gt; Create(TEntity entity);&lt;br&gt;
    public void Update(TEntity entity);&lt;br&gt;
    public void Delete(TEntity entity);&lt;br&gt;
    public Task&amp;lt;int&amp;gt; SaveChangesAsync();&lt;br&gt;
    public Task&amp;lt;bool&amp;gt; IsExist(Expression&amp;lt;Func&amp;lt;TEntity, bool&amp;gt;&amp;gt; expression);&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
Explanation:&lt;/p&gt;

&lt;p&gt;GetById: Retrieves an entity by its ID, optionally including related entities.&lt;/p&gt;

&lt;p&gt;GetAll: Returns all entities from the database.&lt;/p&gt;

&lt;p&gt;FindAll: Fetches entities based on a filter expression.&lt;/p&gt;

&lt;p&gt;Create: Adds a new entity to the database.&lt;/p&gt;

&lt;p&gt;Update: Updates an existing entity.&lt;/p&gt;

&lt;p&gt;Delete: Removes an entity from the database.&lt;/p&gt;

&lt;p&gt;SaveChangesAsync: Saves all changes asynchronously.&lt;/p&gt;

&lt;p&gt;IsExist: Checks if an entity exists based on a given condition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Implementing the Repository Class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Repository class implements IRepository and provides actual database interaction using Entity Framework Core.&lt;/p&gt;

&lt;p&gt;public class Repository : IRepository where TEntity : BaseEntity, new()&lt;br&gt;
{&lt;br&gt;
    private readonly AppDbContext _context;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Repository(AppDbContext context)
{
    _context = context;
}

public DbSet&amp;lt;TEntity&amp;gt; Table =&amp;gt; _context.Set&amp;lt;TEntity&amp;gt;();

public async Task&amp;lt;TEntity&amp;gt; Create(TEntity entity)
{
    await Table.AddAsync(entity);
    return entity;
}

public void Delete(TEntity entity)
{
    Table.Remove(entity);
}

public IQueryable&amp;lt;TEntity&amp;gt; FindAll(Expression&amp;lt;Func&amp;lt;TEntity, bool&amp;gt;&amp;gt; expression = null, params string[] includes)
{
    IQueryable&amp;lt;TEntity&amp;gt; query = Table.AsNoTracking();

    foreach (var include in includes)
    {
        if (!string.IsNullOrWhiteSpace(include))
        {
            query = query.Include(include);
        }
    }

    if (expression != null)
    {
        query = query.Where(expression);
    }

    return query;
}

public IQueryable&amp;lt;TEntity&amp;gt; GetAll(params string[] includes)
{
    IQueryable&amp;lt;TEntity&amp;gt; query = Table.AsNoTracking();

    foreach (var include in includes)
    {
        query = query.Include(include);
    }

    return query;
}

public async Task&amp;lt;TEntity?&amp;gt; GetById(Guid id, string[] includes)
{
    IQueryable&amp;lt;TEntity&amp;gt; query = Table.AsNoTracking();

    foreach (var include in includes)
    {
        query = query.Include(include);
    }

    return await query.AsNoTracking().FirstOrDefaultAsync(x =&amp;gt; x.Id == id);
}

public async Task&amp;lt;bool&amp;gt; IsExist(Expression&amp;lt;Func&amp;lt;TEntity, bool&amp;gt;&amp;gt; expression)
{
    return await Table.AnyAsync(expression);
}

public async Task&amp;lt;int&amp;gt; SaveChangesAsync()
{
    return await _context.SaveChangesAsync();
}

public void Update(TEntity entity)
{
    Table.Update(entity);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;Explanation:&lt;/p&gt;

&lt;p&gt;Uses DbSet to interact with the database table corresponding to TEntity.&lt;/p&gt;

&lt;p&gt;AsNoTracking() ensures that retrieved entities are not tracked by EF Core, improving performance for read operations.&lt;/p&gt;

&lt;p&gt;Includes support navigation properties, allowing eager loading of related entities.&lt;/p&gt;

&lt;p&gt;Uses async methods for database interactions to optimize performance in web applications.&lt;/p&gt;

&lt;p&gt;Advantages of Using a Generic Repository&lt;/p&gt;

&lt;p&gt;Code Reusability: Eliminates repetitive CRUD logic for different entities.&lt;/p&gt;

&lt;p&gt;Scalability: Makes it easier to extend functionalities as the application grows.&lt;/p&gt;

&lt;p&gt;Consistency: Ensures a uniform data access approach across the codebase.&lt;/p&gt;

&lt;p&gt;Separation of Concerns: Decouples business logic from data access logic.&lt;/p&gt;

&lt;p&gt;Testability: Facilitates unit testing by allowing dependency injection of repositories.&lt;/p&gt;

&lt;p&gt;In case if you have some specific methods, you can create a new repository:&lt;br&gt;
&lt;code&gt;public class PositionRepository : Repository&amp;lt;Position&amp;gt;, IPositionRepository&lt;br&gt;
    {&lt;br&gt;
        public PositionRepository(AppDbContext context) : base(context)&lt;br&gt;
        {&lt;br&gt;
        }&lt;br&gt;
    }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A generic repository pattern is a powerful tool in C# development with Entity Framework Core. It streamlines data access, improves maintainability, and ensures consistency across different parts of an application.&lt;/p&gt;

&lt;p&gt;By implementing IRepository and Repository, we can manage database operations efficiently while adhering to clean architecture principles.&lt;/p&gt;

&lt;p&gt;Do you use a generic repository in your projects? Let me know in the comments!&lt;/p&gt;

&lt;p&gt;Here's the full example of my application which you can download from this link:&lt;br&gt;
&lt;a href="https://drive.google.com/file/d/1gAOgQdNSuP09s93Vr-gxU2VVV58x-Plw/view?pli=1" rel="noopener noreferrer"&gt;My Project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the example of Program.cs from my project:&lt;br&gt;
var builder = WebApplication.CreateBuilder(args);&lt;/p&gt;

&lt;p&gt;// Add services to the container.&lt;br&gt;
builder.Services.AddControllersWithViews();&lt;/p&gt;

&lt;p&gt;builder.Services.AddDbContext(options =&amp;gt; options.UseSqlServer(&lt;br&gt;
builder.Configuration.GetConnectionString("Default")&lt;br&gt;
));&lt;/p&gt;

&lt;p&gt;builder.Services.AddDALServices();&lt;/p&gt;

&lt;p&gt;builder.Services.AddBusinessServices();&lt;/p&gt;

&lt;p&gt;builder.Services.AddIdentity()&lt;br&gt;
.AddEntityFrameworkStores()&lt;br&gt;
.AddDefaultTokenProviders();&lt;/p&gt;

&lt;p&gt;builder.Services.AddAuthentication()&lt;br&gt;
.AddCookie(options =&amp;gt;&lt;br&gt;
{&lt;br&gt;
options.LoginPath = "/Account/Login";&lt;br&gt;
options.ExpireTimeSpan = TimeSpan.FromDays(7);&lt;br&gt;
options.AccessDeniedPath = "/Account/AccessDenied";&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;builder.Services.Configure(options =&amp;gt;&lt;br&gt;
{&lt;br&gt;
options.Password.RequireDigit = false;&lt;br&gt;
options.Password.RequireLowercase = false;&lt;br&gt;
options.Password.RequireUppercase = false;&lt;br&gt;
options.Password.RequireNonAlphanumeric = false;&lt;br&gt;
options.Password.RequiredLength = 6;&lt;br&gt;
options.User.RequireUniqueEmail = true;&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;var app = builder.Build();&lt;/p&gt;

&lt;p&gt;// Configure the HTTP request pipeline.&lt;br&gt;
if (!app.Environment.IsDevelopment())&lt;br&gt;
{&lt;br&gt;
app.UseExceptionHandler("/Home/Error");&lt;br&gt;
}&lt;br&gt;
app.UseStaticFiles();&lt;/p&gt;

&lt;p&gt;app.UseRouting();&lt;/p&gt;

&lt;p&gt;app.UseAuthentication();&lt;br&gt;
app.UseAuthorization();&lt;/p&gt;

&lt;p&gt;app.MapControllerRoute(&lt;br&gt;
name: "areas",&lt;br&gt;
pattern: "{area:exists}/{controller=Dashboard}/{action=Index}/{id?}"&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;app.MapControllerRoute(&lt;br&gt;
name: "default",&lt;br&gt;
pattern: "{controller=Home}/{action=Index}/{id?}");&lt;/p&gt;

&lt;p&gt;app.Run();&lt;br&gt;
What is the best practice of file upload in my case for .NET Web API?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
