Git Product home page Git Product logo

efcore.genericrepository's Introduction

๐Ÿ‘จโ€๐Ÿ’ป ๐’๐”๐Œ๐Œ๐€๐‘๐˜

Itโ€™s me, Tanvir Ahmad Arjel, a Full Stack Software Engineer, with more than 6 years of professional experience in Enterprise Application and Software Development having strong problem solving, data structures and algorithms knowledge. In short, I have expertise in the following fields:

1. Microservice and Distributed System. 2. System Design and Database Design.
3. Event Driven Architecture 4. Event Sourcing
5. Cloud Functions 6. Azure DevOps, CI/CD pipeline integration
7. Domain-Driven Design 8. Clean Architecture

๐Ÿ’ช ๐’๐Š๐ˆ๐‹๐‹๐’

๐ƒ๐š๐ญ๐š ๐’๐ญ๐ซ๐ฎ๐œ๐ญ๐ฎ๐ซ๐ž๐ฌ ๐š๐ง๐ ๐€๐ฅ๐ ๐จ๐ซ๐ข๐ญ๐ก๐ฆ๐ฌ

LeetCode : 550+ problems solved.

HackerRank : 4 Stars

๐๐ซ๐จ๐ ๐ซ๐š๐ฆ๐ฆ๐ข๐ง๐  ๐‹๐š๐ง๐ ๐ฎ๐š๐ ๐ž๐ฌ

C# | C++ | JavaScript | TypeScript

๐…๐ซ๐š๐ฆ๐ž๐ฐ๐จ๐ซ๐ค๐ฌ & ๐๐ฅ๐š๐ญ๐Ÿ๐จ๐ซ๐ฆ๐ฌ

.NET/.NET Core | ASP.NET Core | Blazor | Entity Framework Core | Angular

๐—–๐—น๐—ผ๐˜‚๐—ฑ

Microsoft Azure | Azure Functions | Azure CI/CD | Google Cloud | Google Cloud Functions | Google PubSub

๐ƒ๐š๐ญ๐š๐›๐š๐ฌ๐ž๐ฌ

Microsoft SQL Server | PostgreSQL | MongoDB | Redis | EventStoreDB

๐“๐จ๐จ๐ฅ๐ฌ

Docker | Git | TFS | Kafka | RabbitMQ | xUnit | Hangfire | Serilog | Exceptionless | Ocelot | AutoMapper | MediatR | Swagger/OpenAPI

๐’๐จ๐Ÿ๐ญ๐ฐ๐š๐ซ๐ž ๐€๐ซ๐œ๐ก๐ข๐ญ๐ž๐œ๐ญ๐ฎ๐ซ๐ž ๐๐š๐ญ๐ญ๐ž๐ซ๐ง๐ฌ

MVC | REST API | Microservice | CQRS and Event Sourcing | Domain Driven Design | Event Driven Architecture | TDD

๐…๐ซ๐จ๐ง๐ญ-๐„๐ง๐

HTML5 | CSS3 | Bootstrap | JavaScript | jQuery | Angular

โœ‰๏ธ ๐‘๐„๐€๐‚๐‡ ๐Œ๐„

Email: [email protected]

efcore.genericrepository's People

Contributors

oguzgoez avatar tanviragoda avatar tanvirarjel avatar vinod-vetrivel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

efcore.genericrepository's Issues

UpdateBy without entity available?

hi,

Is there an update method without having to have an entity? Something like this:

from this C# code

repository.Update(x => x.Id == 4, x => x.Name = "Updated Value");

to this sql

UPDATE table SET Name = 'Updated Value' WHERE Id = 4;

or update multiple entities like this

repository.Update(x => x.OtherValue is null, x => x.Name = "Updated Value");

to this sql

UPDATE table SET Name = 'Updated Value' WHERE OtherValue = NULL;

Usage with multiple DbContexts

How would you use the library with 2 DbContexts (independent from one another)?

services.AddGenericRepository<ApplicationDbContext>();
services.AddGenericRepository<TenantAdminDbContext>();

The issue is now how to inject the "right" repository into a class so that it picks up the correct DbContext?

Found this article and also this one addressing this but would like to get a feedback from you to avoid different directions.

Q: How to use transctions

I am trying your repository.

I am in a situation where i should delete a data, insert another one and if insert is ok, get the id of inserted to put it in an history table (no foreign key here).

To do this, I need to open a transction manually, put several SaveChanges and if everything is ok commit the transaction (obviously, if something went wrong, rollback the transaction _)

Is it possible with this repository ?

Add predicate to ExecuteUpdateAsync

ExecuteUpdateAsync of EFCore rely on the source IQueryable has already been filtered by Where Linq, but in your version, i can not see anywhere to add filter for your repository, which mean whenever we call something like

await repository.ExecuteUpdateAsync<Person>(
    sp => sp.SetProperty(t => t.IsDeleted, true)
)
// translate to
/*
UPDATE People SET IsDeleted = true;
*/

whole record will get updated immediately, maybe you can add an predicate to your ExecuteUpdateAsync to let consumer supply their own filter condition just like what we can do in EFCore

await context.People
    .Where(p => p.CreatedDate <= DateTime.Now.AddDays(-30))
    .ExecuteUpdateAsync(sp =>  sp => sp.SetProperty(t => t.IsDeleted, true)) );
// now we have where clause
/*
UPDATE People SET IsDeleted = false
WHERE CreatedDate <= @p__linq__0;
*/

You should continue with development

Firstly, thank you on working on such needed library. Regardless of how many stars (*) you get, you continue working on the and make it better. As times goes people will see value.

..Ben

How to know if delete was successful in a transaction?

Hi,

in a transaction, how would you delete an entity and verify the result before proceeding to another database operation?

For instance, with EF DbContext it's possible to get from the method _context.SaveChanges() an int with the number of records affected. That way, it's possible to commit or rollback. How do you do this with your library since all your methods do not return the value returned by the original dbcontext SaveChanges()? Why are you returning void?

This should be possible:
int affectedRows = await _repository.DeleteAsync(user);

I suggest that all Delete and Update methods are updated to this:

public async Task<int> DeleteAsync<T>(T entity, CancellationToken cancellationToken = default)
            where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            _dbContext.Set<T>().Remove(entity);
            return await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }
public async Task<int> UpdateAsync<T>(IEnumerable<T> entities, CancellationToken cancellationToken = default)
            where T : class
        {
            if (entities == null)
            {
                throw new ArgumentNullException(nameof(entities));
            }

            _dbContext.Set<T>().UpdateRange(entities);
            return await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }

This way they will return the int returned by _dbContext.SaveChangesAsync().

Thank you

Returning types should be changed?

HI once again,

I decided to open a new Issue since this is another matter.

The InsertAsync method returns an object[] type but I wonder why you decided to hide the default EntityEntry return type.
If the goal is to simplify and just return the primary key, then this could be done without hiding EntityEntry by using the following definition:

public async Task<(EntityEntry entity, object[] primaryKey)> InsertAsync<T>(T entity, CancellationToken cancellationToken = default)
           where T : class
        {
            if (entity == null)
            {
                throw new ArgumentNullException(nameof(entity));
            }

            EntityEntry<T> entityEntry = await _dbContext.Set<T>().AddAsync(entity, cancellationToken).ConfigureAwait(false);
            await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

            object[] primaryKeyValue = entityEntry.Metadata.FindPrimaryKey().Properties.
                Select(p => entityEntry.Property(p.Name).CurrentValue).ToArray();

            return (entityEntry, primaryKeyValue);
        }

What do you think of this?

how to use multiple dbcontext?

i want used multiple dbcontext

because my database is multiple

but exemple is only once used dbcontext

waiting for your answer

Can you do the automapper.odata support or is it redundant?

I am using "odata" in my web api project. and "automapper" has that data support.
https://github.com/AutoMapper/AutoMapper.Extensions.OData

my method:

[HttpGet]
[EnableQuery]
public IActionResult Get(ODataQueryOptions<MyInternetHatlariDto> options)
{
//I don't want to use "context" how can I use your library here.
return Ok(_context.MyInternetHatlaris.Get(_mapper, options));
}

I also use your library. but since this is not supported, I have to use the database object directly. Do you think support should come or is it unnecessary?

Generic Repository is not compatible with regular repositories

