AuxiliaryLibraries.GenericRepository.Core 1.0.7

dotnet add package AuxiliaryLibraries.GenericRepository.Core --version 1.0.7
NuGet\Install-Package AuxiliaryLibraries.GenericRepository.Core -Version 1.0.7
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AuxiliaryLibraries.GenericRepository.Core" Version="1.0.7" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AuxiliaryLibraries.GenericRepository.Core --version 1.0.7
#r "nuget: AuxiliaryLibraries.GenericRepository.Core, 1.0.7"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AuxiliaryLibraries.GenericRepository.Core as a Cake Addin
#addin nuget:?package=AuxiliaryLibraries.GenericRepository.Core&version=1.0.7

// Install AuxiliaryLibraries.GenericRepository.Core as a Cake Tool
#tool nuget:?package=AuxiliaryLibraries.GenericRepository.Core&version=1.0.7

There might be no project that doesn't need this library. With the help of this library you are able to get rid of implementing CRUD operations for all of your entities. You should just configure a few lines for every enity and that's it, you have implemented all of CRUD operations along with lots of other useful functions.
Besides, as this library use AuxiliaryLibraries.CacheManager, if you configure CacheManager, you will access a very handy feature which is caching with no extra effort.

This use generic repository and unit of work design patterns for implementing all of its functions. Furthermore, CacheManager uses Redis.

Table of Contents

  • About The Library
  • How it works
  • How to use
  • What do you have now
  • Easy Peasy caching
  • Configuration

About The Library

This library helps you to implement your projects faster than usual, because you can focus on implementing logic of your project and leave the Data Access Layer to this library. You have to do a few steps as described below. Entity Framework Core is used in this library and if you choose another types of ORM this library might not useful for you.

How it works

There are a few base classes inside the library, you should create one interface and one repository class for each entity, and inherit them from those base classes. If you need to change the functionality of any of default functions, you can simply override them. Or you can add any other functionality to your repositories. After that you must register your classes to your IOC container.

How to use

Let's imagine we have one entity. In real projects you will have more than that. Do these steps for all of them.

Table - Entity

public partial class Person
{
    public long Id { get; set; }
    public string FirstName { get; set; }
    public string LastnametName { get; set; }
    public string Address { get; set; }
    public DateTime CreateDate { get; set; }
    public DateTime LastModifiedDate { get; set; }
}

Repository

Now you should create one interface and one Repository class as follows:

Person Repository Interface

There is a base interface called IRepository. Create an interface and inherit it from IRepository<TEntity>:

public interface IPersonRepository : IRepository<Person>
{
    // For default you do not need to add anything here.
}

Person Repository

There is a base interface called Repository. Create a class and inherit it from Repository<TEntity> and IPersonRepository: If you want to use Cashing follow code below:

public class PersonRepository : Repository<Person>, IPersonRepository
{
    // TestDbContext is the name of your DbContext, Replace it with your own DbContext name
    private TestDbContext TestDbContext => Context as TestDbContext;

    public PersonRepository(TestDbContext context, ICacheService cacheService) : base(context, cacheService)
    {
      // For default you do not need to add anything here.
    }
}

But if you don't want to use Cashing there is no need to pass ICacheService. In this case, wherevere we have cacheFirst, cacheFirst won't work. Besides, wherevere we have RefreshCache, TryGet, TryGetCache, or anything else related to cache won't work.

public class PersonRepository : Repository<Person>, IPersonRepository
{
    // TestDbContext is the name of your DbContext, Replace it with your own DbContext name
    private TestDbContext TestDbContext => Context as TestDbContext;

    public PersonRepository(TestDbContext context) : base(context)
    {
      // For default you do not need to add anything here.
    }
}

If you need to add any other functionality to your repositories you can do like this:

Person Repository Interface

public interface IPersonRepository : IRepository<Person>
{
    Task<Person> GetLastSourceAsync(bool cacheFirst = false);
}

Person Repository

public class PersonRepository : Repository<Person>, IPersonRepository
{
    // TestDbContext is the name of your DbContext, Replace it with your own DbContext name
    private TestDbContext TestDbContext => Context as TestDbContext;

    // Or
    // public PersonRepository(TestDbContext context) : base(context)
    public PersonRepository(TestDbContext context, ICacheService cacheService) : base(context, cacheService)
    {
        public virtual async Task<Person> GetLastSourceAsync(bool cacheFirst = false)
        {
            var cacheKey = CacheConventions.Cachekey<Person>(id: 1, baseKey: "Last");
            if (!cacheFirst || !CacheService.TryGet(cacheKey, out Person cachedItem))
            {
                cachedItem = await TestDbContext.People.OrderByDescending(x => x.CreateDate).FirstOrDefaultAsync(x => !string.IsNullOrEmpty(x.FirstName));
                if (cacheFirst && cachedItem != null)
                    CacheService.Set(cacheKey, cachedItem);
            }
            return cachedItem;
        }
    }
}

UnitOfWork

