Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/build-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
dotnet-version: |
8.0.x
9.0.x
- name: Set BUILD_CONFIGURATION
if: startsWith(github.ref, 'refs/tags/')
run: echo "BUILD_CONFIGURATION=Release" >> $GITHUB_ENV
- name: Restore
run: dotnet restore
- name: Build
Expand Down Expand Up @@ -58,6 +61,33 @@ jobs:
path: ./test-results
if: ${{ always() }} # Always run this step even on failure

integration-tests:
runs-on: ubuntu-latest
needs: build
name: Integration Tests
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
- name: Set BUILD_CONFIGURATION
if: startsWith(github.ref, 'refs/tags/')
run: echo "BUILD_CONFIGURATION=Release" >> $GITHUB_ENV
- name: Restore
run: dotnet restore
- name: Run Integration Tests
run: dotnet test --configuration ${{env.BUILD_CONFIGURATION}} --filter "Category=Integration" --logger trx --results-directory integration-test-results
- name: Upload integration test results
uses: actions/upload-artifact@v4
with:
name: integration-test-results
path: ./integration-test-results

deploy-testing:
if: github.event_name == 'push'
runs-on: ubuntu-latest
Expand Down
48 changes: 33 additions & 15 deletions src/AppCoreNet.Data.EntityFramework/DbContextDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using AppCoreNet.Diagnostics;
Expand All @@ -16,56 +17,73 @@ namespace AppCoreNet.Data.EntityFramework;
public sealed class DbContextDataProvider<TDbContext> : IDataProvider
where TDbContext : DbContext
{
private readonly string _name;
private readonly DbContextDataProviderServices<TDbContext> _services;

/// <inheritdoc />
public string Name => _name;
public string Name { get; }

/// <summary>
/// Gets the <see cref="DbContext"/> of the data provider.
/// </summary>
/// <value>The <see cref="DbContext"/>.</value>
public TDbContext DbContext => _services.DbContext;
public TDbContext DbContext { get; }

/// <summary>
/// Gets the <see cref="IEntityMapper"/> of the data provider.
/// </summary>
public IEntityMapper EntityMapper => _services.EntityMapper;
public IEntityMapper EntityMapper { get; }

/// <summary>
/// Gets the <see cref="ITokenGenerator"/> of the data provider.
/// </summary>
public ITokenGenerator TokenGenerator => _services.TokenGenerator;
public ITokenGenerator TokenGenerator { get; }

/// <summary>
/// Gets the <see cref="DbContextQueryHandlerFactory{TDbContext}"/> of the data provider.
/// </summary>
public DbContextQueryHandlerFactory<TDbContext> QueryHandlerFactory => _services.QueryHandlerFactory;
public DbContextQueryHandlerFactory<TDbContext> QueryHandlerFactory { get; }

/// <summary>
/// Gets the <see cref="DbContextTransactionManager{TDbContext}"/>.
/// </summary>
public DbContextTransactionManager<TDbContext> TransactionManager => _services.TransactionManager;
public DbContextTransactionManager<TDbContext> TransactionManager { get; }

ITransactionManager IDataProvider.TransactionManager => TransactionManager;

internal DataProviderLogger<DbContextDataProvider<TDbContext>> Logger => _services.Logger;
internal DataProviderLogger<DbContextDataProvider<TDbContext>> Logger { get; }

/// <summary>
/// Initializes a new instance of the <see cref="DbContextDataProvider{TDbContext}"/> class.
/// </summary>
/// <param name="name">The name of the data provider.</param>
/// <param name="services">The <see cref="DbContextDataProviderServices{TDbContext}"/>.</param>
/// <param name="dbContext">The <see cref="DbContext"/>.</param>
/// <param name="entityMapper">The <see cref="IEntityMapper"/>.</param>
/// <param name="tokenGenerator">The <see cref="ITokenGenerator"/>.</param>
/// <param name="queryHandlerFactory">The <see cref="DbContextQueryHandlerFactory{TDbContext}"/>.</param>
/// <param name="transactionManager">The <see cref="DbContextTransactionManager{TDbContext}"/>.</param>
/// <param name="logger">The <see cref="DataProviderLogger{T}"/>.</param>
public DbContextDataProvider(
string name,
DbContextDataProviderServices<TDbContext> services)
TDbContext dbContext,
IEntityMapper entityMapper,
ITokenGenerator tokenGenerator,
DbContextQueryHandlerFactory<TDbContext> queryHandlerFactory,
DbContextTransactionManager<TDbContext> transactionManager,
DataProviderLogger<DbContextDataProvider<TDbContext>> logger)
{
Ensure.Arg.NotNull(name);
Ensure.Arg.NotNull(services);
Ensure.Arg.NotNull(dbContext);
Ensure.Arg.NotNull(entityMapper);
Ensure.Arg.NotNull(tokenGenerator);
Ensure.Arg.NotNull(queryHandlerFactory);
Ensure.Arg.NotNull(transactionManager);
Ensure.Arg.NotNull(logger);

_name = name;
_services = services;
Name = name;
DbContext = dbContext;
EntityMapper = entityMapper;
TokenGenerator = tokenGenerator;
QueryHandlerFactory = queryHandlerFactory;
TransactionManager = transactionManager;
Logger = logger;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@

using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;

namespace AppCoreNet.Data.EntityFramework;

internal sealed class DbContextDataProviderOptions
{
public Type? EntityMapperType { get; set; }
private static readonly Func<IServiceProvider, IEntityMapper> _defaultEntityMapperFactory =
static sp => sp.GetRequiredService<IEntityMapper>();

public Type? TokenGeneratorType { get; set; }
private static readonly Func<IServiceProvider, ITokenGenerator> _defaultTokenGeneratorFactory =
static sp => sp.GetRequiredService<ITokenGenerator>();

public Func<IServiceProvider, IEntityMapper> EntityMapperFactory { get; set; } = _defaultEntityMapperFactory;

public Func<IServiceProvider, ITokenGenerator> TokenGeneratorFactory { get; set; } = _defaultTokenGeneratorFactory;

public ISet<Type> QueryHandlerTypes { get; } = new HashSet<Type>();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,8 @@ public EntityFrameworkDataProviderBuilder<TDbContext> AddRepository<TService, TI
typeof(TService),
new Func<IServiceProvider, TImplementation>(sp =>
{
var provider =
(DbContextDataProvider<TDbContext>)sp.GetRequiredService<IDataProviderResolver>()
.Resolve(Name);

var resolver = sp.GetRequiredService<IDataProviderResolver>();
var provider = (DbContextDataProvider<TDbContext>)resolver.Resolve(Name);
return ActivatorUtilities.CreateInstance<TImplementation>(sp, provider);
}),
ProviderLifetime));
Expand Down Expand Up @@ -120,7 +118,9 @@ public EntityFrameworkDataProviderBuilder<TDbContext> AddTokenGenerator<T>()
{
Services.Configure<DbContextDataProviderOptions>(
Name,
o => o.TokenGeneratorType = typeof(T));
o =>
o.TokenGeneratorFactory = static sp =>
ActivatorUtilities.GetServiceOrCreateInstance<T>(sp));