That is, if you try to save a customer via generic repository with a List<Order> and these orders came from scoped IOrderRepository (that is supposed to use the same ApplicationContext) then generic repository will assume these orders are new and will try to insert them into database which is incorrect.

BUG: implementation ToPaginatedListAsync with PaginationSpecification only pick up the last condition in condition list

In this method QueryableExtensions.ToPaginatedListAsync
this block code is problem

IQueryable<T> countSource = source;

// modify the IQueryable using the specification's expression criteria
 if (specification.Conditions != null && specification.Conditions.Count != 0)
{
     foreach (Expression<Func<T, bool>> condition in specification.Conditions)
     {
           countSource = source.Where(condition);
     }
}

as you can see the current loop will always assign the current condition in condition list to countSource , which override every item before it. I guess people usually use one condition then everything work fine, but when testing with multiple condition i observe something off and browsing from source, i found this.
I think this is small problem due to you mistyping :)), anyway it should be

-  countSource = source.Where(condition);
+ countSource = countSource.Where(condition);

Client side evaluation

Hi @TanvirArjel Nice library here. I wanted to ask if you considered the drop in support of client side evaluation in query conditions, and how that is handled here . especially in specification where you use extension methods like Contains, Any etc)

Incorrect instructions on using Paginated features

The instructions presented at https://github.com/TanvirArjel/EFCore.GenericRepository#pagination-support are up to date?

The sample shows

Specification<Employee> specification = new Specification<Employee>();
            specification.Conditions.Add(e => e.EmployeeName.Contains("Ta"));
            specification.Includes = q => q.Include(e => e.Department);
            specification.OrderBy = q => q.OrderBy(e => e.EmployeeName);
            specification.Skip = 0;
            specification.Take = 4;

            long count = await _repository.GetLongCountAsync(specification.Conditions);

            List<EmployeeDto> paginatedList = await _repository.GetListAsync(specification, e => new EmployeeDto
            {
                EmployeeName = e.EmployeeName,
                DepartmentName = e.DepartmentName
            });

So, which is the correct one?

Use of "Any" in specification

Hello,
I'm facing a fancy problem.
I can't use Any in the specification condition

I think the example is self explanatory:

//... irrevelant stuff to get an instance of repository
Parent parent = new Parent {
// .... irrevelant other fields
array_of_ids_of_children = [1,2,3]
};
Specification<Children> specification = new Specification<Children>();
specification.Conditions.Add(c => parent.array_of_ids_of_children.Any(t => t == c.Id)); // Here, the array is detached from the context since Parent is a new Entity
var result = repository.GetListAsync(specification); // <-- will throw has "Any" will not be resolved correctly despite beeing an Any on an array of primitive type (here an array of integers)
return result;

Have you experinced the same issue ?
I think it come from the dynamic lambda construction.

Paginated conditions issue

Hi,

it seems that Conditions for PaginationSpecification were not considered correctly.
If I have something like

var specification = new PaginationSpecification<Transaction>();
specification.Conditions.Add(transaction => transactionIds.Contains(transaction.Id));
specification.Conditions.Add(e => e.TransactionStatus == StatusEnum.ForSell);
specification.PageIndex = 1;
specification.PageSize = 20;

I got "totalItems": 1342, which is wrong. BUt if I do one liner condition like

specification.Conditions.Add(transaction => transactionIds.Contains(transaction.Id) && transaction.TransactionStatus == StatusEnum.ForSell);

everything is OK. Is there some bug there or I am missing something with PaginationSpecification?

Documentation unitofwork

Hello,
You're project seem promising!
Could you add documentation over unitofwork?
Ex: modifying many entities from diffรฉrents repo and commit or rollback every one's at once ๐Ÿ˜

Version 6.0 : Breaking Changes

Currently, IRepository command methods are calling await _dbContext.SaveChangesAsync() internally, which arguably breaks the single responsibility pattern. So we have decided to bring the await _dbContext.SaveChangesAsync() out of the following methods:

1. InsertAsync();
2. UpdateAsync();
3. DeleteAsync();

The above methods will be replaced with the following methods:

1. Add();
2. Update();
3. Remove();