Now it's time to have UnitOfWork. Create an interface and a class for UnitOfWork

IUnitOfWork

There is a base interface called IUnitOfWork. Create an interface and inherit it from that IUnitOfWork. Then you have to add all of your repositories here inside this interface just like below:

public interface ITestUnitOfWork : IUnitOfWork
{
    IPersonRepository People { get; }
}

UnitOfWork

There is a base class called UnitOfWork. Create a class and inherit it from that UnitOfWork and ITestUnitOfWork. Then you have to decorate your class as follows and add all of your repositories here inside this class just like below:

public class TestUnitOfWork : UnitOfWork, ITestUnitOfWork
{
    private TestDbContext TestDbContext => Context as TestDbContext;

    public TestUnitOfWork
    (
        TestDbContext context,
        ICacheService cacheService, // This is optional, you are able to not pass it.
        IPersonRepository PersonRepository
    ) : base(context, cacheService)
    {
        People = PersonRepository;
    }

    public IPersonRepository People { get; set; }
}

What do you have now

Almost Done. There is only a few configurations left. But before that lets desscuss what do you have now. Imagine we have a service called PersonService and we have these functionality including many others insise the service.

public class PersonService : IPersonService
{
    private readonly ITestUnitOfWork UnitOfWork;

    public PersonService(ITestUnitOfWork testUnitOfWork)
    {
        this.UnitOfWork = testUnitOfWork;
    }

    public async ValueTask Get()
    {
        // Getting first row of table
        var data1 = await UnitOfWork.People.FirstAsync();
        // Getting first row of table with specifications, and set cache as true
        var data1 = await UnitOfWork.People.FirstAsync(predicate: x => x.CreateDate > DateTime.Now,
                                                            orderBy: x => x.OrderByDescending(o => o.CreateDate),
                                                            cacheFirst: true,
                                                            asNoTracking: AsNoTrackingType.AsNoTracking);
        
        // Getting first row of table
        var data2 = await UnitOfWork.People.FirstOrDefaultAsync();
        // Getting first row of table with specifications, and set cache as true
        var data2 = await UnitOfWork.People.FirstOrDefaultAsync(predicate: x => x.CreateDate > DateTime.Now,
                                                            orderBy: x => x.OrderByDescending(o => o.CreateDate),
                                                            cacheFirst: true,
                                                            asNoTracking: AsNoTrackingType.AsNoTrackingWithIdentityResolution);

        // Getting last row of table
        var data3 = await UnitOfWork.People.LastAsync();
        // Getting last row of table with specifications, and set cache as true
        var data4 = await UnitOfWork.People.LastOrDefaultAsync(predicate: x => x.CreateDate > DateTime.Now,
                                                            orderBy: x => x.OrderByDescending(o => o.CreateDate),
                                                            cacheFirst: true,
                                                            asNoTracking: AsNoTrackingType.None);

        // Selecting/Searching throughout the table 
        var data5 = await UnitOfWork.People.FindAsync(predicate: x => x.CreateDate > DateTime.Now,
                                                            orderBy: x => x.OrderByDescending(o => o.CreateDate),
                                                            cacheFirst: true,
                                                            page: 1, pageSize: 100,
                                                            asNoTracking: AsNoTrackingType.AsNoTracking);

        // Get a row with the id of 12 => Id can be any DATA TYPE
        var data6 = await UnitOfWork.People.GetAsync(12, cacheFirst: true);

        // Get all data of a table
        var data7 = await UnitOfWork.People.GetAllAsync(cacheFirst: true, asNoTracking: AsNoTrackingType.AsNoTracking);

        // Get maximum value
        var data8 = await UnitOfWork.People.MaxAsync(x => x.Rate, cacheFirst: true);
        // Get maximum value according to the predicate
        var data8 = await UnitOfWork.People.MaxAsync(predicate: x => x.CreateDate > DateTime.Now, selector: x => x.Rate, cacheFirst: true);

        // Get minimum value
        var data9 = await UnitOfWork.People.MinAsync(x => x.Rate, cacheFirst: true);
        // Get minimum value according to the predicate
        var data9 = await UnitOfWork.People.MinAsync(predicate: x => x.CreateDate > DateTime.Now, selector: x => x.Rate, cacheFirst: true);

        // Get Sum of values
        var data10 = await UnitOfWork.People.SumAsync(predicate: x => x.CreateDate > DateTime.Now, selector: x => x.Rate, cacheFirst: true);

        // Get Sum of values
        var data11 = await UnitOfWork.People.CountAsync();
        // Get Sum of values according to the predicate
        var data11 = await UnitOfWork.People.CountAsync(predicate: x => x.CreateDate > DateTime.Now, selector: x => x.Rate, cacheFirst: true);

        // Get Average of values
        var data12 = await UnitOfWork.People.AverageAsync(predicate: x => x.CreateDate > DateTime.Now, selector: x => x.Rate, cacheFirst: true); 
    }

