-
Notifications
You must be signed in to change notification settings - Fork 934
Reduce the number of calls to UpdateTimestampsCache #1467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
391d231
616a1dd
38e569c
bcf4965
5878986
7b04696
b0c5aa2
7e3b9fc
3defece
4f24faa
9607294
63dc8cd
181bba1
298bb22
ed63678
93ae332
fd5c66e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
//------------------------------------------------------------------------------ | ||
// <auto-generated> | ||
// This code was generated by AsyncGenerator. | ||
// | ||
// Changes to this file may cause incorrect behavior and will be lost if | ||
// the code is regenerated. | ||
// </auto-generated> | ||
//------------------------------------------------------------------------------ | ||
|
||
|
||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using NHibernate.Cache; | ||
using NHibernate.Cfg; | ||
using NHibernate.Impl; | ||
using NHibernate.Test.SecondLevelCacheTests; | ||
using NSubstitute; | ||
using NUnit.Framework; | ||
using Environment = System.Environment; | ||
|
||
namespace NHibernate.Test.SecondLevelCacheTest | ||
{ | ||
using System.Threading; | ||
[TestFixture] | ||
public class InvalidationTestsAsync : TestCase | ||
{ | ||
protected override string MappingsAssembly => "NHibernate.Test"; | ||
|
||
protected override IList Mappings => new[] { "SecondLevelCacheTest.Item.hbm.xml" }; | ||
|
||
protected override void Configure(Configuration configuration) | ||
{ | ||
base.Configure(configuration); | ||
configuration.Properties[Cfg.Environment.CacheProvider] = typeof(HashtableCacheProvider).AssemblyQualifiedName; | ||
configuration.Properties[Cfg.Environment.UseQueryCache] = "true"; | ||
} | ||
|
||
[Test] | ||
public async Task InvalidatesEntitiesAsync() | ||
{ | ||
var cache = Substitute.For<UpdateTimestampsCache>(Sfi.Settings, new Dictionary<string, string>()); | ||
((SessionFactoryImpl) (Sfi as DebugSessionFactory).ActualFactory).SetPropertyUsingReflection(x => x.UpdateTimestampsCache, cache); | ||
|
||
var items = new List<Item>(); | ||
using (ISession session = OpenSession()) | ||
{ | ||
using (ITransaction tx = session.BeginTransaction()) | ||
{ | ||
foreach (var i in Enumerable.Range(1, 10)) | ||
{ | ||
var item = new Item { Id = i }; | ||
await (session.SaveAsync(item)); | ||
} | ||
await (tx.CommitAsync()); | ||
} | ||
|
||
using (ITransaction tx = session.BeginTransaction()) | ||
{ | ||
foreach (var i in Enumerable.Range(1, 10)) | ||
{ | ||
var item = await (session.GetAsync<Item>(i)); | ||
item.Name = item.Id.ToString(); | ||
} | ||
await (tx.CommitAsync()); | ||
} | ||
|
||
using (ITransaction tx = session.BeginTransaction()) | ||
{ | ||
foreach (var i in Enumerable.Range(1, 10)) | ||
{ | ||
var item = await (session.GetAsync<Item>(i)); | ||
await (session.DeleteAsync(item)); | ||
} | ||
await (tx.CommitAsync()); | ||
} | ||
} | ||
//Should receive one preinvalidation and one invalidation per commit | ||
await (cache.Received(3).PreInvalidateAsync(Arg.Is<object[]>(x => x.Length==1 && (string)x[0] == "Item"), CancellationToken.None)); | ||
await (cache.Received(3).InvalidateAsync(Arg.Is<object[]>(x => x.Length == 1 && (string) x[0] == "Item"), CancellationToken.None)); | ||
|
||
} | ||
|
||
public async Task CleanUpAsync(CancellationToken cancellationToken = default(CancellationToken)) | ||
{ | ||
using (ISession s = OpenSession()) | ||
using (ITransaction tx = s.BeginTransaction()) | ||
{ | ||
await (s.DeleteAsync("from Item", cancellationToken)); | ||
await (tx.CommitAsync(cancellationToken)); | ||
} | ||
} | ||
|
||
public void CleanUp() | ||
{ | ||
using (ISession s = OpenSession()) | ||
using (ITransaction tx = s.BeginTransaction()) | ||
{ | ||
s.Delete("from Item"); | ||
tx.Commit(); | ||
} | ||
} | ||
|
||
protected override void OnTearDown() | ||
{ | ||
CleanUp(); | ||
base.OnTearDown(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using NHibernate.Cache; | ||
using NHibernate.Cfg; | ||
using NHibernate.Impl; | ||
using NHibernate.Test.SecondLevelCacheTests; | ||
using NSubstitute; | ||
using NUnit.Framework; | ||
using Environment = System.Environment; | ||
|
||
namespace NHibernate.Test.SecondLevelCacheTest | ||
{ | ||
[TestFixture] | ||
public class InvalidationTests : TestCase | ||
{ | ||
protected override string MappingsAssembly => "NHibernate.Test"; | ||
|
||
protected override IList Mappings => new[] { "SecondLevelCacheTest.Item.hbm.xml" }; | ||
|
||
protected override void Configure(Configuration configuration) | ||
{ | ||
base.Configure(configuration); | ||
configuration.Properties[Cfg.Environment.CacheProvider] = typeof(HashtableCacheProvider).AssemblyQualifiedName; | ||
configuration.Properties[Cfg.Environment.UseQueryCache] = "true"; | ||
} | ||
|
||
[Test] | ||
public void InvalidatesEntities() | ||
{ | ||
var cache = Substitute.For<UpdateTimestampsCache>(Sfi.Settings, new Dictionary<string, string>()); | ||
((SessionFactoryImpl) (Sfi as DebugSessionFactory).ActualFactory).SetPropertyUsingReflection(x => x.UpdateTimestampsCache, cache); | ||
|
||
var items = new List<Item>(); | ||
using (ISession session = OpenSession()) | ||
{ | ||
using (ITransaction tx = session.BeginTransaction()) | ||
{ | ||
foreach (var i in Enumerable.Range(1, 10)) | ||
{ | ||
var item = new Item { Id = i }; | ||
session.Save(item); | ||
} | ||
tx.Commit(); | ||
} | ||
|
||
using (ITransaction tx = session.BeginTransaction()) | ||
{ | ||
foreach (var i in Enumerable.Range(1, 10)) | ||
{ | ||
var item = session.Get<Item>(i); | ||
item.Name = item.Id.ToString(); | ||
} | ||
tx.Commit(); | ||
} | ||
|
||
using (ITransaction tx = session.BeginTransaction()) | ||
{ | ||
foreach (var i in Enumerable.Range(1, 10)) | ||
{ | ||
var item = session.Get<Item>(i); | ||
session.Delete(item); | ||
} | ||
tx.Commit(); | ||
} | ||
} | ||
//Should receive one preinvalidation and one invalidation per commit | ||
cache.Received(3).PreInvalidate(Arg.Is<object[]>(x => x.Length==1 && (string)x[0] == "Item")); | ||
cache.Received(3).Invalidate(Arg.Is<object[]>(x => x.Length == 1 && (string) x[0] == "Item")); | ||
|
||
} | ||
|
||
public void CleanUp() | ||
{ | ||
using (ISession s = OpenSession()) | ||
using (ITransaction tx = s.BeginTransaction()) | ||
{ | ||
s.Delete("from Item"); | ||
tx.Commit(); | ||
} | ||
} | ||
|
||
protected override void OnTearDown() | ||
{ | ||
CleanUp(); | ||
base.OnTearDown(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using System; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
|
||
namespace NHibernate.Test | ||
{ | ||
//Utility extension methods for use in unit tests. | ||
public static class TestExtensions | ||
{ | ||
public static T SetPropertyUsingReflection<T, TProp>(this T instance, Expression<Func<T, TProp>> property, TProp value) | ||
{ | ||
var method = property.Body as MemberExpression; | ||
var propertyInfo = typeof(T).GetProperty(method.Member.Name); | ||
if (propertyInfo!= null && propertyInfo.CanWrite) | ||
{ | ||
propertyInfo.SetValue(instance, value); | ||
} | ||
else | ||
{ | ||
//camel cased field | ||
var name = method.Member.Name.Substring(0, 1).ToLowerInvariant() + method.Member.Name.Substring(1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not convinced by the need of At the very least if we keep this, it should throw when not finding the field. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a method I just copied straight from my own at-work code, where we usually use it to set properties on NH mapped domain classes, where the setters are hidden. It uses the current form to provide chaining, so that settings many properties doesn't become a burden. The lambda is just refactoring friendly (we don't use the field version, it was added here) I don't mind having a simpler implementation.
Absolutely. That was an omission. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So maybe adjust it by changing if (field != null)
{
field.SetValue(instance, value);
} To: if (field == null)
var field = typeof(T).GetField("_" + name, BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null)
throw new InvalidOperationException($"Unable to find a setter or a backing field for property {method.Member.Name}");
field.SetValue(instance, value); |
||
var field = typeof(T).GetField(name, BindingFlags.NonPublic | BindingFlags.Instance); | ||
if (field != null) | ||
{ | ||
field.SetValue(instance, value); | ||
} | ||
} | ||
return instance; | ||
} | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maca88 we need a way to ignore a method if it's async counterpart is marked as obsolete. This would happen a lot in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add an option to make this possible in the following days, most likely tomorrow.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @maca88. Don't need to rush.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new option was added and released with the version 0.7.0. To ignore all obsolete async counterparts add the following lines inside the
analyzation
property:where
Obsolete
is a method rule defined as: