diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration.cs index e720adb5..0d95de32 100644 --- a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration.cs +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration.cs @@ -15,6 +15,7 @@ #elif EF6 using System.Data.Entity; +using System.Data.Entity.Core.Objects; #elif EFCORE using Microsoft.EntityFrameworkCore; @@ -34,6 +35,12 @@ public AuditConfiguration() EntityValueFormatters = new List>>(); ExcludeIncludeEntityPredicates = new List>(); +#if EF5 || EF6 + ExcludeIncludeByInstanceEntityPredicates = new List>(); +#elif EFCORE + ExcludeIncludeByInstanceEntityPredicates = new List>(); +#endif + ExcludeIncludePropertyPredicates = new List>(); SoftAddedPredicates = new List>(); @@ -72,6 +79,14 @@ public AuditConfiguration() /// A list of predicates to exclude or include entities. public List> ExcludeIncludeEntityPredicates { get; set; } + /// Gets or sets a list of predicates to exclude or include by instance entities. + /// A list of predicates to exclude or include by instance entities. +#if EF5 || EF6 + public List> ExcludeIncludeByInstanceEntityPredicates { get; set; } +#elif EFCORE + public List> ExcludeIncludeByInstanceEntityPredicates { get; set; } +#endif + /// Gets or sets a list of predicates to exclude or include properties. /// A list of predicates to exclude or include properties. public List> ExcludeIncludePropertyPredicates { get; set; } @@ -179,6 +194,11 @@ public AuditConfiguration Clone() IgnoreRelationshipDeleted = IgnoreRelationshipDeleted, EntityValueFormatters = new List>>(EntityValueFormatters), ExcludeIncludeEntityPredicates = new List>(ExcludeIncludeEntityPredicates), +#if EF5 || EF6 + ExcludeIncludeByInstanceEntityPredicates = new List>(ExcludeIncludeByInstanceEntityPredicates), +#elif EFCORE + ExcludeIncludeByInstanceEntityPredicates = new List>(ExcludeIncludeByInstanceEntityPredicates), +#endif ExcludeIncludePropertyPredicates = new List>(ExcludeIncludePropertyPredicates), SoftAddedPredicates = new List>(SoftAddedPredicates), SoftDeletedPredicates = new List>(SoftDeletedPredicates), diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeByEntry.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeByEntry.cs new file mode 100644 index 00000000..5fa0452f --- /dev/null +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeByEntry.cs @@ -0,0 +1,42 @@ +// Description: Entity Framework Bulk Operations & Utilities (EF Bulk SaveChanges, Insert, Update, Delete, Merge | LINQ Query Cache, Deferred, Filter, IncludeFilter, IncludeOptimize | Audit) +// Website & Documentation: https://github.com/zzzprojects/Entity-Framework-Plus +// Forum & Issues: https://github.com/zzzprojects/EntityFramework-Plus/issues +// License: https://github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE +// More projects: http://www.zzzprojects.com/ +// Copyright © ZZZ Projects Inc. 2014 - 2016. All rights reserved. + +using System; + +#if EF5 +using System.Data.Entity; +using System.Data.Objects; + +#elif EF6 +using System.Data.Entity; +using System.Data.Entity.Core.Objects; + +#elif EFCORE +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +#endif + +namespace Z.EntityFramework.Plus +{ + public partial class AuditConfiguration + { + /// Excludes (by entity entry) from the audit all entities which satisfy the predicate. + /// The exclude entity predicate. + /// An AuditConfiguration. +#if EF5 || EF6 + public AuditConfiguration ExcludeByEntry(Func excludeEntityPredicate) +#elif EFCORE + public AuditConfiguration ExcludeByEntry(Func excludeEntityPredicate) +#endif + + { + ExcludeIncludeByInstanceEntityPredicates.Add(x => excludeEntityPredicate(x) ? (bool?) false : null); + return this; + } + } +} \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeByInstance.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeByInstance.cs new file mode 100644 index 00000000..03f40752 --- /dev/null +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeByInstance.cs @@ -0,0 +1,23 @@ +// Description: Entity Framework Bulk Operations & Utilities (EF Bulk SaveChanges, Insert, Update, Delete, Merge | LINQ Query Cache, Deferred, Filter, IncludeFilter, IncludeOptimize | Audit) +// Website & Documentation: https://github.com/zzzprojects/Entity-Framework-Plus +// Forum & Issues: https://github.com/zzzprojects/EntityFramework-Plus/issues +// License: https://github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE +// More projects: http://www.zzzprojects.com/ +// Copyright © ZZZ Projects Inc. 2014 - 2016. All rights reserved. + +using System; + +namespace Z.EntityFramework.Plus +{ + public partial class AuditConfiguration + { + /// Excludes (by entity instance) from the audit all entities which satisfy the predicate. + /// The exclude entity predicate. + /// An AuditConfiguration. + public AuditConfiguration ExcludeByInstance(Func excludeEntityPredicate) + { + ExcludeIncludeByInstanceEntityPredicates.Add(x => excludeEntityPredicate(x.Entity) ? (bool?)false : null); + return this; + } + } +} \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeEntity.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeEntity.cs index 43c3bb58..16be4f6d 100644 --- a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeEntity.cs +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/ExcludeEntity.cs @@ -11,7 +11,8 @@ namespace Z.EntityFramework.Plus { public partial class AuditConfiguration { - /// Excludes from the audit all entities which satisfy the predicate. + + /// Excludes (by entity type) from the audit all entities which satisfy the predicate. /// The exclude entity predicate. /// An AuditConfiguration. public AuditConfiguration Exclude(Func excludeEntityPredicate) @@ -20,7 +21,7 @@ public AuditConfiguration Exclude(Func excludeEntityPredicate) return this; } - /// Excludes from the audit all entities of 'T' type or entities which the type derive from 'T'. + /// Excludes (by entity type) from the audit all entities of 'T' type or entities which the type derive from 'T'. /// Generic type to exclude. /// An AuditConfiguration. public AuditConfiguration Exclude() diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeByEntry.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeByEntry.cs new file mode 100644 index 00000000..fbb5037f --- /dev/null +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeByEntry.cs @@ -0,0 +1,42 @@ +// Description: Entity Framework Bulk Operations & Utilities (EF Bulk SaveChanges, Insert, Update, Delete, Merge | LINQ Query Cache, Deferred, Filter, IncludeFilter, IncludeOptimize | Audit) +// Website & Documentation: https://github.com/zzzprojects/Entity-Framework-Plus +// Forum & Issues: https://github.com/zzzprojects/EntityFramework-Plus/issues +// License: https://github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE +// More projects: http://www.zzzprojects.com/ +// Copyright © ZZZ Projects Inc. 2014 - 2016. All rights reserved. + +using System; + +#if EF5 +using System.Data.Entity; +using System.Data.Objects; + +#elif EF6 +using System.Data.Entity; +using System.Data.Entity.Core.Objects; + +#elif EFCORE +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; + +#endif + +namespace Z.EntityFramework.Plus +{ + public partial class AuditConfiguration + { + /// Includes (by entity entry) from the audit all entities which satisfy the predicate. + /// The include entity predicate. + /// An AuditConfiguration. +#if EF5 || EF6 + public AuditConfiguration IncludeByEntry(Func includeEntityPredicate) +#elif EFCORE + public AuditConfiguration IncludeByEntry(Func includeEntityPredicate) +#endif + + { + ExcludeIncludeByInstanceEntityPredicates.Add(x => includeEntityPredicate(x) ? (bool?)true : null); + return this; + } + } +} \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeByInstance.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeByInstance.cs new file mode 100644 index 00000000..aceaa62d --- /dev/null +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeByInstance.cs @@ -0,0 +1,23 @@ +// Description: Entity Framework Bulk Operations & Utilities (EF Bulk SaveChanges, Insert, Update, Delete, Merge | LINQ Query Cache, Deferred, Filter, IncludeFilter, IncludeOptimize | Audit) +// Website & Documentation: https://github.com/zzzprojects/Entity-Framework-Plus +// Forum & Issues: https://github.com/zzzprojects/EntityFramework-Plus/issues +// License: https://github.com/zzzprojects/EntityFramework-Plus/blob/master/LICENSE +// More projects: http://www.zzzprojects.com/ +// Copyright © ZZZ Projects Inc. 2014 - 2016. All rights reserved. + +using System; + +namespace Z.EntityFramework.Plus +{ + public partial class AuditConfiguration + { + /// Includes (by entity instance) from the audit all entities which satisfy the predicate. + /// The include entity predicate. + /// An AuditConfiguration. + public AuditConfiguration IncludeByInstance(Func includeEntityPredicate) + { + ExcludeIncludeByInstanceEntityPredicates.Add(x => includeEntityPredicate(x.Entity) ? (bool?) true : null); + return this; + } + } +} \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeEntity.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeEntity.cs index 0f097f0e..c1ad4b2c 100644 --- a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeEntity.cs +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IncludeEntity.cs @@ -11,7 +11,7 @@ namespace Z.EntityFramework.Plus { public partial class AuditConfiguration { - /// Includes from the audit all entities which satisfy the predicate. + /// Includes (by entity type) from the audit all entities which satisfy the predicate. /// The include entity predicate. /// An AuditConfiguration. public AuditConfiguration Include(Func includeEntityPredicate) @@ -20,7 +20,7 @@ public AuditConfiguration Include(Func includeEntityPredicate) return this; } - /// Includes from the audit all entities of 'T' type or entities which the type derive from 'T'. + /// Includes (by entity type) from the audit all entities of 'T' type or entities which the type derive from 'T'. /// Generic type to include. /// An AuditConfiguration. public AuditConfiguration Include() diff --git a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IsAuditedEntity.cs b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IsAuditedEntity.cs index 4f74391b..9fbbe196 100644 --- a/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IsAuditedEntity.cs +++ b/src/shared/Z.EF.Plus.Audit.Shared/AuditConfiguration/IsAuditedEntity.cs @@ -29,7 +29,8 @@ public bool IsAuditedEntity(ObjectStateEntry entry) public bool IsAuditedEntity(EntityEntry entry) #endif { - if (ExcludeIncludeEntityPredicates.Count == 0 || entry.Entity == null) + if (entry.Entity == null + || (ExcludeIncludeEntityPredicates.Count == 0 && ExcludeIncludeByInstanceEntityPredicates.Count == 0)) { return true; } @@ -54,6 +55,15 @@ public bool IsAuditedEntity(EntityEntry entry) IsAuditedDictionary.TryAdd(key, value); } + foreach(var excludeIncludeByInstanceEntityFunc in ExcludeIncludeByInstanceEntityPredicates) + { + var maybeIncluded = excludeIncludeByInstanceEntityFunc(entry); + if (maybeIncluded.HasValue) + { + value = maybeIncluded.Value; + } + } + return value; } } diff --git a/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.projitems b/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.projitems index 56a37558..ce8dc6c3 100644 --- a/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.projitems +++ b/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.projitems @@ -2,10 +2,10 @@ {2ad53f92-e59b-4cba-b195-62918e6865fb} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) @@ -19,8 +19,12 @@ + + + + diff --git a/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.shproj b/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.shproj index cce660e2..334feeb7 100644 --- a/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.shproj +++ b/src/shared/Z.EF.Plus.Audit.Shared/Z.EF.Plus.Audit.Shared.shproj @@ -3,10 +3,14 @@ 6bfa5473-a429-4f9e-aa40-1387716de22d 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.projitems b/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.projitems index b841c233..4eb26fbe 100644 --- a/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.projitems +++ b/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.projitems @@ -2,10 +2,10 @@ {868b0e69-fb31-4d58-949c-fbf0f476d36c} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.shproj b/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.shproj index 590d3781..602c9b3b 100644 --- a/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.shproj +++ b/src/shared/Z.EF.Plus.BatchDelete.Shared/Z.EF.Plus.BatchDelete.Shared.shproj @@ -3,10 +3,14 @@ 5b3fa372-a872-4ca1-b7c0-5352052ac75a 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.projitems b/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.projitems index c5a2d2fa..bf74836e 100644 --- a/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.projitems +++ b/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.projitems @@ -2,10 +2,10 @@ {f53c2b9e-ca23-4465-b14a-6f72c6100b71} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.shproj b/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.shproj index e1cff201..fd66bff5 100644 --- a/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.shproj +++ b/src/shared/Z.EF.Plus.BatchUpdate.Shared/Z.EF.Plus.BatchUpdate.Shared.shproj @@ -3,10 +3,14 @@ ce6171c3-84cc-4ae1-82d0-cdac97ef5ab2 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCache.cs b/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCache.cs index 2d3f89fb..f6b7fcf3 100644 --- a/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCache.cs +++ b/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCache.cs @@ -143,7 +143,7 @@ public static IEnumerable FromCache(this IQueryable query, MemoryCacheE { if (!QueryCacheManager.IsEnabled) { - return query.AsNoTracking().ToList(); + return QueryCacheManager.ResolveAsNoTracking(query).ToList(); } var key = QueryCacheManager.GetCacheKey(query, tags); @@ -151,7 +151,7 @@ public static IEnumerable FromCache(this IQueryable query, MemoryCacheE object item; if (!QueryCacheManager.Cache.TryGetValue(key, out item)) { - item = query.AsNoTracking().ToList(); + item = QueryCacheManager.ResolveAsNoTracking(query).ToList(); item = QueryCacheManager.Cache.Set(key, item, options); QueryCacheManager.AddCacheTag(key, tags); QueryCacheManager.AddCacheTag(key,typeof(T).Name + QueryCacheManager.CacheTypeSuffix); diff --git a/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCacheAsync.cs b/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCacheAsync.cs index f551a996..6f88ccc3 100644 --- a/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCacheAsync.cs +++ b/src/shared/Z.EF.Plus.QueryCache.Shared/Extensions/IQueryable`/FromCacheAsync.cs @@ -284,7 +284,7 @@ public static Task> FromCacheAsync(this IQueryable query, p { if (!QueryCacheManager.IsEnabled) { - return await query.AsNoTracking().ToListAsync(cancellationToken).ConfigureAwait(false); + return await QueryCacheManager.ResolveAsNoTracking(query).ToListAsync(cancellationToken).ConfigureAwait(false); } var key = QueryCacheManager.GetCacheKey(query, tags, true); @@ -292,7 +292,7 @@ public static Task> FromCacheAsync(this IQueryable query, p object item; if (!QueryCacheManager.Cache.TryGetValue(key, out item)) { - item = await query.AsNoTracking().ToListAsync(cancellationToken).ConfigureAwait(false); + item = await QueryCacheManager.ResolveAsNoTracking(query).ToListAsync(cancellationToken).ConfigureAwait(false); item = QueryCacheManager.Cache.Set(key, item, options); QueryCacheManager.AddCacheTag(key, tags); QueryCacheManager.AddCacheTag(key, typeof(T).Name + QueryCacheManager.CacheTypeSuffix); diff --git a/src/shared/Z.EF.Plus.QueryCache.Shared/QueryCacheManager.cs b/src/shared/Z.EF.Plus.QueryCache.Shared/QueryCacheManager.cs index 55668fd4..0151cb69 100644 --- a/src/shared/Z.EF.Plus.QueryCache.Shared/QueryCacheManager.cs +++ b/src/shared/Z.EF.Plus.QueryCache.Shared/QueryCacheManager.cs @@ -30,6 +30,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; using Microsoft.Extensions.Caching.Memory; +using System.Collections; + #endif @@ -52,7 +54,7 @@ static QueryCacheManager() DefaultCacheItemPolicy = new CacheItemPolicy(); #elif EFCORE Cache = new MemoryCache(new MemoryCacheOptions()); - DefaultMemoryCacheEntryOptions = new MemoryCacheEntryOptions(); + DefaultMemoryCacheEntryOptions = new MemoryCacheEntryOptions(); #endif CachePrefix = "Z.EntityFramework.Plus.QueryCacheManager;"; CacheTypeSuffix = "_ZZZ_QueryCacheManager_CacheType"; @@ -191,7 +193,7 @@ public static Func MemoryCacheEntryOptionsFactory _memoryCacheEntryOptionsFactory = value; _defaultMemoryCacheEntryOptions = null; } - } + } #endif /// Gets or sets the cache prefix to use to create the cache key. @@ -212,6 +214,14 @@ public static Func MemoryCacheEntryOptionsFactory /// true if the Query Cache is enabled. public static bool IsEnabled { get; set; } = true; +#if EFCORE_5X + /// + /// Gets or sets a value indicating whether the cache should use 'AsNoTrackingWithIdentityResolution()' instead of 'AsNoTracking()'. + /// + /// True if the cache should use 'AsNoTrackingWithIdentityResolution()' instead of 'AsNoTracking()', false if not. + public static bool UseAsNoTrackingWithIdentityResolution { get; set; } +#endif + /// /// Gets or sets a value indicating whether the connection in cache key should be included. /// @@ -343,18 +353,18 @@ internal static void AddCacheTag(string cacheKey, params string[] tags) foreach (var tag in tags) { CacheTags.AddOrUpdate(CachePrefix + tag, x => new List {cacheKey}, (x, list) => - { - lock (list) - { + { + lock (list) + { // never lock something related to this list elsewhere or ensure we don't create a deadlock if (!list.Contains(cacheKey)) - { - list.Add(cacheKey); - } - } + { + list.Add(cacheKey); + } + } - return list; - }); + return list; + }); } } @@ -377,36 +387,36 @@ public static void ExpireAll() Cache.Remove(item); } } -#else - /// Expire all cached objects && tag. +#else + /// Expire all cached objects && tag. public static void ExpireAll() - { - var tags = CacheTags.Select(x => x.Key).ToList(); + { + var tags = CacheTags.Select(x => x.Key).ToList(); // We do not use ExpireTag because type doesn't have CachePrefix foreach (var tag in tags) - { - List list; - if (CacheTags.TryRemove(tag, out list)) - { - // never lock something related to this list elsewhere or ensure we don't create a deadlock - lock (list) - { - foreach (var item in list) - { - Cache.Remove(item); - } - } - } - } + { + List list; + if (CacheTags.TryRemove(tag, out list)) + { + // never lock something related to this list elsewhere or ensure we don't create a deadlock + lock (list) + { + foreach (var item in list) + { + Cache.Remove(item); + } + } + } + } } #endif /// Expire type. /// The type. public static void ExpireType(Type type) - { - ExpireTag(type.Name + CacheTypeSuffix); - } + { + ExpireTag(type.Name + CacheTypeSuffix); + } /// Expire type. public static void ExpireType() @@ -439,7 +449,7 @@ public static void ExpireTag(params string[] tags) { List list; if (CacheTags.TryRemove(CachePrefix + tag, out list)) - { + { // never lock something related to this list elsewhere or ensure we don't create a deadlock lock (list) { @@ -450,7 +460,7 @@ public static void ExpireTag(params string[] tags) } } } - } + } /// Gets cached keys used to cache or retrieve a query from the QueryCacheManager. /// The query to cache or retrieve from the QueryCacheManager. @@ -499,7 +509,7 @@ public static string GetCacheKey(IQueryable query, string[] tags, bool isAsync = } #elif EFCORE RelationalQueryContext queryContext = null; - + var command = query.CreateCommand(out queryContext); sb.AppendLine(CachePrefix); @@ -574,21 +584,22 @@ public static string GetCacheKey(IQueryable query, string[] tags, bool isAsync = sb.Append(parameter.Value); sb.AppendLine(";"); - if (parameter.Value is object[] parameterValues) + // Array contient IList + if (parameter.Value is IList parameterValues) { - foreach (var param in parameterValues) - { - if (param is DbParameter dbParameter) - { - sb.Append(dbParameter.Value?.ToString() ?? "NULL"); - sb.AppendLine(";"); + foreach (var param in parameterValues) + { + if (param is DbParameter dbParameter) + { + sb.Append(dbParameter.Value?.ToString() ?? "NULL"); + sb.AppendLine(";"); } else if (param != null) { sb.Append(param); sb.AppendLine(";"); } - } + } } } #endif @@ -827,13 +838,22 @@ public static string GetConnectionStringForCacheKey(RelationalQueryContext query } // FORCE database name in case "ChangeDatabase()" method is used - var connectionString = string.Concat(connection.DataSource ?? "", - Environment.NewLine, + var connectionString = string.Concat(connection.DataSource ?? "", + Environment.NewLine, connection.Database ?? "", Environment.NewLine, connectionStringWithoutPassword ?? ""); return connectionString; } + + internal static IQueryable ResolveAsNoTracking(IQueryable query) where TEntity : class + { +#if EFCORE_5X + return UseAsNoTrackingWithIdentityResolution ? query.AsNoTrackingWithIdentityResolution() : query.AsNoTracking(); +#else + return query.AsNoTracking(); +#endif + } #endif diff --git a/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.projitems b/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.projitems index bc1e70f4..5acd4ccf 100644 --- a/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.projitems @@ -2,10 +2,10 @@ {ea8a91c0-de7f-4a15-aa22-4bbfd2139674} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.shproj b/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.shproj index 2efd0501..0e8a2d8d 100644 --- a/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryCache.Shared/Z.EF.Plus.QueryCache.Shared.shproj @@ -3,10 +3,14 @@ 287a066e-6fcc-42b2-bacc-e24faf056e4c 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.projitems b/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.projitems index c51e2c6b..6eeab16e 100644 --- a/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.projitems @@ -2,10 +2,10 @@ {4cab62a7-a320-414e-b290-943e4dafafc1} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.shproj b/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.shproj index 9038af5b..8d5f72f6 100644 --- a/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryDeferred.Shared/Z.EF.Plus.QueryDeferred.Shared.shproj @@ -3,10 +3,14 @@ b9fce31e-4830-408c-95f0-447acb0b7490 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.projitems b/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.projitems index c12fea4f..e5e3c974 100644 --- a/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.projitems @@ -2,10 +2,10 @@ {47155a83-7578-47a8-b566-a64a9dca3c89} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.shproj b/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.shproj index ecbe3827..bc3ee092 100644 --- a/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryExtensions.Shared/Z.EF.Plus.QueryExtensions.Shared.shproj @@ -3,10 +3,14 @@ c3df9de1-3c26-42c8-b972-902214161bd7 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.projitems b/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.projitems index 02949a5b..20a064a0 100644 --- a/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.projitems @@ -2,10 +2,10 @@ {0863c778-9f94-41d0-9e89-edbeca19d75d} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.shproj b/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.shproj index 97134b01..11697df2 100644 --- a/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryFilter.Shared/Z.EF.Plus.QueryFilter.Shared.shproj @@ -3,10 +3,14 @@ b8db9c52-11b7-4d06-8e01-e25903f560ca 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.projitems b/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.projitems index 54a1d838..d8ebb40b 100644 --- a/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.projitems @@ -2,10 +2,10 @@ {37924acc-312d-4d89-ae3a-7f221f3d34f7} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.shproj b/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.shproj index 30fa1a5d..0154faaa 100644 --- a/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryFilterInterceptor.Shared/Z.EF.Plus.QueryFilterInterceptor.Shared.shproj @@ -3,10 +3,14 @@ 6731e6cf-d2ea-4ed2-9b32-02c462271d80 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureBatch.cs b/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureBatch.cs index 045a9b1b..e6ed114a 100644 --- a/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureBatch.cs +++ b/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureBatch.cs @@ -106,15 +106,20 @@ public void ExecuteQueries() // We deactivated temporary some QueryFuture for EF Core as they don't work correctly // We need to still make them "work" for IncludeFilter feature - var isMySqlPomelo = assemblyName == "Pomelo.EntityFrameworkCore.MySql"; + // sa chie dans notre dataReader en EF.Core 3 (2 pas test, mais je suppose pareil) +#if (EFCORE_2X || EFCORE_3X) && !EFCORE_5X + var isMySqlPomeloUnsupported = assemblyName == "Pomelo.EntityFrameworkCore.MySql"; +#else + var isMySqlPomeloUnsupported = false; +#endif var isOracle = assemblyName == "Oracle.EntityFrameworkCore" || assemblyName == "Devart.Data.Oracle.Entity.EFCore"; - if (allowQueryBatch && (isOracle || isMySqlPomelo)) + if (allowQueryBatch && (isOracle || isMySqlPomeloUnsupported)) { allowQueryBatch = false; } #endif - if (!allowQueryBatch) + if (!allowQueryBatch) { foreach (var query in Queries) { @@ -185,24 +190,24 @@ public void ExecuteQueries() { QueryFutureManager.OnBatchExecuting?.Invoke(command); #if EF5 - using (var reader = command.ExecuteReader()) - { - foreach (var query in Queries) + using (var reader = command.ExecuteReader()) { - query.SetResult(reader); - reader.NextResult(); + foreach (var query in Queries) + { + query.SetResult(reader); + reader.NextResult(); + } } -} #elif EF6 - var interceptionContext = Context.GetInterceptionContext(); - using (var reader = DbInterception.Dispatch.Command.Reader(command, new DbCommandInterceptionContext(interceptionContext))) - { - foreach (var query in Queries) + var interceptionContext = Context.GetInterceptionContext(); + using (var reader = DbInterception.Dispatch.Command.Reader(command, new DbCommandInterceptionContext(interceptionContext))) { - query.SetResult(reader); - reader.NextResult(); + foreach (var query in Queries) + { + query.SetResult(reader); + reader.NextResult(); + } } - } #elif EFCORE using (var reader = command.ExecuteReader()) { @@ -376,10 +381,11 @@ protected DbCommand CreateCommandCombined(bool isAsync = false) var isOracleManaged = command.GetType().FullName.Contains("Oracle.ManagedDataAccess"); var isOracleDevArt = command.GetType().FullName.Contains("Devart"); + var isPostgreSQL = command.GetType().FullName.Contains("Npgsql"); #if EFCORE_3X // foreach is broken need stop and new Foreach, a for is better here, but I don't know if is possible Include with logique with new IncludeOptimized in a Where logic or other. In theory I guess yes, in true I don't know. // For now I try without check that. - for (int i = 0; i < Queries.Count;i++) + for (int i = 0; i < Queries.Count;i++) { var query = Queries.ElementAt(i); // first check is because parano. @@ -403,7 +409,7 @@ protected DbCommand CreateCommandCombined(bool isAsync = false) foreach (var query in Queries) { - // GENERATE SQL + // GENERATE SQL #if EF5 var sql = query.Query.ToTraceString(); var parameters = query.Query.Parameters; @@ -553,6 +559,34 @@ protected DbCommand CreateCommandCombined(bool isAsync = false) command.Parameters.Add(dbParameter); } + if (isPostgreSQL) + { + var relationalTypeMappingProperty = typeof(TypeMappedRelationalParameter).GetProperty("RelationalTypeMapping", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + if (relationalTypeMappingProperty != null) + { + var relationalTypeMapping = (RelationalTypeMapping)relationalTypeMappingProperty.GetValue(relationalParameter); + + if (relationalTypeMapping != null && relationalTypeMapping.StoreType.Equals("citext", StringComparison.OrdinalIgnoreCase)) + { + + var propertyPostgreSQLDBType = dbParameter.GetType().GetProperty("NpgsqlDbType", BindingFlags.Public | BindingFlags.Instance); + + if (propertyPostgreSQLDBType != null) + { + // 51 + // NpgsqlTypes.NpgsqlDbType.Citext + propertyPostgreSQLDBType.SetValue(dbParameter, 51); + } + else + { + // parce que sinon le Query va fonctionner, mais pas fournir ou impacté potentiellement les mauvaise donné sans que le client le sache. + throw new Exception("Oops! The following class 'NpgsqlDbType' was not found from the assembly when trying to solve the 'citext' column type for the `QueryFuture` feature."); + } + } + } + } + // REPLACE parameter with new value if (isOracle || isOracleManaged || isOracleDevArt) { @@ -567,7 +601,7 @@ protected DbCommand CreateCommandCombined(bool isAsync = false) - sb.AppendLine(string.Concat("-- EF+ Query Future: ", queryCount, " of ", Queries.Count)); + sb.AppendLine(string.Concat("-- EF+ Query Future: ", queryCount, " of ", Queries.Count)); if (isOracle || isOracleManaged || isOracleDevArt) { diff --git a/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureManager.cs b/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureManager.cs index da51d2d2..36d3262c 100644 --- a/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureManager.cs +++ b/src/shared/Z.EF.Plus.QueryFuture.Shared/QueryFutureManager.cs @@ -87,8 +87,8 @@ public static QueryFutureBatch AddOrGetBatch(DbContext context) #endif { QueryFutureBatch futureBatch; - - if (!CacheWeakFutureBatch.TryGetValue(context, out futureBatch)) + + if (!CacheWeakFutureBatch.TryGetValue(context, out futureBatch)) { futureBatch = new QueryFutureBatch(context); CacheWeakFutureBatch.Add(context, futureBatch); @@ -104,20 +104,20 @@ public static void ExecuteBatch(DbContext context) #elif EFCORE var batch = AddOrGetBatch(context); #endif - batch.ExecuteQueries(); + batch.ExecuteQueries(); } #if NET45 || EFCORE public static async Task ExecuteBatchAsync(DbContext context, CancellationToken cancellationToken = default(CancellationToken)) - { + { #if EF5 || EF6 var batch = AddOrGetBatch(context.GetObjectContext()); #elif EFCORE var batch = AddOrGetBatch(context); #endif - await batch.ExecuteQueriesAsync(cancellationToken).ConfigureAwait(false); - } + await batch.ExecuteQueriesAsync(cancellationToken).ConfigureAwait(false); + } #endif } } \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.projitems b/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.projitems index 27df9af6..84657b23 100644 --- a/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.projitems @@ -2,10 +2,10 @@ {2e37412b-7ff2-4ec4-aa19-efbec26335ad} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.shproj b/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.shproj index 7334d270..d26dc6b8 100644 --- a/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryFuture.Shared/Z.EF.Plus.QueryFuture.Shared.shproj @@ -3,10 +3,14 @@ 5ca1ed0d-1322-49f0-920b-f0c4ba36560e 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryHook/InterceptorCommandExecutingExtensions.cs b/src/shared/Z.EF.Plus.QueryHook/InterceptorCommandExecutingExtensions.cs new file mode 100644 index 00000000..9a4302d6 --- /dev/null +++ b/src/shared/Z.EF.Plus.QueryHook/InterceptorCommandExecutingExtensions.cs @@ -0,0 +1,32 @@ +#if ((EF6 && !EF5) && (NET45 || NETSTANDARD)) || EFCORE_3X +using System; +using System.Data.Common; +using System.Linq; + +namespace Z.EntityFramework.Plus +{ + public static class InterceptorCommandExecutingExtensions + { + /// + /// An IQueryable<T> extension method that will add a hook and let you make an action on the command (change command text for example) before it get executed. + /// The type of elements of the query. + /// The IQueryable<T>. + /// The action to execute before the command is executed. + /// The IQueryable<T>. + public static IQueryable InterceptorCommandExecuting(this IQueryable @this, Action action) + { + return Z.EntityFramework.Plus.PublicMethodForEFPlus.InterceptorCommandExecuting(@this, action); + } + + /// + /// An IQueryable<T> extension method that will replace all "LEFT JOIN" in the query by "INNER JOIN". + /// The type of elements of the query. + /// The IQueryable<T>. + /// The IQueryable<T>. + public static IQueryable ReplaceAllLeftJoinByInnerJoin(this IQueryable @this) + { + return Z.EntityFramework.Plus.PublicMethodForEFPlus.ReplaceAllLeftJoinByInnerJoin(@this); + } + } +} +#endif \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.QueryHook/Z.EF.Plus.QueryHook.projitems b/src/shared/Z.EF.Plus.QueryHook/Z.EF.Plus.QueryHook.projitems index 26f63d05..8ff2aafe 100644 --- a/src/shared/Z.EF.Plus.QueryHook/Z.EF.Plus.QueryHook.projitems +++ b/src/shared/Z.EF.Plus.QueryHook/Z.EF.Plus.QueryHook.projitems @@ -9,6 +9,7 @@ Z.EF.Plus.QueryHook + diff --git a/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.projitems b/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.projitems index df0e98f3..83d14ab8 100644 --- a/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.projitems @@ -2,10 +2,10 @@ {0322107e-4922-4bb1-aae3-909b56443c8c} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.shproj b/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.shproj index 192678b4..967697cd 100644 --- a/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryIncludeFilter.Shared/Z.EF.Plus.QueryIncludeFilter.Shared.shproj @@ -3,10 +3,14 @@ dc4c972b-6ae7-4620-b2cb-2afd106e638b 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.projitems b/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.projitems index 2cba5975..21c0ca20 100644 --- a/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.projitems @@ -2,10 +2,10 @@ {484e4a79-ba9c-46ef-8e3e-c7bfde200430} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.shproj b/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.shproj index e0067303..09470509 100644 --- a/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryIncludeFilterCore.Shared/Z.EF.Plus.QueryIncludeFilterCore.Shared.shproj @@ -3,10 +3,14 @@ 23bfa108-fe19-4a8e-886a-74f86728fbc9 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.projitems b/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.projitems index 99c405f4..4a49e712 100644 --- a/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.projitems +++ b/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.projitems @@ -2,10 +2,10 @@ {9ef2d9c1-6378-499e-a816-b964928c0b95} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.shproj b/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.shproj index a5c4838a..bf414d5e 100644 --- a/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.shproj +++ b/src/shared/Z.EF.Plus.QueryIncludeOptimized.Shared/Z.EF.Plus.QueryIncludeOptimized.Shared.shproj @@ -3,10 +3,14 @@ d1953d60-b322-4caf-92df-0581a1fd758f 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus.SetIdentity.Shared/EF6/SetIdentity.cs b/src/shared/Z.EF.Plus.SetIdentity.Shared/EF6/SetIdentity.cs new file mode 100644 index 00000000..407100e1 --- /dev/null +++ b/src/shared/Z.EF.Plus.SetIdentity.Shared/EF6/SetIdentity.cs @@ -0,0 +1,113 @@ +#if EF6 +using System; +using System.Data; +using System.Data.Entity; +using System.Data.Entity.Core.Objects; +using Z.EntityFramework.Extensions.Core.SchemaObjectModel; + +namespace Z.EntityFramework.Plus +{ + public static class SetIdentityExtensions + { + /// Set IDENTITY_INSERT ON for the specific entity type. The connection will be opened if closed. + /// The entity type. + /// The DbSet. + public static void SqlServerSetIdentityInsertOn(this DbSet @this) where T : class + { + SqlServerSetIdentity(@this.GetDbContext(), typeof(T), true); + } + + /// Set IDENTITY_INSERT ON for the specific entity type. The connection will be opened if closed. + /// The entity type. + /// The context. + public static void SqlServerSetIdentityInsertOn(this DbContext @this) where T : class + { + SqlServerSetIdentity(@this, typeof(T), true); + } + + /// Set IDENTITY_INSERT ON for the specific entity type. The connection will be opened if closed. + /// The context. + /// The entity type. + public static void SqlServerSetIdentityInsertOn(this DbContext @this, Type type) + { + SqlServerSetIdentity(@this, type, true); + } + + /// Set IDENTITY_INSERT OFF for the specific entity type. + /// The entity type. + /// The DbSet. + public static void SqlServerSetIdentityInsertOff(this DbSet @this) where T : class + { + SqlServerSetIdentity(@this.GetDbContext(), typeof(T), false); + } + + /// Set IDENTITY_INSERT OFF for the specific entity type. + /// The entity type. + /// The context. + public static void SqlServerSetIdentityInsertOff(this DbContext @this) where T : class + { + SqlServerSetIdentity(@this, typeof(T), false); + } + + /// Set IDENTITY_INSERT OFF for the specific entity type. + /// The context. + /// The entity type. + public static void SqlServerSetIdentityInsertOff(this DbContext @this, Type type) + { + SqlServerSetIdentity(@this, type, false); + } + + internal static void SqlServerSetIdentity(DbContext context, Type type, bool OnOff) + { + var entityTypes = context.GetModel().ConceptualModel.EntityTypes; + + var objectType = ObjectContext.GetObjectType(type); + + var typeName = objectType.Name; + var entityFound = entityTypes.Find(x => x.Name == typeName); + string schemaTableName = null; + if (entityFound != null && entityFound.EntityTypeMapping != null && entityFound.EntityTypeMapping.MappingFragment != null + && entityFound.EntityTypeMapping.MappingFragment.StoreEntitySet != null && !string.IsNullOrEmpty(entityFound.EntityTypeMapping.MappingFragment.StoreEntitySet.Table)) + { + schemaTableName = entityFound.EntityTypeMapping.MappingFragment.StoreEntitySet.GetDestinationTableName(context); + } + else + { + throw new Exception($"Oops! The entity type '{typeName}' could not be found in the model for the method 'SqlServerSetIdentityInsertOn' or 'SqlServerSetIdentityInsertOff'."); + } + + var innerConnection = context.Database.Connection; + + // OPEN the connection if we own it + if (innerConnection.State == ConnectionState.Closed) + { + if(!OnOff) + { + throw new InvalidOperationException("Oops! The method 'SqlServerSetIdentityInsertOff' can only be called with an open connection."); + } + + try + { + innerConnection.Open(); + } + catch (Exception e) + { + if (e.Message == "Cannot access a disposed object.\r\nObject name: 'Transaction'.") + { + throw new InvalidOperationException("The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.", e); + } + throw; + } + + } + + using (var command = innerConnection.CreateCommand()) + { + var onOff = OnOff ? "ON" : "OFF"; + command.CommandText = $"SET IDENTITY_INSERT {schemaTableName} {onOff};"; + command.ExecuteNonQuery(); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.SetIdentity.Shared/EFCore/SetIdentity.cs b/src/shared/Z.EF.Plus.SetIdentity.Shared/EFCore/SetIdentity.cs new file mode 100644 index 00000000..a06292e0 --- /dev/null +++ b/src/shared/Z.EF.Plus.SetIdentity.Shared/EFCore/SetIdentity.cs @@ -0,0 +1,123 @@ +#if EFCORE +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Reflection; +using System.Security.AccessControl; +using System.Text; + +namespace Z.EntityFramework.Plus +{ + public static class SetIdentityExtensions + { + /// Set IDENTITY_INSERT ON for the specific entity type. The connection will be opened if closed. + /// The entity type. + /// The DbSet. + public static void SqlServerSetIdentityInsertOn(this DbSet @this) where T : class + { + SqlServerSetIdentity(@this.GetDbContext(), typeof(T), true); + } + + /// Set IDENTITY_INSERT ON for the specific entity type. The connection will be opened if closed. + /// The entity type. + /// The context. + public static void SqlServerSetIdentityInsertOn(this DbContext @this) where T : class + { + SqlServerSetIdentity(@this, typeof(T), true); + } + + /// Set IDENTITY_INSERT ON for the specific entity type. The connection will be opened if closed. + /// The context. + /// The entity type. + public static void SqlServerSetIdentityInsertOn(this DbContext @this, Type type) + { + SqlServerSetIdentity(@this, type, true); + } + + /// Set IDENTITY_INSERT OFF for the specific entity type. + /// The entity type. + /// The DbSet. + public static void SqlServerSetIdentityInsertOff(this DbSet @this) where T : class + { + SqlServerSetIdentity(@this.GetDbContext(), typeof(T), false); + } + + /// Set IDENTITY_INSERT OFF for the specific entity type. + /// The entity type. + /// The context. + public static void SqlServerSetIdentityInsertOff(this DbContext @this) where T : class + { + SqlServerSetIdentity(@this, typeof(T), false); + } + + /// Set IDENTITY_INSERT OFF for the specific entity type. + /// The context. + /// The entity type. + public static void SqlServerSetIdentityInsertOff(this DbContext @this, Type type) + { + SqlServerSetIdentity(@this, type, false); + } + + internal static void SqlServerSetIdentity(DbContext context, Type type, bool OnOff) + { + // TPT ou TPC ==> j'assume que donnerons le bon type... ou on va chercher la base? ==> pas de préférence. + var entityType = context.Model.FindEntityType(type); + + if (entityType == null) + { + throw new Exception($"Oops! The entity type '{type.Name}' could not be found in the model for the method 'SqlServerSetIdentityInsertOn' or 'SqlServerSetIdentityInsertOff'."); + } + + // logique du GetTableNameWithSchema +#if !EFCORE_3X + var relational = entityType.Relational(); + var name = !string.IsNullOrEmpty(relational.Schema) + ? relational.Schema + "." + relational.TableName + : relational.TableName; +#else + var entityZ = entityType.ToZInfo(); + var name = !string.IsNullOrEmpty(entityZ.SchemaNameEF) + ? entityZ.SchemaNameEF + "." + entityZ.TableNameEF + : entityZ.TableNameEF; +#endif + var innerConnection = context.Database.GetDbConnection(); + + // OPEN the connection if we own it + if (innerConnection.State == ConnectionState.Closed) + { + if(!OnOff) + { + throw new InvalidOperationException("Oops! The method 'SqlServerSetIdentityInsertOff' can only be called with an open connection."); + } + + try + { + context.Database.OpenConnection(); + } + catch (Exception e) + { + if (e.Message == "Cannot access a disposed object.\r\nObject name: 'Transaction'.") + { + throw new InvalidOperationException("The transaction associated with the current connection has completed but has not been disposed. The transaction must be disposed before the connection can be used to execute SQL statements.", e); + } + + throw; + } + + } + + using (var command = innerConnection.CreateCommand()) + { + var onOff = OnOff ? "ON" : "OFF"; + command.CommandText = $"SET IDENTITY_INSERT {name} {onOff};"; + command.ExecuteNonQuery(); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.SetIdentity.Shared/Z.EF.Plus.SetIdentity.Shared.projitems b/src/shared/Z.EF.Plus.SetIdentity.Shared/Z.EF.Plus.SetIdentity.Shared.projitems new file mode 100644 index 00000000..d8da3f98 --- /dev/null +++ b/src/shared/Z.EF.Plus.SetIdentity.Shared/Z.EF.Plus.SetIdentity.Shared.projitems @@ -0,0 +1,15 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 75ee5225-9de8-4576-8481-5b71ea5958d7 + + + Z.EF.Plus.SetIdentity.Shared + + + + + + \ No newline at end of file diff --git a/src/shared/Z.EF.Plus.SetIdentity.Shared/Z.EF.Plus.SetIdentity.Shared.shproj b/src/shared/Z.EF.Plus.SetIdentity.Shared/Z.EF.Plus.SetIdentity.Shared.shproj new file mode 100644 index 00000000..435f74af --- /dev/null +++ b/src/shared/Z.EF.Plus.SetIdentity.Shared/Z.EF.Plus.SetIdentity.Shared.shproj @@ -0,0 +1,13 @@ + + + + {83527BB9-2D04-44C3-A1B9-E493ADBF071A} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.projitems b/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.projitems index c20f7622..7679d855 100644 --- a/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.projitems +++ b/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.projitems @@ -2,10 +2,10 @@ {e11c5da2-5a40-43ba-96ff-5a67049739bb} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.shproj b/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.shproj index e7318f93..00d4d52e 100644 --- a/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.shproj +++ b/src/shared/Z.EF.Plus._Core.Shared/Z.EF.Plus._Core.Shared.shproj @@ -3,10 +3,14 @@ 27cbb38c-9814-4251-b919-905514d6d8a0 14.0 - SAK - SAK - SAK - SAK + + + + + + + + diff --git a/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.projitems b/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.projitems index b8a321b2..08c5c073 100644 --- a/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.projitems +++ b/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.projitems @@ -2,10 +2,10 @@ {c8b8ebb1-2a7c-477f-852e-a91b89fb4b6b} - SAK - SAK - SAK - SAK + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) diff --git a/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.shproj b/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.shproj index d54ce328..33002263 100644 --- a/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.shproj +++ b/src/shared/Z.EF.Plus._ExceptionMessage.Shared/Z.EF.Plus._ExceptionMessage.Shared.shproj @@ -3,10 +3,14 @@ 9d4357ac-9e62-4cf0-ae5e-29142cfc0a97 14.0 - SAK - SAK - SAK - SAK + + + + + + + +