Giter Site home page Giter Site logo

arch / unitofwork Goto Github PK

View Code? Open in Web Editor NEW
1.3K 97.0 336.0 142 KB

A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, multiple database with distributed transaction supported, and MySQL multiple databases/tables sharding supported.

License: MIT License

C# 100.00%
entityframeworkcore unitofwork

unitofwork's Introduction

UnitOfWork

A plugin for Microsoft.EntityFrameworkCore to support repository, unit of work patterns, and multiple database with distributed transaction supported.

Support MySQL multiple databases/tables sharding

In MySQL, physically, a schema is synonymous with a database. You can substitute the keyword SCHEMA instead of DATABASE in MySQL SQL syntax, for example using CREATE SCHEMA instead of CREATE DATABASE. Some other database products draw a distinction. For example, in the Oracle Database product, a schema represents only a part of a database: the tables and other objects owned by a single user.

So, for MySQL, the easy way to support this feature is to dynamically change the SCHEMA at the runtime.

After v1.1.2 had support MySQL multiple databases/tables sharding in the same model in the same machine. For different machine, you can use DbContextFactory to dynamically create DbContext. You can use Pomelo.EntityFrameworkCore.MySql to test this feature. @PomeloFoundation

Quickly start

How to use UnitOfWork

public void ConfigureServices(IServiceCollection services)
{
    // use in memory for testing.
    services
        .AddDbContext<QuickStartContext>(opt => opt.UseInMemoryDatabase())
        .AddUnitOfWork<QuickStartContext>()
        .AddCustomRepository<Blog, CustomBlogRepository>();
}

private readonly IUnitOfWork _unitOfWork;

// 1. IRepositoryFactory used for readonly scenario;
// 2. IUnitOfWork used for read/write scenario;
// 3. IUnitOfWork<TContext> used for multiple databases scenario;
public ValuesController(IUnitOfWork unitOfWork)
{
    _unitOfWork = unitOfWork;

    // Change database only work for MySQL right now.
    unitOfWork.ChangeDatabase($"uow_db_{DateTime.Now.Year}");

    var userRepo = unitOfWork.GetRepository<User>();
    var postRepo = unitOfWork.GetRepository<Post>();

    var ym = DateTime.Now.ToString("yyyyMM");

    userRepo.ChangeTable($"user_{ym}");
    postRepo.ChangeTable($"post_{ym}");

    var user = new User
    {
        UserName = "rigofunc",
        Password = "password"
    };

    userRepo.Insert(user);

    var post = new Post
    {
        UserId = user.UserId,
        Content = "What a piece of junk!"
    };

    postRepo.Insert(post);

    unitOfWork.SaveChanges();

    var find = userRepo.Find(user.UserId);

    find.Password = "p@ssword";

    unitOfWork.SaveChanges();

    // projection

}

Projection & Including

// projection
var pagedList = _unitOfWork.GetRepository<Blog>().GetPagedList(b => new { Name = b.Title, Link = b.Url }, pageIndex: pageIndex, pageSize: pageSize);
var projection = _unitOfWork.GetRepository<Blog>().GetFirstOrDefault(b => new { Name = b.Title, Link = b.Url }, predicate: x => x.Title.Contains(term));

// including
[HttpGet]
public async Task<IPagedList<Blog>> Get()
{
    return await _unitOfWork.GetRepository<Blog>().GetPagedListAsync(include: source => source.Include(blog => blog.Posts).ThenInclude(post => post.Comments));
}

unitofwork's People

Contributors

djechelon avatar gus-sosa avatar kevingy avatar mohmehrnia avatar panda-big avatar rigofunc avatar sergeyilyin avatar shawson avatar spemoorthy avatar wangshifeng 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  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

unitofwork's Issues

no extension method 'addUnitOfWork'

Hi,

I just installed the package and I'm getting this error:

