Skip to content

Commit 9f48d42

Browse files
committed
Get DbContext/Entry straight from the trigger context
1 parent a176cba commit 9f48d42

File tree

7 files changed

+128
-27
lines changed

7 files changed

+128
-27
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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 Microsoft.EntityFrameworkCore;
8+
using Microsoft.EntityFrameworkCore.ChangeTracking;
9+
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
10+
11+
namespace EntityFrameworkCore.Triggered
12+
{
13+
public static class TriggerContextExtensions
14+
{
15+
/// <summary>
16+
/// Get the DbContext EntityEntry for the Entity
17+
/// </summary>
18+
/// <returns>The EntityEntry associated with the DbContext</returns>
19+
/// <exception cref="InvalidOperationException">Throws when the context is not of type <see cref="TriggerContext{TEntity}" /> </exception>
20+
public static EntityEntry<TEntity> GetEntry<TEntity>(this ITriggerContext<TEntity> context)
21+
where TEntity : class
22+
{
23+
if (context is not TriggerContext<TEntity> typedContext)
24+
throw new InvalidOperationException("GetEntry requires ITriggerContext<T> to be of type TriggerContext<T>");
25+
26+
return typedContext.Entry;
27+
}
28+
29+
/// <summary>
30+
/// Get the DbContext associated with this Entity
31+
/// </summary>
32+
/// <returns>The DbContext that fired this trigger</returns>
33+
/// <exception cref="InvalidOperationException">Throws when the context is not of type <see cref="TriggerContext{TEntity}" /> </exception>
34+
public static DbContext GetDbContext<TEntity>(this ITriggerContext<TEntity> context)
35+
where TEntity : class
36+
{
37+
if (context is not TriggerContext<TEntity> typedContext)
38+
throw new InvalidOperationException("GetDbContext requires ITriggerContext<T> to be of type TriggerContext<T>");
39+
40+
return typedContext.Entry.Context;
41+
}
42+
}
43+
}

src/EntityFrameworkCore.Triggered/Internal/TriggerContextDescriptor.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace EntityFrameworkCore.Triggered.Internal
66
{
77
public readonly struct TriggerContextDescriptor
88
{
9-
static readonly ConcurrentDictionary<Type, Func<object, PropertyValues?, ChangeType, EntityBagStateManager, object>> _cachedTriggerContextFactories = new();
9+
static readonly ConcurrentDictionary<Type, Func<EntityEntry, PropertyValues?, ChangeType, EntityBagStateManager, object>> _cachedTriggerContextFactories = new();
1010

1111
readonly EntityEntry _entityEntry;
1212
readonly ChangeType _changeType;
@@ -37,11 +37,11 @@ public object GetTriggerContext(EntityBagStateManager entityBagStateManager)
3737
var entityType = entityEntry.Entity.GetType();
3838

3939
var triggerContextFactory = _cachedTriggerContextFactories.GetOrAdd(entityType, entityType =>
40-
(Func<object, PropertyValues?, ChangeType, EntityBagStateManager, object >)typeof(TriggerContextFactory<>).MakeGenericType(entityType)
40+
(Func<EntityEntry, PropertyValues?, ChangeType, EntityBagStateManager, object >)typeof(TriggerContextFactory<>).MakeGenericType(entityType)
4141
!.GetMethod(nameof(TriggerContextFactory<object>.Activate))
42-
!.CreateDelegate(typeof(Func<object, PropertyValues?, ChangeType, EntityBagStateManager, object>)));
42+
!.CreateDelegate(typeof(Func<EntityEntry, PropertyValues?, ChangeType, EntityBagStateManager, object>)));
4343

44-
return triggerContextFactory(entityEntry.Entity, originalValues, changeType, entityBagStateManager);
44+
return triggerContextFactory(entityEntry, originalValues, changeType, entityBagStateManager);
4545
}
4646
}
4747
}
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
11
using System;
22
using System.Linq.Expressions;
33
using Microsoft.EntityFrameworkCore.ChangeTracking;
4+
using Microsoft.Extensions.DependencyInjection;
45

