Skip to content

Commit 0fdca16

Browse files
author
zzzprojects
committed
Fix Audit for EFCore && Many to Many
Fix Audit for EFCore && Many to Many
1 parent ce99a4a commit 0fdca16

File tree

7 files changed

+165
-26
lines changed

7 files changed

+165
-26
lines changed

src/shared/Z.EF.Plus.Audit.Shared/Audit/PostSaveChanges.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88

99
using System;
10+
using System.Linq;
1011

1112
#if EF5
1213
using System.Data;
@@ -15,6 +16,7 @@
1516
#elif EF6
1617

1718
#elif EFCORE
19+
using Microsoft.EntityFrameworkCore;
1820
using Microsoft.EntityFrameworkCore.ChangeTracking;
1921

2022
#endif
@@ -59,12 +61,23 @@ public static void PostSaveChanges(Audit audit)
5961
}
6062
}
6163
#elif EFCORE
62-
foreach (var property in entry.Properties)
64+
if (entry.State == AuditEntryState.EntityModified && entry.Entry.State == EntityState.Detached)
6365
{
64-
if (!property.IsValueSet)
66+
// Oops! It's has not beed modified but deleted
67+
entry.State = AuditEntryState.EntityDeleted;
68+
69+
var listToRemove = entry.Properties.Where(x => !x.PropertyEntry.Metadata.IsKey()).ToList();
70+
listToRemove.ForEach(x => entry.Properties.Remove(x));
71+
}
72+
else
73+
{
74+
foreach (var property in entry.Properties)
6575
{
66-
var currentValue = property.PropertyEntry.CurrentValue;
67-
property.NewValue = currentValue;
76+
if (!property.IsValueSet)
77+
{
78+
var currentValue = property.PropertyEntry.CurrentValue;
79+
property.NewValue = currentValue;
80+
}
6881
}
6982
}
7083
#endif

src/shared/Z.EF.Plus.QueryCache.Shared/QueryCacheManager.cs

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ public static Func<MemoryCacheEntryOptions> MemoryCacheEntryOptionsFactory
165165
/// <value>true if use tag as cache key, false if not.</value>
166166
public static bool UseTagsAsCacheKey { get; set; }
167167

168+
/// <summary>
169+
/// Gets or sets a value indicating whether this object is command information optional for cache
170+
/// key.
171+
/// </summary>
172+
/// <value>
173+
/// True if this object is command information optional for cache key, false if not.
174+
/// </value>
175+
public static bool IsCommandInfoOptionalForCacheKey { get; set; }
176+
168177
/// <summary>Adds cache tags corresponding to a cached key in the CacheTags dictionary.</summary>
169178
/// <param name="cacheKey">The cache key.</param>
170179
/// <param name="tags">A variable-length parameters list containing tags corresponding to the <paramref name="cacheKey" />.</param>
@@ -257,16 +266,22 @@ public static string GetCacheKey(IQueryable query, string[] tags)
257266
}
258267
}
259268

260-
var objectQuery = query.GetObjectQuery();
269+
if (IsCommandInfoOptionalForCacheKey && !UseFirstTagAsCacheKey && !UseTagsAsCacheKey)
270+
{
271+
throw new Exception(ExceptionMessage.QueryCache_IsCommandInfoOptionalForCacheKey_Invalid);
272+
}
273+
274+
var objectQuery = IsCommandInfoOptionalForCacheKey ? query.GetObjectQuerySafe() : query.GetObjectQuery();
261275

262276
sb.AppendLine(CachePrefix);
263277

264-
if (IncludeConnectionInCacheKey)
278+
if (IncludeConnectionInCacheKey && objectQuery != null)
265279
{
266280
sb.AppendLine(GetConnectionStringForCacheKey(query));
267281
}
268282
#elif EFCORE
269-
RelationalQueryContext queryContext;
283+
RelationalQueryContext queryContext = null;
284+
270285
var command = query.CreateCommand(out queryContext);
271286

272287
sb.AppendLine(CachePrefix);
@@ -302,27 +317,35 @@ public static string GetCacheKey(IQueryable query, string[] tags)
302317
sb.AppendLine(string.Join(";", tags));
303318

304319
#if EF5
305-
sb.AppendLine(objectQuery.ToTraceString());
306-
307-
foreach (var parameter in objectQuery.Parameters)
320+
if (objectQuery != null)
308321
{
309-
sb.Append(parameter.Name);
310-
sb.Append(";");
311-
sb.Append(parameter.Value);
312-
sb.AppendLine(";");
322+
sb.AppendLine(objectQuery.ToTraceString());
323+
324+
foreach (var parameter in objectQuery.Parameters)
325+
{
326+
sb.Append(parameter.Name);
327+
sb.Append(";");
328+
sb.Append(parameter.Value);
329+
sb.AppendLine(";");
330+
}
313331
}
314-
#elif EF6
315-
var commandTextAndParameters = objectQuery.GetCommandTextAndParameters();
316-
sb.AppendLine(commandTextAndParameters.Item1);
317332

