Skip to content

Commit ade4a19

Browse files
committed
Fixed factory issues for trigger instances
1 parent 26bf2cd commit ade4a19

File tree

8 files changed

+188
-27
lines changed

8 files changed

+188
-27
lines changed

src/EntityFrameworkCore.Triggered/Internal/ApplicationTriggerServiceProviderAccessor.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ public IServiceProvider GetTriggerServiceProvider()
5757
}
5858
else if (_scopedServiceProviderTransform != null)
5959
{
60-
6160
var dbContextOptions = _internalServiceProvider.GetRequiredService<IDbContextOptions>();
6261
var coreOptionsExtension = dbContextOptions.FindExtension<CoreOptionsExtension>();
6362
var serviceProvider = coreOptionsExtension.ApplicationServiceProvider ?? _internalServiceProvider;
@@ -66,8 +65,8 @@ public IServiceProvider GetTriggerServiceProvider()
6665
}
6766
else
6867
{
69-
_serviceScope = _internalServiceProvider.CreateScope();
70-
_applicationScopedServiceProvider = _serviceScope.ServiceProvider;
68+
var dbContext = _internalServiceProvider.GetRequiredService<ICurrentDbContext>().Context;
69+
_applicationScopedServiceProvider = new HybridServiceProvider(_internalServiceProvider, dbContext);
7170
}
7271

7372
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Microsoft.EntityFrameworkCore;
7+
8+
namespace EntityFrameworkCore.Triggered.Internal
9+
{
10+
public sealed class HybridServiceProvider : IServiceProvider
11+
{
12+
readonly DbContext _dbContext;
13+
readonly IServiceProvider _serviceProvider;
14+
15+
public HybridServiceProvider(IServiceProvider serviceProvider, DbContext dbContext)
16+
{
17+
_serviceProvider = serviceProvider;
18+
_dbContext = dbContext;
19+
}
20+
21+
public object? GetService(Type serviceType)
22+
{
23+
var result = _serviceProvider.GetService(serviceType);
24+
if (result is not null)
25+
{
26+
return result;
27+
}
28+
29+
if (typeof(DbContext).IsAssignableFrom(serviceType))
30+
{
31+
return _dbContext;
32+
}
33+
34+
return default;
35+
}
36+
}
37+
}

src/EntityFrameworkCore.Triggered/Internal/TriggerFactory.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,9 @@ public IEnumerable<object> Resolve(IServiceProvider serviceProvider, Type trigge
3333
var triggerServiceFactories = _internalServiceProvider.GetServices(typeof(ITriggerInstanceFactory<>).MakeGenericType(triggerType)).Cast<ITriggerInstanceFactory>();
3434
if (triggerServiceFactories.Any())
3535
{
36-
var dbContext = _internalServiceProvider.GetService<Microsoft.EntityFrameworkCore.Infrastructure.ICurrentDbContext>()?.Context;
37-
3836
foreach (var triggerServiceFactory in triggerServiceFactories)
3937
{
40-
yield return triggerServiceFactory.Create(dbContext, serviceProvider);
38+
yield return triggerServiceFactory.Create(serviceProvider);
4139
}
4240
}
4341
}

src/EntityFrameworkCore.Triggered/Internal/TriggerInstanceFactory.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace EntityFrameworkCore.Triggered.Internal
1111

1212
public interface ITriggerInstanceFactory
1313
{
14-
object Create(DbContext? dbContext, IServiceProvider serviceProvider);
14+
object Create(IServiceProvider serviceProvider);
1515
}
1616

1717
public interface ITriggerInstanceFactory<out TTriggerType> : ITriggerInstanceFactory
@@ -21,23 +21,24 @@ public interface ITriggerInstanceFactory<out TTriggerType> : ITriggerInstanceFac
2121

2222
public sealed class TriggerInstanceFactory<TTriggerType> : ITriggerInstanceFactory<TTriggerType>
2323
{
24-
readonly object? _serviceInstance;
24+
object? _instance;
2525

26-
public TriggerInstanceFactory(object? serviceInstance)
26+
public TriggerInstanceFactory(object? instance)
2727
{
28-
_serviceInstance = serviceInstance;
28+
_instance = instance;
2929
}
3030

31-
public object Create(DbContext? dbContext, IServiceProvider serviceProvider)
31+
public object Create(IServiceProvider serviceProvider)
3232
{
33-
if (_serviceInstance is not null)
33+
if (_instance is not null)
3434
{
35-
return _serviceInstance;
35+
return _instance;
3636
}
3737

38-
var arguments = dbContext is not null ? new object[] { dbContext } : Array.Empty<object>();
38+
// todo: create object factory and cache
39+
_instance = ActivatorUtilities.CreateInstance(serviceProvider, typeof(TTriggerType));
3940

40-
return ActivatorUtilities.CreateInstance(serviceProvider, typeof(TTriggerType), arguments);
41+
return _instance;
4142
}
4243
}
4344
}