return this;
}
Expand All @@ -135,7 +135,9 @@ public EntityFrameworkDataProviderBuilder<TDbContext> AddEntityMapper<T>()
{
Services.Configure<DbContextDataProviderOptions>(
Name,
o => o.EntityMapperType = typeof(T));
o =>
o.EntityMapperFactory = static sp =>
ActivatorUtilities.GetServiceOrCreateInstance<T>(sp));

return this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Licensed under the MIT license.
// Copyright (c) The AppCore .NET project.

using System;
using System.Data.Entity;
using AppCoreNet.Data;
using AppCoreNet.Data.EntityFramework;
using AppCoreNet.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;

// ReSharper disable once CheckNamespace
namespace AppCoreNet.Extensions.DependencyInjection;
Expand All @@ -18,9 +21,6 @@ public static class EntityFrameworkDataProviderBuilderExtensions
/// <summary>
/// Registers a <see cref="DbContext"/> data provider in the <see cref="IServiceCollection"/>.
/// </summary>
/// <remarks>
/// Note that you have to register the <see cref="DbContext"/> on your own.
/// </remarks>
/// <typeparam name="TDbContext">The type of the <see cref="DbContext"/>.</typeparam>
/// <param name="builder">The <see cref="IDataProviderBuilder"/>.</param>
/// <param name="name">The name of the data provider.</param>
Expand All @@ -35,23 +35,58 @@ public static EntityFrameworkDataProviderBuilder<TDbContext> AddEntityFramework<
Ensure.Arg.NotNull(builder);
Ensure.Arg.NotNull(name);

builder.Services.AddOptions();
builder.Services.AddLogging();
IServiceCollection services = builder.Services;

services.AddOptions();
services.AddLogging();

builder.Services.TryAdd(ServiceDescriptor.Describe(typeof(TDbContext), typeof(TDbContext), providerLifetime));
services.TryAdd(
ServiceDescriptor.Describe(
typeof(TDbContext),
typeof(TDbContext),
providerLifetime));

services.TryAdd(
ServiceDescriptor.Describe(
typeof(DbContextTransactionManager<TDbContext>),
typeof(DbContextTransactionManager<TDbContext>),
providerLifetime));

services.TryAdd(
ServiceDescriptor.DescribeKeyed(
typeof(DbContextQueryHandlerFactory<TDbContext>),
name,
static (sp, name) =>
{
DbContextDataProviderOptions options = GetOptions(sp, (string)name!);
return new DbContextQueryHandlerFactory<TDbContext>(sp, options.QueryHandlerTypes);
},
providerLifetime));

builder.AddProvider<DbContextDataProvider<TDbContext>>(
name,
providerLifetime,
static (sp, name) =>
{
DbContextDataProviderServices<TDbContext> services =
DbContextDataProviderServices<TDbContext>.Create(name, sp);
DbContextDataProviderOptions options = GetOptions(sp, name);

return new DbContextDataProvider<TDbContext>(name, services);
return new DbContextDataProvider<TDbContext>(
name,
sp.GetRequiredService<TDbContext>(),
options.EntityMapperFactory(sp),
options.TokenGeneratorFactory(sp),
sp.GetRequiredKeyedService<DbContextQueryHandlerFactory<TDbContext>>(name),
sp.GetRequiredService<DbContextTransactionManager<TDbContext>>(),
sp.GetRequiredService<DataProviderLogger<DbContextDataProvider<TDbContext>>>());
});

return new EntityFrameworkDataProviderBuilder<TDbContext>(name, builder.Services, providerLifetime);
return new EntityFrameworkDataProviderBuilder<TDbContext>(name, services, providerLifetime);

static DbContextDataProviderOptions GetOptions(IServiceProvider sp, string name)
{
var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<DbContextDataProviderOptions>>();
return optionsMonitor.Get(name);
}
}

/// <summary>
Expand Down
Loading