318-
foreach (DbParameter parameter in commandTextAndParameters.Item2)
333+
#elif EF6
334+
if (objectQuery != null)
319335
{
320-
sb.Append(parameter.ParameterName);
321-
sb.Append(";");
322-
sb.Append(parameter.Value);
323-
sb.AppendLine(";");
336+
var commandTextAndParameters = objectQuery.GetCommandTextAndParameters();
337+
sb.AppendLine(commandTextAndParameters.Item1);
338+
339+
foreach (DbParameter parameter in commandTextAndParameters.Item2)
340+
{
341+
sb.Append(parameter.ParameterName);
342+
sb.Append(";");
343+
sb.Append(parameter.Value);
344+
sb.AppendLine(";");
345+
}
324346
}
325347
#elif EFCORE
348+
326349
sb.AppendLine(query.Expression.ToString());
327350
sb.AppendLine(command.CommandText);
328351

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Description: Entity Framework Bulk Operations & Utilities (EF Bulk SaveChanges, Insert, Update, Delete, Merge | LINQ Query Cache, Deferred, Filter, IncludeFilter, IncludeOptimize | Audit)
2+
// Website & Documentation: https://github.com/zzzprojects/Entity-Framework-Plus
3+
// Forum & Issues: https://github.com/zzzprojects/EntityFramework-Plus/issues
4+
// License: https://github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE
5+
// More projects: http://www.zzzprojects.com/
6+
// Copyright © ZZZ Projects Inc. 2014 - 2016. All rights reserved.
7+
8+
#if FULL || BATCH_DELETE || BATCH_UPDATE || QUERY_CACHE || QUERY_FILTER
9+
#if EF5 || EF6
10+
using System;
11+
using System.Data.Entity;
12+
using System.Data.Entity.Infrastructure;
13+
using System.Linq;
14+
using System.Reflection;
15+
#if EF5
16+
using System.Data.Objects;
17+
18+
#elif EF6
19+
using System.Data.Entity.Core.Objects;
20+
21+
#endif
22+
23+
namespace Z.EntityFramework.Plus
24+
{
25+
internal static partial class InternalExtensions
26+
{
27+
/// <summary>An IQueryable&lt;TEntity&gt; extension method that get the ObjectQuery from the query.</summary>
28+
/// <param name="query">The query to get the ObjectQuery from.</param>
29+
/// <returns>The ObjectQuery from the query.</returns>
30+
internal static ObjectQuery GetObjectQuerySafe(this IQueryable query)
31+
{
32+
// CHECK ObjectQuery
33+
var objectQuery = query as ObjectQuery;
34+
if (objectQuery != null)
35+
{
36+
return objectQuery;
37+
}
38+
39+
// CHECK DbQuery
40+
var dbQuery = query as DbQuery;
41+
42+
if (dbQuery != null)
43+
{
44+
var internalQueryProperty = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.NonPublic | BindingFlags.Instance);
45+
var internalQuery = internalQueryProperty.GetValue(dbQuery, null);
46+
var objectQueryContextProperty = internalQuery.GetType().GetProperty("ObjectQuery", BindingFlags.Public | BindingFlags.Instance);
47+
var objectQueryContext = objectQueryContextProperty.GetValue(internalQuery, null);
48+
49+
objectQuery = objectQueryContext as ObjectQuery;
50+
51+
return objectQuery;
52+
}
53+
54+
if (query.GetType().IsGenericType && query.GetType().GetGenericTypeDefinition() == typeof(DbQuery<>))
55+
{
56+
var internalQueryProperty = typeof(DbQuery<>).MakeGenericType(query.ElementType).GetProperty("InternalQuery", BindingFlags.NonPublic | BindingFlags.Instance);
57+
var internalQuery = internalQueryProperty.GetValue(query, null);
58+
var objectQueryContextProperty = internalQuery.GetType().GetProperty("ObjectQuery", BindingFlags.Public | BindingFlags.Instance);
59+
var objectQueryContext = objectQueryContextProperty.GetValue(internalQuery, null);
60+
61+
objectQuery = objectQueryContext as ObjectQuery;
62+
63+
return objectQuery;
64+
}
65+
66+
if (query.GetType().IsGenericType && query.GetType().GetGenericTypeDefinition() == typeof(DbSet<>))
67+
{
68+
var internalQueryProperty = typeof(DbSet<>).MakeGenericType(query.ElementType).GetProperty("InternalQuery", BindingFlags.NonPublic | BindingFlags.Instance);
69+
var internalQuery = internalQueryProperty.GetValue(query, null);
70+
var objectQueryContextProperty = internalQuery.GetType().GetProperty("ObjectQuery", BindingFlags.Public | BindingFlags.Instance);
71+
var objectQueryContext = objectQueryContextProperty.GetValue(internalQuery, null);
72+
73+
objectQuery = objectQueryContext as ObjectQuery;
74+
75+
return objectQuery;
76+
}
77+
78+
return null;
79+
}
80+
}
81+
}
82+
83+
#endif
84+
#endif