    public async Task GetQuery()
    {
        // You have access to Where clause for any other usage
        await UnitOfWork.People.Where(x => x.CreateDate > DateTime.Now).Include(x => x.PersonRules).ToListAsync();
        // You can get AsQueryable() for any further implementation
        await UnitOfWork.People.AsQueryable().Where(x => x.CreateDate > DateTime.Now).ToListAsync();
        // You can even excecute your own T-Sql query
        await UnitOfWork.People.FromSqlRaw("SELECT * FROM [dbo].[People]").ToListAsync();
    }

    public void Remove()
    {
        UnitOfWork.People.Remove(person);
        UnitOfWork.People.RemoveRange(people);
        UnitOfWork.SaveChangesAsync();
    }

    public async Task Add()
    {
        UnitOfWork.People.Add(person);
        UnitOfWork.People.AddRange(people);

        await UnitOfWork.People.AddAsync(person);
        await UnitOfWork.People.AddRangeAsync(people);

        await UnitOfWork.SaveChangesAsync();
    }

    public async Task Update()
    {
        var person = await UnitOfWork.People.GetAsync(12);
        person.LastModifiedDate = DateTime.Now;
        await UnitOfWork.SaveChangesAsync();
    }

    public async Task DoSometingTransactional()
    {
        UnitOfWork.BeginTransaction();
        try
        {
            var person = await UnitOfWork.People.GetAsync(12);
            person.LastModifiedDate = DateTime.Now;
            await UnitOfWork.SaveChangesAsync();
            await UnitOfWork.CommitAsync();
        }
        catch (Exception ex)
        {
            await UnitOfWork.RollbackAsync();
        }
    }
}

Easy Peasy caching

With the help of AuxiliaryLibraries.CacheManager we have a easy access to cashing our data here. As mentioned above, we can cache data just by setting cacheFirst as true. If we do that, it means using the cache, if data has already been stored in Redis just fetch and if it didn't get data from Database and then set it to redis.The interesting part here is, there is no need to set the cache key, the naming convention is used according to the Entity Name and the parameters you passed. There might be a big question: When the cached data will update? There is a very easy answer for that and that is when you use UnitOfWork.SaveChangesAsync() caching data will remove. Not all of the cached data, of course, just that part you have updated, inserted, or deleted to or from DB. (The recently affected data) Let's dive into caching a little bit deeper. If you want to use caching out of those functions, you can do as follows, or simply use AuxiliaryLibraries.CacheManager and follow its instructions.

public class PersonService : IPersonService
{
    public async Task Caching()
    {
        // cache key is generating according to the passed parameters to functions. Pass them just like that into the below function
        var cacheKey = CacheConventions.Cachekey<Person>(id: 1, baseKey: "Last");
        // Or
        var cacheKey = CacheConventions.Cachekey<Person>(predicate: x => x.CreateDate > DateTime.Now,
                                                            orderBy: x => x.OrderByDescending(o => o.CreateDate));

        // Removing value of the cacheKey
        UnitOfWork.RefreshCache(cacheKey);
        // Replacing the value of the cacheKey by obj
        await UnitOfWork.RefreshCache(cacheKey, obj);
        // Removing any cached data related to Person
        await UnitOfWork.RefreshCache<Person>(cacheFirst: true);
        // Removing any cached data related to Person with the predicate
        UnitOfWork.RefreshCache<Person>(predicate: x => x.CreateDate > DateTime.Now);
        // Or you can simply use 
        UnitOfWork.People.RefreshCache(cacheKey); // Removing value of the cacheKey
        await UnitOfWork.People.RefreshCache(cacheKey, obj); // Replacing the value of the cacheKey by obj
        UnitOfWork.People.RefreshCache(); // Removing any cached data related to Person
        UnitOfWork.People.RefreshCache(predicate: x => x.CreateDate > DateTime.Now); // Removing any cached data related to Person with the predicate
        // Try to get a value from cache
        if (UnitOfWork.TryGetCache<Person>(cacheKey, out var person))
        {
            person.ToString();
        }
    }
}

Configuration

Now what we do for the last part is the Configuration. For this we must register all of our repositories and unit of work classes and interfaces into the IOC container in Startup.cs file like this:

service.AddScoped<ITestUnitOfWork, TestUnitOfWork>();
service.AddScoped<IPersonRepository, PersonRepository>();

And we need to call use caching function in Startup.cs file as well if we want to use caching. Otherwise, it is not necessary.

service.UseCache(configuration);
//add this to appsettings.json
"CacheConfiguration": {
 "AbsoluteExpirationInHours": 1,
 "SlidingExpirationInMinutes": 30,
 "Host": "111.111.111.111",
 "Port": "80",
 "Password": "redis",
 "DatabaseID": 1
}
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.7 79 4/2/2024
1.0.6 285 2/18/2023
1.0.5 220 2/7/2023
1.0.4 237 2/7/2023
1.0.3 255 2/5/2023
1.0.2 267 2/2/2023
1.0.1 227 2/2/2023
1.0.0 260 1/30/2023