And to persist the changes to the database, the user has to call the await _repository.SaveChangesAsync() explicitly.

Currently:

_repository.InsertAsync(employee);

In from version 6.0:

_repository.Add(employee);
await _repository.SaveChangesAsync();

However, what should be the next method names:

1. Add();
2. Update();
3. Remove();

or

1. Insert();
2. Update();
3. Delete();

Please share your thoughts.

Why should we use this library?

I have came across a question engaging my mind.
We Implement Repository pattern to decouple our logic from data-layer.
By using your generic repository we are coupling logic layer with this library.

net core 5.0 many to many out of the box

Hello
I am using the new .net core 5 and ef core 5.0.10 many-to-many feature with sqlite.
I have a class Group that contains List User List Worker and the User and Worker class have callback List Group for the many-to-many relationship.
If i have a existing User and Worker i have to get all entities wit no tracking when getting all the entities(Group,Users,Workers) to add to the Group.Users and Group.Workers.
The problem is when i want to update the Group.Users/Group.Workers list to add new values. I get this error
image
I already tried with tracking no tracking.
Is there a solution for the many-to-many to work out of the box ?
I know i can create a intermediary class link add it as dbset overwrite the out of the box many-to-many relationship and update the intermediary table separate.
Thank you.

Supporting UnitOfWork

Hi,
Thanks for your good implementation.
I have a suggestion, In my opinion it is better we have a separated UnitOfWork pattern on top of this repository instead of doing SaveChanges inner repository because it is not responsibility of a repository, and it breaks single responsibility.

There are lot's of duplicated code

Problems with MySQL EF Core

Hi,

Does the library also work with the EF Core version of MySQL?
I use this initialization:

string connectionString = config.GetConnectionString("Database")!;
services.AddDbContext<DatabaseContext>(options =>
{
    options.UseMySQL(connectionString);
});
services.AddGenericRepository<DatabaseContext>();
services.AddQueryRepository<DatabaseContext>();

If I want to use the instance, the following problem occurs:

No database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.

If I use DatabaseContext directly, there are no problems.

Update to EF7

Hello,
Do you plan to update to EF7 to use new feature for example Bulk Delete or support for JSON Columns?

Thanks

Support Dynamic Connection String

Hi. thanks for this great library
I have a multi-tenant application and each tenant has its own connection string
how can I handle this?
if currently, the library supports dynamic connection strings, please tell me how

Proper use of your library by multiple projects?

Hi Tanvir;

I'd like to ask your opinion for the proper way to use your library by multiple projects, and yet, have a central location to maintain migrations process.

Suppose I have a Blazor server side (which is pretty much like an ASP.Net Core Razor pages, and I have another project which is Web API. And I want to use your library among these two or more project.
Here is my approach and you tell me if it is correct or if there is a better way.

a) I would create a new Class Library based on .Netcore 3.1 (Data Access Layer) which will reference EntityFrameworkCore library.

b) This library will also reference another shared project that holds all the entities (for API, Blazor server & Blazor client)

c) I then bring in your library into this Data Access Layer, and create my "ApplicationDbContext" class in this module.

d) Then in each Project (API, Blazor Server and Blazor), I will register the "ApplicationDBContext" with DI and provide the Connection string in each project's App settings.
This way, when each project is running, they inject an instance of ApplicationDBContext to DataAccessLayer and then pass it to your library in DAL
.

DAL will have the folder for all migrations.

Is this a proper setup to ensure each calling project, instantiate an instance of ApplicationDBContext, sends it to DAL and also DAL maintains a central location for migrations?
If there is a better way, please let me know.

Perhaps you can create a simple solution with and ASP Core, WebAPI and DataAccessLibrary to show the proper placements of all the parts.

Thanks!
..Ben

A way of doing insert or update in ef core.

I spotted that there is a way to do an 'insert or update' using ef core, is this useful to you?

https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.update?view=efcore-5.0

and an example of usage is shown in the Dbcontext Update section:

https://www.learnentityframeworkcore.com/dbcontext/modifying-data

The only problem I see is that the method updates all the fields of the entity, so can overwrite values back to null, but this may not be a problem, I am not sure.

Hopefully this means you can add an upsert feature, even if it has a limitation.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.