src/shared/Z.EF.Plus._Core.Shared/EFCore/IQueryable`/CreateCommand.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,12 @@ public static IRelationalCommand CreateCommand(this IQueryable source, out Relat
177177

178178
// REFLECTION: Query.Provider._queryCompiler._database._queryCompilationContextFactory.Logger
179179
var loggerField = queryCompilationContextFactory.GetType().GetProperty("Logger", BindingFlags.NonPublic | BindingFlags.Instance);
180-
var logger = (ISensitiveDataLogger)loggerField.GetValue(queryCompilationContextFactory);
180+
var logger = loggerField.GetValue(queryCompilationContextFactory);
181181

182182
// CREATE new query from query visitor
183-
newQuery = ParameterExtractingExpressionVisitor.ExtractParameters(source.Expression, queryContext, evaluatableExpressionFilter, logger);
183+
var extractParametersMethods = typeof(ParameterExtractingExpressionVisitor).GetMethod("ExtractParameters", BindingFlags.Public | BindingFlags.Static);
184+
newQuery = (Expression) extractParametersMethods.Invoke(null, new object[] {source.Expression, queryContext, evaluatableExpressionFilter, logger});
185+
//ParameterExtractingExpressionVisitor.ExtractParameters(source.Expression, queryContext, evaluatableExpressionFilter, logger);
184186
}
185187

186188
//var query = new QueryAnnotatingExpressionVisitor().Visit(source.Expression);

src/shared/Z.EF.Plus._Core.Shared/EFCore/IQueryable`/GetDbContext.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,23 @@ public static DbContext GetDbContext(this IQueryable query)
8181
var queryContextFactory = (RelationalQueryContextFactory) queryContextFactoryField.GetValue(compiler);
8282

8383
#if EFCORE
84-
var stateManagerField = typeof(QueryContextFactory).GetProperty("StateManager", BindingFlags.NonPublic | BindingFlags.Instance);
85-
var stateManagerDynamic = stateManagerField.GetValue(queryContextFactory);
84+
object stateManagerDynamic;
85+
86+
var dependenciesProperty = typeof(RelationalQueryContextFactory).GetProperty("Dependencies", BindingFlags.NonPublic | BindingFlags.Instance);
87+
if (dependenciesProperty != null)
88+
{
89+
// EFCore 2.x
90+
var dependencies = dependenciesProperty.GetValue(queryContextFactory);
91+
92+
var stateManagerField = typeof(DbContext).GetTypeFromAssembly_Core("Microsoft.EntityFrameworkCore.Query.QueryContextDependencies").GetProperty("StateManager", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
93+
stateManagerDynamic = stateManagerField.GetValue(dependencies);
94+
}
95+
else
96+
{
97+
// EFCore 1.x
98+
var stateManagerField = typeof(QueryContextFactory).GetProperty("StateManager", BindingFlags.NonPublic | BindingFlags.Instance);
99+
stateManagerDynamic = stateManagerField.GetValue(queryContextFactory);
100+
}
86101

87102
IStateManager stateManager = stateManagerDynamic as IStateManager;
88103

src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\EntityConnection\GetStoreConnection.cs" />
3131
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\EntityConnection\GetStoreTransaction.cs" />
3232
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\IDbSet`\GetDbContext.cs" />
33+
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\IQueryable\IQueryable.GetObjectQuerySafe.cs" />
3334
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\IQueryable\IQueryable.GetObjectQuery.cs" />
3435
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\IQueryable\IQueryable.SelectByName.cs" />
3536
<Compile Include="$(MSBuildThisFileDirectory)EF5_EF6\IQueryable`\IQueryable`.GetDbContext.cs" />

src/shared/Z.EF.Plus._ExceptionMessage.Shared/ExceptionMessage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal class ExceptionMessage
2626
#if FULL || QUERY_CACHE
2727
public static string QueryCache_FirstTagNullOrEmpty = "Oops! The option 'UseFirstTagAsCacheKey' is enabled but we found no tag, an empty tag string, or a null tag string instead. Make sure a tag is provided, and it's not null or empty. For more information, contact us: [email protected]";
2828
public static string QueryCache_UseTagsNullOrEmpty = "Oops! The option 'UseTagsAsCacheKey' is enabled but we found no tag, an empty tag string, or a null tag string instead. Make sure a tag is provided, and it's not null or empty. For more information, contact us: [email protected]";
29+
public static string QueryCache_IsCommandInfoOptionalForCacheKey_Invalid = "Oops! When the option 'IsCommandInfoOptionalForCacheKey' is enabled, one of the following options must also be enabled: 'UseFirstTagAsCacheKey' or 'UseTagsAsCacheKey'. For more information, contact us: [email protected]";
2930
#endif
3031
#if FULL || QUERY_FILTER
3132
public static string QueryFilter_SetFilteredNotFound = "Oops! No set for the specified type has been found. Please report the issue to our support team: [email protected]";

0 commit comments

Comments
 (0)