test/EntityFrameworkCore.Triggered.Tests/EntityFrameworkCore.Triggered.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
<ItemGroup>
1515
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
16+
<PackageReference Include="ScenarioTests.XUnit" Version="0.3.2" />
1617
<PackageReference Include="xunit" Version="2.4.1" />
1718
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
1819
<PrivateAssets>all</PrivateAssets>
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using EntityFrameworkCore.Triggered.Internal;
7+
using Microsoft.EntityFrameworkCore;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using ScenarioTests;
10+
using Xunit;
11+
12+
namespace EntityFrameworkCore.Triggered.Tests.Internal
13+
{
14+
public partial class HybridServiceProviderTests
15+
{
16+
class CustomDbContext : DbContext
17+
{
18+
19+
}
20+
21+
class ConcreteCustomDbContext : CustomDbContext
22+
{
23+
24+
}
25+
26+
[Scenario]
27+
public void ScenarioTests(ScenarioContext scenario)
28+
{
29+
var dbContext = new ConcreteCustomDbContext();
30+
var serviceProvider = new ServiceCollection()
31+
.AddSingleton("X")
32+
.BuildServiceProvider();
33+
34+
var subject = new HybridServiceProvider(serviceProvider, dbContext);
35+
36+
scenario.Fact("Can retrieve DbContext from ServiceProvider", () => {
37+
var result = subject.GetService<DbContext>();
38+
Assert.NotNull(result);
39+
});
40+
41+
scenario.Fact("Can retrieve CustomDbContext from ServiceProvider", () => {
42+
var result = subject.GetService<CustomDbContext>();
43+
Assert.NotNull(result);
44+
});
45+
46+
scenario.Fact("Can retrieve ConcreteCustomDbContext from ServiceProvider", () => {
47+
var result = subject.GetService<ConcreteCustomDbContext>();
48+
Assert.NotNull(result);
49+
});
50+
51+
scenario.Fact("Can retrieve custom service from ServiceProvider", () => {
52+
var result = subject.GetService<string>();
53+
Assert.NotNull(result);
54+
});
55+
}
56+
57+
[Fact]
58+
public void CanOverrideDbContext()
59+
{
60+
var dbContext1 = new ConcreteCustomDbContext();
61+
var dbContext2 = new ConcreteCustomDbContext();
62+
63+
var serviceProvider = new ServiceCollection()
64+
.AddSingleton(dbContext2)
65+
.BuildServiceProvider();
66+
67+
var subject = new HybridServiceProvider(serviceProvider, dbContext1);
68+
69+
var result = subject.GetRequiredService<ConcreteCustomDbContext>();
70+
71+
Assert.Equal(dbContext2, result);
72+
}
73+
74+
}
75+
}

test/EntityFrameworkCore.Triggered.Tests/Internal/TriggerFactoryTests.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,5 @@ public void Resolve_FromInternalServices_GetsConstructedUsingExternalServiceProv
111111
Assert.NotNull(trigger);
112112
Assert.Equal(subject, trigger.TriggerFactory);
113113
}
114-
115-
[Fact]
116-
public void Resolve_FromInternalServices_AcceptsTheDbContextAsAnArugmnet()
117-
{
118-
using var dbContext = new SampleDbContext3();
119-
var subject = dbContext.GetService<TriggerFactory>();
120-
121-
var trigger = subject.Resolve(dbContext.GetInfrastructure(), typeof(IBeforeSaveTrigger<object>)).FirstOrDefault() as SampleTrigger3;
122-
123-
Assert.NotNull(trigger);
124-
Assert.Equal(dbContext, trigger.DbContext);
125-
}
126114
}
127115
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using EntityFrameworkCore.Triggered.Internal;
8+
using EntityFrameworkCore.Triggered.Tests.Stubs;
9+
using Microsoft.EntityFrameworkCore;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using ScenarioTests;
12+
using Xunit;
13+
14+
namespace EntityFrameworkCore.Triggered.Tests.Internal
15+
{
16+
public partial class TriggerInstanceFactoryTests
17+
{
18+
class SampleDbContext : DbContext
19+
{
20+
public SampleDbContext(DbContextOptions options) : base(options)
21+
{
22+
}
23+
}
24+
25+
class SampleTrigger1 { }
26+
class SampleTrigger2 { public SampleTrigger2(SampleDbContext sampleDbContext) { } }
27+
28+
[Scenario]
29+
public void TriggerInstanceFactoriesBehaviorTests(ScenarioContext scenario)
30+
{
31+
var serviceProvider = new ServiceCollection()
32+
.AddDbContext<SampleDbContext>()
33+
.BuildServiceProvider();
34+
35+
var dbContext = serviceProvider.GetRequiredService<SampleDbContext>();
36+
37+
ITriggerInstanceFactory subject = new TriggerInstanceFactory<SampleTrigger1>(null);
38+
var instance = subject.Create(serviceProvider);
39+
40+
scenario.Fact("Creates an instance on first request", () => {
41+
Assert.NotNull(instance);
42+
});
43+
44+
scenario.Fact("Caches that instance on subsequent requests", () => {
45+
var secondInstance = subject.Create(serviceProvider);
46+
Assert.Equal(instance, secondInstance);
47+
});
48+
49+
scenario.Fact("When provided with an initial instance, it will always return that", () => {
50+
subject = new TriggerInstanceFactory<SampleTrigger1>(instance);
51+
var secondInstance = subject.Create(serviceProvider);
52+
Assert.Equal(instance, secondInstance);
53+
});
54+
55+
scenario.Fact("Gets provided the DbContext when its requested", () => {
56+
subject = new TriggerInstanceFactory<SampleTrigger2>(null);
57+
instance = subject.Create(serviceProvider);
58+
Assert.NotNull(instance);
59+
});
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)