'IServiceCollection' does not contain a definition for 'addUnitOfWork' and no extension method 'addUnitOfWork' accepting a first argument of type 'IServiceCollection' could be found (are you missing a using directive or an assembly

with this code

 services.AddDbContext<RegistrosDataModel>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")))
                .addUnitOfWork<RegistrosDataModel>();

this are all the namespaces imported

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using Registros.Model;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;

Design discussing: what's the best way to implement the generic unit of work and repository patterns, and then refactor this repo

  1. Currently, I want to support custom repository, however, I found the IRepository interface has so many methods, and all of them tight coupling with EF. That's to say, without EF/LINQ, many methods of IRepository without any meaning, such as nobody want those methods when using Dapper.

  2. The GetPagedList method has so many optional parameters, it's hard to use, we SHOULD through
    Fluent API to enhance the API usage.

So I want to refactor the IRepository interface and through extension methods providing backward compatibility. The initial idea is:

  1. The IRepository interface only defines the CRUD.
namespace Microsoft.EntityFrameworkCore
{
    public interface IRepository<TEntity> where TEntity : class
    {
	IPagedList<TEntity> GetPagedList(int pageIndex = 0, int pageSize = 20);

        IPagedList<TResult> GetPagedList<TResult>(Func<TEntity, TResult> selector, int pageIndex = 0, int pageSize = 20) where TResult : class;

        TEntity Find(params object[] keyValues);

        Task<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken = default(CancellationToken));

        void Insert(TEntity entity);

        Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default(CancellationToken));

        void Update(TEntity entity);

        void Delete(object id);
    }
}
  1. Through some extension methods to provide backward compatibility.
namespace Microsoft.EntityFrameworkCore.UnitOfWork
{
    public static class BackwardCompatibilityExtensions
    {	
	[Obsolete]
        public static IPagedList<TEntity> GetPagedList<TEntity>(this IRepository<TEntity> repository, 
                                                                Expression<Func<TEntity, bool>> predicate = null,
                                                                Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
                                                                Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null,
                                                                int pageIndex = 0,
                                                                int pageSize = 20,
                                                                bool disableTracking = true) 
        {
        }
    }
}
  1. Through Fluent API to enhance the API usage.