56
namespace EntityFrameworkCore.Triggered.Internal
67
{
78
public static class TriggerContextFactory<TEntityType>
89
where TEntityType : class
910
{
10-
readonly static Func<object, PropertyValues?, ChangeType, EntityBagStateManager, TriggerContext<TEntityType>> _factoryMethod = CreateFactoryMethod();
11+
readonly static Func<EntityEntry, PropertyValues?, ChangeType, EntityBagStateManager, TriggerContext<TEntityType>> _factoryMethod = CreateFactoryMethod();
1112

12-
static Func<object, PropertyValues?, ChangeType, EntityBagStateManager, TriggerContext<TEntityType>> CreateFactoryMethod()
13+
static Func<EntityEntry, PropertyValues?, ChangeType, EntityBagStateManager, TriggerContext<TEntityType>> CreateFactoryMethod()
1314
{
14-
var entityParamExpression = Expression.Parameter(typeof(object), "object");
15+
var entityEntryParamExpression = Expression.Parameter(typeof(EntityEntry), "entityEntry");
1516
var originalValuesParamExpression = Expression.Parameter(typeof(PropertyValues), "originalValues");
1617
var changeTypeParamExpression = Expression.Parameter(typeof(ChangeType), "changeType");
1718
var entityBagStateManagerExpression = Expression.Parameter(typeof(EntityBagStateManager), "entityBagStateManager");
1819

19-
return Expression.Lambda<Func<object, PropertyValues?, ChangeType, EntityBagStateManager, TriggerContext<TEntityType>>>(
20+
return Expression.Lambda<Func<EntityEntry, PropertyValues?, ChangeType, EntityBagStateManager, TriggerContext<TEntityType>>>(
2021
Expression.New(
21-
typeof(TriggerContext<>).MakeGenericType(typeof(TEntityType)).GetConstructor(new[] { typeof(object), typeof(PropertyValues), typeof(ChangeType), typeof(EntityBagStateManager) })!,
22-
entityParamExpression,
22+
typeof(TriggerContext<>).MakeGenericType(typeof(TEntityType)).GetConstructor(new[] { typeof(EntityEntry), typeof(PropertyValues), typeof(ChangeType), typeof(EntityBagStateManager) })!,
23+
entityEntryParamExpression,
2324
originalValuesParamExpression,
2425
changeTypeParamExpression,
2526
entityBagStateManagerExpression
2627
),
27-
entityParamExpression,
28+
entityEntryParamExpression,
2829
originalValuesParamExpression,
2930
changeTypeParamExpression,
3031
entityBagStateManagerExpression
3132
)
3233
.Compile();
3334
}
3435

35-
public static object Activate(object entity, PropertyValues? originalValues, ChangeType changeType, EntityBagStateManager entityBagStateManager)
36-
=> _factoryMethod(entity, originalValues, changeType, entityBagStateManager);
36+
public static object Activate(EntityEntry entityEntry, PropertyValues? originalValues, ChangeType changeType, EntityBagStateManager entityBagStateManager)
37+
=> _factoryMethod(entityEntry, originalValues, changeType, entityBagStateManager);
3738
}
3839
}

src/EntityFrameworkCore.Triggered/TriggerContext.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@ namespace EntityFrameworkCore.Triggered
77
public class TriggerContext<TEntity> : ITriggerContext<TEntity>
88
where TEntity : class
99
{
10-
readonly TEntity _entity;
10+
readonly EntityEntry _entityEntry;
1111
readonly ChangeType _type;
1212
readonly PropertyValues? _originalValues;
1313
readonly EntityBagStateManager _entityBagStateManager;
1414

1515
TEntity? _unmodifiedEntity;
1616

17-
public TriggerContext(object entity, PropertyValues? originalValues, ChangeType changeType, EntityBagStateManager entityBagStateManager)
17+
public TriggerContext(EntityEntry entityEntry, PropertyValues? originalValues, ChangeType changeType, EntityBagStateManager entityBagStateManager)
1818
{
19-
_entity = (TEntity)entity;
19+
_entityEntry = entityEntry;
2020
_originalValues = originalValues;
2121
_type = changeType;
2222
_entityBagStateManager = entityBagStateManager;
2323
}
2424

2525
public ChangeType ChangeType => _type;
26-
public TEntity Entity => _entity;
26+
public TEntity Entity => (TEntity)_entityEntry.Entity;
2727
public TEntity? UnmodifiedEntity
2828
{
2929
get
@@ -44,6 +44,8 @@ public TEntity? UnmodifiedEntity
4444
}
4545
}
4646

47-
public IDictionary<object, object> Items => _entityBagStateManager.GetForEntity(_entity);
47+
public IDictionary<object, object> Items => _entityBagStateManager.GetForEntity(_entityEntry.Entity);
48+
49+
public EntityEntry<TEntity> Entry => (EntityEntry<TEntity>)_entityEntry;
4850
}
4951
}

test/EntityFrameworkCore.Triggered.Extensions.Tests/GlobalSuppressions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
using System.Diagnostics.CodeAnalysis;
77