repo.Where(predicate).OrderBy(x=>x.Id).Include(s => s.Include(x => x.Posts).ThenInclude(p => p.Author).GetPagedList(1, 20);

Loading by ID with eager-loaded relationships

Hi,

Thanks for the great UnitOfWork project, but I've just hit the need to load an entity by ID while also loading related entities.

I've come across issue #20 about eager loading when using Find/FindAsync), but I'm a bit concerned that the solution/workaround proposed there causes multiple queries to the DB, and adds thus unnecessary latency. Using the following example:

var party = await _partyRepository.FindAsync(id);
await _unitOfWork.DbContext.Entry(party).Reference(p => p.PartyType).LoadAsync();

I get the following SQL queries to teh database (using Npgsql):

dbug: Npgsql.NpgsqlConnection[3]
      Opening connection to database 'beetle' on server 'tcp://127.0.0.1:5432'.
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
      Executed DbCommand (81ms) [Parameters=[@__get_Item_0='?'], CommandType='Text', CommandTimeout='30']
      SELECT "e"."id", "e"."active_end", "e"."active_start", "e"."discriminator", "e"."full_name", "e"."notes", "e"."party_type_code", "e"."family_name", "e"."given_name", "e"."initials", "e"."other_names"
      FROM "party"."party" AS "e"
      WHERE "e"."party_type_code" IN ('P', 'O', 'Party') AND ("e"."id" = @__get_Item_0)
      LIMIT 1
dbug: Npgsql.NpgsqlConnection[4]
      Closing connection to database 'beetle' on server 'tcp://127.0.0.1:5432'.
dbug: Npgsql.NpgsqlConnection[3]
      Opening connection to database 'beetle' on server 'tcp://127.0.0.1:5432'.
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
      Executed DbCommand (9ms) [Parameters=[@__get_Item_0='?'], CommandType='Text', CommandTimeout='30']
      SELECT "e"."code", "e"."description"
      FROM "party"."party_type" AS "e"
      WHERE "e"."code" = @__get_Item_0
dbug: Npgsql.NpgsqlConnection[4]
      Closing connection to database 'beetle' on server 'tcp://127.0.0.1:5432'.

At the moment with UnitOfWork 1.x I can emulate Find with eager loading with a single DB query by using Query(e => e.Id == id).Include(e => e.RelatedEntity).FIrstOrDefault(). However, I notice that Query() is deprecated and scheduled for removal in 2.0 (which I agree is the right course of action, as exposing IQueryable<T> doesn't seem to be the right behaviour for a repository pattern). However, without .Query() there isn't any easy way to fetch a single record with eager-loaded associations in a join.

For comparison with the .Find()/.LoadAsync() example above, eager loading a single entity by id using .Query() looks like:

var party = await _partyRepository.Query(p => p.Id == id)
                                  .Include(p => p.PartyType)
                                  .FirstOrDefaultAsync();

and runs the following SQL:

dbug: Npgsql.NpgsqlConnection[3]
      Opening connection to database 'beetle' on server 'tcp://127.0.0.1:5432'.
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
      Executed DbCommand (87ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
      SELECT "p"."id", "p"."active_end", "p"."active_start", "p"."discriminator", "p"."full_name", "p"."notes", "p"."party_type_code", "p"."family_name", "p"."given_name", "p"."initials", "p"."other_names", "p0"."code", "p0"."description"
      FROM "party"."party" AS "p"
      INNER JOIN "party"."party_type" AS "p0" ON "p"."party_type_code" = "p0"."code"
      WHERE "p"."party_type_code" IN ('P', 'O', 'Party') AND ("p"."id" = @__id_0)
      LIMIT 1
dbug: Npgsql.NpgsqlConnection[4]
      Closing connection to database 'beetle' on server 'tcp://127.0.0.1:5432'.

This single round-trip is going to be a lot faster, and scale better. In situations where multiple related entities need to be loaded, the difference could quickly become significant.

I'm wondering if you'd be willing to consider adding a more flexible alternative to Find()/FindAsync() which allows eager loading to be used going forward? I know you're limited by what DbSet<T> allows for .Find(), but maybe an alternative like .GetFirst(<predicate>, <orderby>, <include>) (ie. similar signature to .GetPagedList()) could be considered? Or is there a better solution for this?

The custom repositories proposed in other issues and PRs would go some way to allowing developers to get around this limitation by writing custom repositories, but I still think an easy way to load by ID with eager loading of relationships on the base Repository<T> class is a must-have.

Thanks for the great package, and keep up the good work!

Cheers,

Timshel

Find vs GetPagedList.FirstOrDefault

Hi.
I've found some strange behaviour in the code. I've an entity TenantUser, which has composite PK.

            builder.Entity<TenantUser>()
                   .HasKey(x => new { x.UserId, x.TenantId });

And I want to find if there is a TenantUser with UserId = X and TenantId = Y.
I've tried to do this like this:

            var test1 = repo.Find(user.Id, tenant.Id) != null;
            var test2 = repo.GetPagedList(x => x.UserId == user.Id && x.TenantId == tenant.Id).Items.FirstOrDefault()  != null;

If entity exists in db - everything's ok.
But if entity with this PK were removed less then a minute ago test1 may be true (wrong!), and in the same time test2 will be false (right!).
Could you give me some advise please?
EF.Core v1.1.2 and UnitOfWork v1.1.3

This is not an Issue but how to include 3 or more navigation properties

I am getting compilation errors when I try to add multiple navigation properties. My attempted code is:

include: source => source.Include(Menu => Menu.MenuDetails).ThenInclude(MenuDetail => MenuDetail.MenuDetailAddons).ThenInclude(MenuDetail => MenuDetail.MenuDetailPrices) etc

Note that I am able to get the MenuDetails property on menu. However, MenuDetailAddons on MenuDetail fails. How do I achieve this?

Thanks

The include parameter fails

Thanks for a great library.

Whenever I use the include parameter to include a navigation property, I get the following error:

[Fiddler] ReadResponse() failed: The server did not return a complete response for this request. Server returned 2,008 bytes.

When I remove the include, I get result back successfully but without navigation properties populated. But I need those navigation properties with their corresponding data. I am trying to avoid multiple round trips to get those data. What could be the issue?

Below is sample domain data:

public MenuDetail()
{
MenuDetailAddons = new HashSet();
MenuDetailPrices = new HashSet();
MenuRelatedItemsMenuDetails = new HashSet();
MenuRelatedItemsMenuDetailsMenuDetailItems = new HashSet();
}

...
public virtual ICollection MenuDetailAddons { get; set; }
public virtual ICollection MenuDetailPrices { get; set; }

Provides some samples about entity projection and including

Hello.

First, thanks for your work, it's a very good work :)
A good idea should be an page with samples.

I take an example, I have a "Order" object and "OrderItem" object each "Order" has several "OrderItem". I'd like :

  1. a IPagedList with "include" (on "OrderItem") and pageIndex and pageSize
  2. an IPagedList with an anonymous type with "include" (on "OrderItem") and pageIndex and pageSize

Both with order.

Thanks a lot,

Using a stub entity to mark for deletion

        /// <summary>
        /// Deletes the entity by the specified primary key.
        /// </summary>
        /// <param name="id">The primary key value.</param>
        public void Delete(object id) {
            // using a stub entity to mark for deletion
            var typeInfo = typeof(TEntity).GetTypeInfo();
            // REVIEW: using metedata to find the key rather than use hardcode 'id'
            var property = typeInfo.GetProperty("Id");
            if (property != null) {
                var entity = Activator.CreateInstance<TEntity>();
                property.SetValue(entity, id);
                _dbContext.Entry(entity).State = EntityState.Deleted;
            }
            else {
                var entity = _dbSet.Find(id);
                if (entity != null) {
                    Delete(entity);
                }
            }
        }

2.0.1 not does build. System.Data does not exist in namespace System.

Repro steps: Clone repo using VS 2017. Build solution. Tons of errors related to netstandard 2.0 like this:

Severity Code Description Project File Line Suppression State
Error CS0012 The type 'DbConnection' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. Microsoft.EntityFrameworkCore.UnitOfWork C:\Users\user\Downloads\UnitOfWork-2.0.1\src\Microsoft.EntityFrameworkCore.UnitOfWork\UnitOfWork.cs 48 Active

any ideas?

Projection with Automapper

Hi,

I was previously using the following LINQ query for a generic page list with automapper for all my entities:

var projection = queryable.OrderByPropertyOrField(orderBy, ascending).Skip(skipAmount).Take(pageSize).ProjectTo<TReturn>(Mapper.ConfigurationProvider);

Can I use the automapper projection with GetPagedList?

Join third table supported?

Dear friends,
I am not familiar to usage of linq expressions. I need an example to get data like query below for usage of predicate parameter. How can I get data using predicate parameter as following SQL query does? Can predicate paremeter used for joined third table? How can I do that?

var Efforts = effortRepository.FromSql(@" SELECT E.* FROM Effort E
Inner Join Project p on p.ProjectId= E.ProjectId
Inner join ProjectManagers PM on PM.ProjectId=p.ProjectId
Where PM.EmoloyeeId=@p0 AND E.Approved=@p1", EmployeeId, Approved);

Update Disconnected Entity

Hi,

Consider the following scenario: Order is disconnected and have navigation properties, the order already exists in the database but some of the navigation properties are not.

public void SaveOrder(Order order)
{
    var repository = UnitOfWork.GetRepository<Order>();
    repository.Update(order);
    await UnitOfWork.SaveChangesAsync(true);
}

Above mentioned code & scenario causes the following error:

InvalidOperationException: The instance of entity type 'Order' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

Is there any other way besides first getting the existing order and assigning the updated values?

Thanks,
Attiqe

Support MySQL multiple databases/tables sharding

Support MySQL multiple databases/tables sharding in the same model. see my article about this.

In MySQL, physically, a schema is synonymous with a database. You can substitute the keyword SCHEMA instead of DATABASE in MySQL SQL syntax, for example using CREATE SCHEMA instead of CREATE DATABASE. Some other database products draw a distinction. For example, in the Oracle Database product, a schema represents only a part of a database: the tables and other objects owned by a single user.

So, for the simplify, MySQL can only dynamically change the SCHEMA at runtime, but for other databases, I need time to change the EF Core code. However, I'm so busy.

Support For ChangeTracker API

HI,
We Are having Disconnected Entities. We would like to use Changetrackr API for Editing Objects.

Example

Order, Ande OrderDetails (List). Change order Having Add, Modify and Delete of line Items. HOW UOM is supporting the below code which is using dbContext Object.

           ChangeTracker.TrackGraph(
            rootEntity,
            n =>
            {
                var entity = (EntityBase)n.Entry.Entity;
                n.Entry.State = ConvertState(entity.State);
            });

           private static EntityState ConvertState(ObjectState state)
          {
             switch (state)
            {
             case ObjectState.Added:
                return EntityState.Added;
            case ObjectState.Modified:
                return EntityState.Modified;
            case ObjectState.Deleted:
                return EntityState.Deleted;
            default:
                return EntityState.Unchanged;
              }
           }

How to return partial entities and count-sql?

Sorry about the bad subject.

1. How do I return only part of the entity, as with a anonymous type?

var ctx = new BasseContext();
var sales = ctx.SaleAd.Select( x => new {x.Id})

2. GetPagedList seem to produce far from efficient SQL

Example, Count:

var repo = uow.GetRepository<SaleAd>();
var count = repo.GetPagedList().TotalCount;
Console.WriteLine(count);

Produces this:

SELECT [x].[Id], [x].[BodyText], [x].[BodyTextUrls], [x].[PageNumber], [x].[Status], [x].[Timestamp], [x].[Title], [x].[Type]
FROM [SaleAd] AS [x]

Compared to using the DbContext directly:

var sales = ctx.SaleAd.Count();

SELECT COUNT(*)
      FROM [SaleAd] AS [s]

Delete(object id) throws System.NullReferenceException

When I called the Delete method with ID, I got the exception System.NullReferenceException.

After checking the code, I found that the problem is in the line

var typeInfo = typeof(TEntity).GetTypeInfo(); var key = _dbContext.Model.FindEntityType(typeInfo.Name).FindPrimaryKey().Properties.FirstOrDefault();

My entity object is in a different project and has different namespace with the datacontext. typeInfo.Name just return the name of the entity but not the full name (with namespace) of the entity. As a result

_dbContext.Model.FindEntityType(typeInfo.Name) cannot find the entity and throw NullReferenceException.

For example,
_dbContext.Model.FindEntityType("MyEntity") //throws exception

_dbContext.Model.FindEntityType("MyProject.DataModel.MyEntity") //works
_dbContext.Model.FindEntityType(typeInfo) //works too

Update works only for find method

Hi,

Please, could you explain this one?

Success - it changes everything correctly

var find = userRepo.Find(user.UserId);
find.Password = "p@ssword";
unitOfWork.SaveChanges();

Fail - it doesn't change anything

var find = userRepo.GetFirstOrDefault(predicate: c => c.UserId == user.UserId);
find.Password = "p@ssword";
unitOfWork.SaveChanges();

Is it your intention or a bug?

How to use UnitOfWork with Microsoft.AspNetCore.TestHost TestServer for integration tests?

I recently decided to add integration tests to my web project by following Microsoft's Documentation1, but I am having difficulties around this UnitOfWork implementation. The recommended way to perform web integration tests is to use a special nuget package called Microsoft.AspNetCore.TestHost. This creates a special version of a web server and supposedly it will read from your Startup class so you don't have to add everything all over again. I cannot get it to work though. Using the code below, if I remove IUnitOfWork from the HomeController constructor, the test will pass, but the way it is now, it will fail with a 500 error somewhere in the TestServer code. Note that passing around IUnitOfWork works fine in my regular app, the one in Startup that this code supposedly references. it is only when I try to use the finicky TestServer for integration tests that I get the error. Obviously, it must have something to do with IUnitOfWork not being registered properly. But how do do that? You can see I am passing in the InitializeServices delegate to the WebHostBuilder. This was in Microsoft's example code. I'm not 100% sure how all that works. I would have thought saying UseStartup<Startup> would be enough to copy everything I am doing in my working startup. Any thoughts? Note that you must point to a valid ContentRoot physical directory. That was another hard lesson I learned on the road to making TestServer work.

My IntegrationTestSetup.cs

using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.ViewComponents;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;

namespace My.IntegrationTests
{
    public class IntegrationTestSetup<TStartup> : IDisposable
    {
        private readonly TestServer _server;
        public HttpClient Client { get; }

        public IntegrationTestSetup()
        {
            var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;
            var contentRoot = Path.Combine("C:\\point\\to\\your\api\\folder");
            // Arrange
            var builder = new WebHostBuilder()
                .UseContentRoot(contentRoot)
                .ConfigureServices(InitializeServices)                
                .UseEnvironment("Development")
                .UseStartup(typeof(TStartup));

            _server = new TestServer(builder);
            Client = _server.CreateClient();
            Client.BaseAddress = new Uri("http://localhost:3001");
        }

        public void Dispose()
        {
            Client.Dispose();
            _server.Dispose();
        }

        protected virtual void InitializeServices(IServiceCollection services)
        {
            var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;

            // Inject a custom application part manager. 
            // Overrides AddMvcCore() because it uses TryAdd().
            var manager = new ApplicationPartManager();
            manager.ApplicationParts.Add(new AssemblyPart(startupAssembly));
            manager.FeatureProviders.Add(new ControllerFeatureProvider());
            manager.FeatureProviders.Add(new ViewComponentFeatureProvider());
            // adding UoW here did not help
            services.AddUnitOfWork<AffilDbContext>();
            services.AddSingleton(manager);
        }
    }
}

My HomeController.cs

using Microsoft.AspNetCore.Mvc;

namespace My.Api.Controllers
{
    [Produces("application/json")]
    [Route("/")]
    public class HomeController : Controller
    {
        private readonly IUnitOfWork _unitOfWork;
        // removing UnitofWork from the constructor makes this pass the unit test
        public HomeController(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        [HttpPost]
        [HttpGet]
        public IActionResult GetHome()
        {
            return Ok("You are at the base of the Api and there is nothing here. Go to /swagger for project documentation.");
        }
    }
}

My IntegrationTests.cs

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json;
using Xunit;

namespace My.IntegrationTests.Controllers
{
    public class BranchControllerIntegrationTests : IClassFixture<IntegrationTestSetup<Api.Startup>>
    {
        private readonly HttpClient _client;

        public BranchControllerIntegrationTests(IntegrationTestSetup<Api.Startup> fixture)
        {
            _client = fixture.Client;
        }
        [Fact]
        public async Task Home_Get()
        {
            // Act
            try
            {
                // this is broken and always returns 500 if I use a controller with UnitOfWork in the constructor, otherwise it passes
                var response = await _client.GetAsync("/");                
                response.EnsureSuccessStatusCode();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
    }
}

How do you GetAll() ?

Hi,

I notice that Query is deprecated, previously to get all records I would simply do this;

myRepo.Query(x => true)

now it mentions to use GetPagedList- whats the recommended method to simply grab every record from a table?

Thanks

missing async??

Is there a reason only one of the methods has an async version? Why doesn't GetFirstOrDefault?

Package Microsoft.EntityFrameworkCore.UnitOfWork 1.1.2 is not compatible with net461

Is there any chances, that your package would be available for .Net framework on Windows?
It will be really cool. Thanks.
UPD 1: I've added NETStandard.Library 1.6.1 in my NuGet packages on this project - it didn't help
UPD 2: I've also installed Microsoft.EntityFrameworkCore.AutoHistory and Microsoft.EntityFrameworkCore.AutoHistory - it didn't helped either
UPD 3: My project is Asp.Net Core 1.1 web app on .Net Framework 4.6.1

IEnumerablePagedListExtensions VS IQueriablePagedListExtensions and non-async IQueriable

I do not know if this was the intention, but I miss an iqueriable non-asynchronous methodfor the ipaged list estensions.

a friend of mine reported that he was using this extenssion but he didnt know why the paged nonasync method from the repositories was taking so mutch time to execute in relation to the nonasync. I helped him and we found that all nonasync method that executes the pagination ends up using the IEnumerablePagedListExtensions.
the problem is that when you don't use IQueriable, and calls for a paged list that must get some info on a remote database, the program will fist get all list of objects from the database instead of sending a limited query.

Does anyone have any issue on changing the pagedlist from ienumerable to iqueriable?

Transaction Scope.

Hi,
We are using UOW. We would like to use TransactionScope

   using (var transaction = await 
             _dbContext.Database.BeginTransactionAsync(System.Data.IsolationLevel.RepeatableRead))
        {
            {
                var result = await query.ToListAsync();
                transaction.Commit();
                return result;
            }
       }

How to replicate the above code in UOW?

problem using more than one db context in an asp.net core application

Hi Guys,
It is really good library that is used as Repository of EFCore. I have included your project to my project as a reference. But when I have used more than one dbcontext, one of the context does not contain appropriate context that I defined in DBContext. Is this a bug or my mistake? Can you help me to solve this issue.

context of startup.cs
` public void ConfigureServices(IServiceCollection services)
{

        services.AddCors();
        services.Configure<PhotoSettings>(Configuration.GetSection("PhotoSettings"));

        string conn = Configuration.GetConnectionString("Default");
        //string CostCenterConnection = Configuration.GetConnectionString("CostCenters");

        services.AddAutoMapper(); 
        var config = new AutoMapper.MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new MappingProfile());
        });

// I Commented out this line because it doesn't work with more than one dbcontext.
//services.AddDbContext(options =>
//options.UseSqlServer(CostCenterConnection, b => b.MigrationsAssembly("CostCenters.Infrastructure")))
//.AddUnitOfWork();

        var mapper = config.CreateMapper();
        services.AddSingleton(mapper);
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(conn, b => b.MigrationsAssembly("CostCenters.Infrastructure")))
            .AddUnitOfWork<ApplicationDbContext>();

services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("IdentityConnectionString")));
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores();

        services.AddAntiforgery(options =>
        {
            options.HeaderName = "X-XSRF-TOKEN";
        });


        ConfigureAuth(services);

        services.AddMvc();

}`

and this is context of my migration assembly

   `  public class CostCenterDbContextFactory : IDesignTimeDbContextFactory<CostCenterDbContext>
{ 
    public CostCenterDbContext CreateDbContext(string[] args)
    {
        var builder = new DbContextOptionsBuilder<CostCenterDbContext>();
        builder.UseSqlServer(
            "Server=(localdb)\\MssqlLocalDB;Database=CostCenters;Integrated Security=true;MultipleActiveResultSets=true");

        return new CostCenterDbContext(builder.Options);
    }
}
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
        var builder = new DbContextOptionsBuilder<ApplicationDbContext>();
        builder.UseSqlServer(
            "Server=(localdb)\\MssqlLocalDB;Database=study;Integrated Security=true;MultipleActiveResultSets=true");

        return new ApplicationDbContext(builder.Options);
    }
} `

The exception is like that;

System.InvalidOperationException occurred
HResult=0x80131509
Message=Cannot create a DbSet for 'Make' because this type is not included in the model for the context.
Source=
StackTrace:
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable()
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Linq.IQueryable.get_Provider() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsNoTracking[TEntity](IQueryable1 source)
at Microsoft.EntityFrameworkCore.Repository1.GetPagedList(Expression1 predicate, Func2 orderBy, Func2 include, Int32 pageIndex, Int32 pageSize, Boolean disableTracking)
at CostCenters.API.Controllers.DataController.GetMakes() in D:\Projects\CostManager\src\MySolution.API\Controllers\DataController.cs:line 37
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext()

Context of my CostCenterDBContext

`public class CostCenterDbContext : DbContext
{
public DbSet Costs { get; set; }
public DbSet Efforts { get; set; }
public DbSet Employees { get; set; }
public DbSet Projects { get; set; }
public DbSet Salaries { get; set; }
public DbSet SpendingCenters { get; set; }

    public CostCenterDbContext(DbContextOptions<CostCenterDbContext> options)
        : base(options) { }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //for many  to many relationship
        modelBuilder.Entity<ProjectSpendingCenter>()
        .HasKey(t => new { t.ProjectId, t.SpendingCenterId });
        //for many to many relationship
        modelBuilder.Entity<EmployeeProject>()
        .HasKey(t => new { t.EmployeeId, t.ProjectId });
    }
}`

Context of My ApplicationDbContext

`public class ApplicationDbContext : DbContext
{
public DbSet Make { get; set; }
public DbSet Model { get; set; }
public DbSet Vehicle { get; set; }
public DbSet Feature { get; set; }
public DbSet Photos { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.Entity<VehicleFeature>().HasKey(vf => new { vf.VehicleId, vf.FeatureId });
    }
}`

Any suggestion will be help.

Thanks in advance

Ihsan

Example Required

var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.ToList();

How to do using ????

GetPagedList(include: source=>source.Include(p=>p.Post))

SaveAsync issue in UoW

If i am trying to save using SaveAsync method of UoW getting transaction error where has if i use the same with SaveChanges no issues observed. With async though error occurs under the hood data is saved. Could you please let me know what is wrong with below code?

//Client calling UoW
var repo = unitOfWork.GetRepository();
await repo.InsertAsync(ersApplication);
await unitOfWork.SaveChangesAsync();

//Sample code in UOW
public async Task SaveChangesAsync()
{
try
{
return await context.SaveChangesAsync();
}
catch (Exception e)
{
}
return 1;
}

at System.Data.SqlClient.SqlTransaction.ZombieCheck()
at System.Data.SqlClient.SqlTransaction.Commit()
at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Commit()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.d__10.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.d__62.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<SaveChangesAsync>d__54.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<SaveChangesAsync>d__52.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__35.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at MoE.ERS.Repositories.EFCore.UnitOfWork.UnitOfWork`1.d__8.MoveNext() in ..\Data\Repositories.EFCore\UnitOfWork\UnitOfWork.cs:line 75

Need help with querying, 2.0.0 is slow!

I want to return the last log I have:

Is this the correct way to do it? OrderByDescending on ID and the pageSize 1?

var sw = Stopwatch.StartNew();
var repo = uow.GetRepository<Logs>();
var firstLog = repo.GetPagedList(orderBy: o => o.OrderByDescending(oo => oo.Id), pageSize: 1).Items.Single().Id;
logger.Info(firstLog);
logger.Info(sw.ElapsedMilliseconds);

Output:

info: Microsoft.EntityFrameworkCore.Database.Command[200101]
      Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [oo].[id], [oo].[logstring], [oo].[timestamp]
      FROM [Logs] AS [oo]
      ORDER BY [oo].[id] DESC
info: Microsoft.EntityFrameworkCore.Database.Command[200101]
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [oo].[id], [oo].[logstring], [oo].[timestamp]
      FROM [Logs] AS [oo]
      ORDER BY [oo].[id] DESC
 :: INFO  - 38115
 :: INFO  - 6625

Interestingly enough, it does 2 queries to the database! Non wich are using "TOP". Is it fetching everything and then doing like a "Take(1)" in linq?

Since I updated to 2.0.0, this command takes 6-7 seconds(!) to complete!
The EF info says it takes 2ms, what takes 2 ms? And if the DB query takes 2ms, what then takes 6,6 seconds?

If I instead use the "ordinary" Context, this is what I get:

var sw = Stopwatch.StartNew();
                using (var ctx = new BackendContext())
                {
                    var firstLog = ctx.Logs.OrderByDescending(x => x.Id).First().Id;
                    logger.Info(firstLog);
                    logger.Info(sw.ElapsedMilliseconds);
                }


Output:


info: Microsoft.EntityFrameworkCore.Database.Command[200101]
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT TOP(1) [x].[id], [x].[logstring], [x].[timestamp]
      FROM [Logs] AS [x]
      ORDER BY [x].[id] DESC
 :: INFO  - 38115
 :: INFO  - 95

This one does _one_ query with TOP(1) and takes 95 ms!

EDIT, more information:

I did also run SQL-server profiler now.... The top Query is when using the ctx directly, the bottom two is when using GetPagedList.
As you can see, it reads the full table twice, ~3,6 secs * 2
Could you please advise on what I'm doing wrong when using the UnitOfWork?

image

Sorry about long time to solve the issues

Recently, I'm using C++ at my working, but I'm a C++ freshman, so I take much time to study C++11/14/17. The good news is the hard time was passed, so I'll solve the all issues next few weeks. Sorry to all the UoW users.

IUnitOfWork 继承 IDisposable?

请问UnifOfWork释放资源是否是有必要的呢?在平时使用中,不是应该让ASP.NET CORE的Dependency Injection去管理生命周期吗

Cast UnitOfWork Null

This code var uow = unitOfWork as UnitOfWork<DbContext>;
if unitOfWork is UnitOfWork<TDbContext>

uow will null with this casting.

Thanks

'A second operation started on this context ...' error

Hi!
I've some simple code which is being called after user opens a web-page, and sometimes I'm getting an error ('A second operation started on this context ...') here, for example when user double-clicks a link.

var repo = unitOfWork.GetRepository<TenantUser>(); return repo.GetPagedList(x => x.UserId == user.Id && !x.Tenant.IsDisabled, include: x => x.Include(t => t.Tenant), pageSize: int.MaxValue).Items.Select(x=>x.Tenant);
Do I use it correctly?

Implement support for custom repositories?

It would be great to be able to allow custom repositories, derived from the base Repository class with extra methods for specific queries? I was thinking of change the the repositories dictionary in the UnitOfWork to make it protected so you can create your own UnitOfWork inheriting from the base and poke your own custom repositories into the dictionary in the constructor? What do you think?

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.