8-
[assembly: SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "<Pending>", Scope = "member", Target = "~M:EntityFrameworkCore.Triggered.Extensions.Tests.TriggerContextOptionsBuilderExtensionsTests.AddAssemblyTriggers_AbstractTrigger_GetsIgnored")]
8+
[assembly: SuppressMessage("Usage", "EF1001:Internal EF Core API usage.", Justification = "False warning", Scope = "namespaceanddescendants", Target = "~N:EntityFrameworkCore.Triggered.Extensions.Tests")]
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
using Microsoft.EntityFrameworkCore.Diagnostics;
8+
using Xunit;
9+
10+
namespace EntityFrameworkCore.Triggered.Extensions.Tests
11+
{
12+
public class TriggerContextExtensionsTests
13+
{
14+
record TestEntity(int Id);
15+
16+
class SampleDbContext : DbContext
17+
{
18+
public DbSet<TestEntity> Entities { get; set; }
19+
20+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
21+
{
22+
optionsBuilder.UseInMemoryDatabase(nameof(TriggerContextExtensionsTests));
23+
optionsBuilder.ConfigureWarnings(warningOptions => {
24+
warningOptions.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning);
25+
});
26+
27+
}
28+
}
29+
30+
[Fact]
31+
public void GetDbContext()
32+
{
33+
using var expected = new SampleDbContext();
34+
var entity = new TestEntity(1);
35+
var triggerContext = new TriggerContext<TestEntity>(expected.Add(entity), null, ChangeType.Added, new Internal.EntityBagStateManager());
36+
37+
var actual = triggerContext.GetDbContext();
38+
39+
Assert.Equal(expected, actual);
40+
}
41+
42+
[Fact]
43+
public void GetEntry()
44+
{
45+
using var dbContext = new SampleDbContext();
46+
var entity = new TestEntity(1);
47+
var expected = dbContext.Add(entity);
48+
var triggerContext = new TriggerContext<TestEntity>(expected, null, ChangeType.Added, new Internal.EntityBagStateManager());
49+
50+
var actual = triggerContext.GetEntry();
51+
52+
Assert.Equal(expected, actual);
53+
}
54+
}
55+
}

test/EntityFrameworkCore.Triggered.Tests/TriggerContextTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void UnmodifiedEntity_WhenTypeAdded_IsEmpty()
2727
{
2828
using var dbContext = new TestDbContext();
2929
var sample1 = new TestModel() { Id = 1 };
30-
var subject = new TriggerContext<object>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, ChangeType.Added, new());
30+
var subject = new TriggerContext<object>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, ChangeType.Added, new());
3131

3232
Assert.Null(subject.UnmodifiedEntity);
3333
}
@@ -37,7 +37,7 @@ public void UnmodifiedEntity_WhenTypeDeleted_IsNotEmpty()
3737
{
3838
using var dbContext = new TestDbContext();
3939
var sample1 = new TestModel();
40-
var subject = new TriggerContext<object>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, ChangeType.Deleted, new());
40+
var subject = new TriggerContext<object>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, ChangeType.Deleted, new());
4141

4242
Assert.NotNull(subject.UnmodifiedEntity);
4343
}
@@ -47,7 +47,7 @@ public void UnmodifiedEntity_WhenTypeModified_IsNotEmpty()
4747
{
4848
using var dbContext = new TestDbContext();
4949
var sample1 = new TestModel();
50-
var subject = new TriggerContext<object>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
50+
var subject = new TriggerContext<object>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
5151

5252
Assert.NotNull(subject.UnmodifiedEntity);
5353
}
@@ -60,7 +60,7 @@ public void UnmodifiedEntity_WhenTypeModified_HoldsUnmodifiedStateBeforeSaveChan
6060
dbContext.Add(sample1);
6161
dbContext.SaveChanges();
6262

63-
var subject = new TriggerContext<TestModel>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
63+
var subject = new TriggerContext<TestModel>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
6464
sample1.Name = "test2";
6565

6666
Assert.NotNull(subject.UnmodifiedEntity);
@@ -77,7 +77,7 @@ public void UnmodifiedEntity_WhenTypeModified_HoldsUnmodifiedStateAfterSaveChang
7777
dbContext.Add(sample1);
7878
dbContext.SaveChanges();
7979

80-
var subject = new TriggerContext<TestModel>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues.Clone(), ChangeType.Modified, new());
80+
var subject = new TriggerContext<TestModel>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues.Clone(), ChangeType.Modified, new());
8181
sample1.Name = "test2";
8282

8383
dbContext.SaveChanges();
@@ -91,7 +91,7 @@ public void Entity_IsNeverEmpty()
9191
{
9292
using var dbContext = new TestDbContext();
9393
var sample1 = new TestModel();
94-
var subject = new TriggerContext<object>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, default, new());
94+
var subject = new TriggerContext<object>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, default, new());
9595

9696
Assert.NotNull(subject.Entity);
9797
}
@@ -101,7 +101,7 @@ public void Type_IsNotEmpty()
101101
{
102102
using var dbContext = new TestDbContext();
103103
var sample1 = new TestModel();
104-
var subject = new TriggerContext<object>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
104+
var subject = new TriggerContext<object>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
105105

106106
Assert.Equal(ChangeType.Modified, subject.ChangeType);
107107
}
@@ -111,7 +111,7 @@ public void EntityBag_ConsistentlyReturnsTheSameInstance()
111111
{
112112
using var dbContext = new TestDbContext();
113113
var sample1 = new TestModel();
114-
var subject = new TriggerContext<object>(dbContext.Entry(sample1).Entity, dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
114+
var subject = new TriggerContext<object>(dbContext.Entry(sample1), dbContext.Entry(sample1).OriginalValues, ChangeType.Modified, new());
115115

116116
var expectedInstance = subject.Items;
117117
Assert.Equal(expectedInstance, subject.Items);

0 commit comments

Comments
 (0)