Load navigation properties necessary for a method in POCO in EF - c#

Please see the following example of my problem.
After I received the result variable from the Calculate() method the EF context is disposed. If I later invoke the DoMethod() on this result I get an error, because the EF navigation property SomeObjects aren't loaded.
I could think of the following solutions to prevent this problem?
Eagerly Loading SomeObjects within the Calculate() method (xyList = context.Xys.Include(x => x.SomeObjects).ToList();) (unnecessary loads of this property if not used later)
Don't close the DB context or use a global context (very bad!)
Load the missing EF navigation property within the DoMethod()
I would go with the third one, because the DoMethod() isn't always invoked and thus I don't need SomeObjects if it's not.
My question is how to realize the third solution? And is it the right way? Querying out of a POCO to get the necessary data seems to be a bit odd.
class Program
{
static void Main(string[] args)
{
...
Xy result = Calculation.Calculate();
...
//Maybee this method is invoked
result.DoMethod();
}
}
// POCO class
public class XY
{
public virtual List<Xz> SomeObjects { get; set; }
public void DoMethod()
{
foreach (var obj in SomeObjects)
{
...
}
}
}
class Calculation
{
public static Xy Calculate() {
Xy result;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
}
return result;
}
}

Here are a few options I've tried or thought of. #3 is one attempt at what you indicate is your preferred approach.
1. Only doing the calculation at the last moment.
This has the overhead of creating context every time result is needed, but defers using context until a result is needed. Your use case dictates whether this is helpful or not.
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation();
...
//Maybe this method is invoked
calc.GetResult().DoMethod();
}
}
class Calculation
{
public Xy GetResult();
{
Xy result;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
}
return result;
}
}
2. Caching the result and keeping the context alive
This is your option #2, but without a global context (which you rightly were concerned about). If you are concerned about not disposing of the context, take a look at this: http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html.
Aside from the memory overhead of the loaded Context, I don't see a downside to this. EF will defer loading SomeObjects until you first need them via a call to DoMethod(). You are trading keeping the context around for not having to load SomeObject unless required.
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation(new MyContext());
//use result, perhaps many times
/*something with calc.Result; */
...
//Maybe this method is invoked
calc.Result.DoMethod();
//context will not go away until Calculation does
}
}
class Calculation
{
private MyContext context = null;
private Xy result = null;
public Calculation(MyContext context)
{
this.context = context;
}
public Xy Result {
get {
if (result == null) {
result = Calculate();
}
return result;
}
}
private Xy Calculate();
{
Xy result;
xyList = context.Xys.ToList();
...
result = xyList[calculatedIndex];
return result;
}
}
3. Implementing your option #3 via a Dynamic Proxy
This allows wrapping the XY in a proxy that behaves like an XY, but intercepting the call to DoMethod to get a new context so that SomeObjects can resolve in the new context. I used Castle Dynamic Proxy, available in the Castle.Core project, which you can simply add via Nuget. There's enough conceptual overhead, that I think it might be a counter-proof of concept. I.e., it demonstrates that keeping the context around so that SomeObjects can be lazy loaded against the original context may be the cleanest idea. And again, refer to arguments in http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext.html for why it may be ok to keep the context around. BTW, that article comes out of dialog with the EF developer team.
using Castle.DynamicProxy;
class Program
{
static void Main(string[] args)
{
...
Calculation calc = new Calculation(new MyContext());
//use result, perhaps many times
/*something with calc.Result; */
...
//Maybe this method is invoked
calc.Result.DoMethod();
}
}
// POCO class
public class XY
{
public virtual List<Xz> SomeObjects { get; set; }
public virtual void DoMethod()
{
foreach (var obj in SomeObjects)
{
...
}
}
}
public class XYInterceptor : XY, IInterceptor
{
public void Intercept(IInvocation invocation)
{
if (invocation.Method.Name == "DoMethod")
{
//get a new context so that we can have SomeObjects resolve properly
using (var context = new MyContext())
{
var newXy = context.Xys.Find(((XY)invocation.InvocationTarget).Id);
newXy.DoMethod();
}
}
else
{
//Any other method goes straight through
invocation.Proceed();
}
}
}
public class Calculation
{
private XY result = null;
public XY Result {
get {
if (result == null) {
result = Calculate();
}
return result;
}
}
private XY Calculate()
{
XY proxyResult;
using (var context = new MyContext())
{
xyList = context.Xys.ToList();
...
Xy realResult = xyList[calculatedIndex];
proxyResult = (new ProxyGenerator()).CreateClassProxyWithTarget<XY>(realResult, new XYInterceptor());
return proxyResult;
}
}
}
One annoying facet of my third sketch is that it does not update the Result with the new XY. That would need to be made to work before it's really ready for use.

Related

NUnit use different objects as Test Cases

I am trying to write an Update Test for different products (which are classes in this case) and different time steps :
public class UpdateTest
{
private static Product ProductLastYear;
private static Product ProductTwoYearsAgo;
public UpdateTest(Product product)
{
var previousReleasedProducts = new Products();
ProductLastYear = previousReleasedProducts.GetProduct(Years.GetLastYear());
ProductTwoYearsAgo = previousReleasedProducts.GetProduct(Years.GetTwoYearsAgo());
}
Each product needs to be installed, and afterwards it is checked if the installation was successful (this is basically a pre-step before the Update). Right now, I am using two Tests for this:
[Test, Category("UpdateLastYear")), Order((int) NunitTestOrderEnum.Order.First)]
public void InstallPreviousReleasedProduct()
{
using (var controller = new BootstrapperController(ProductLastYear))
{
controller.Install();
}
var successfulInstallation = InstallationValidator.ValidateInstall(ProductLastYear);
Assert.That(successfulInstallation, Is.True);
}
[Test, Category("UpdateTwoYearsAgo"), Order((int) NunitTestOrderEnum.Order.First)]
public void InstallTwoYearsAgoProduct()
{
using (var controller = new BootstrapperController(ProductTwoYearsAgo))
{
controller.Install();
}
var successfulInstallation = InstallationValidator.ValidateInstall(ProductTwoYearsAgo);
Assert.That(successfulInstallation, Is.True);
}
Now, both tests have some code redundancy, which I would like to avoid. I was thinking about using TestCases for this, something like :
[TestCase(ProductLastYear), Category("UpdateLastYear"), Order((int) NunitTestOrderEnum.Order.First)]
[TestCase(ProductTwoYearsAgo), Category("UpdateTwoYearsAgo"), Order((int) NunitTestOrderEnum.Order.First)]
public void InstallPreProduct(Product product)
{
using (var controller = new BootstrapperController(product))
{
controller.Install();
}
var successfulInstallation = InstallationValidator.ValidateInstall(product);
Assert.That(successfulInstallation, Is.True);
}
Is something like this possible? I tried different Syntax for that approach, but it does not seem to work that easily.
You can only use compile-time constants within attributes. However your static fields are no constants.
You can use the TestCaseSource-attribute:
[TestCaseSource(nameof(ProvideTestcases))]
public void InstallPreProduct(Product product)
{
using (var controller = new BootstrapperController(product))
{
controller.Install();
}
var successfulInstallation = InstallationValidator.ValidateInstall(product);
Assert.That(successfulInstallation, Is.True);
}
public static IEnumerable<TestCaseData> ProvideTestcases()
{
yield return new TestCaseData(ProductLastYear).SetCategory("UpdateLastYear");
yield return new TestCaseData(ProductTwoYearsAgo).SetCategory("UpdateTwoYearsAgo");
}
However that assumes your static fields are already initialized, which isn't the case. So you need a static constructor for your testclass.
[TestFixture]
public UpdateTest
{
public static ProductLastYear;
public static ProductTwoYearsAgo;
static
{
ProductLastYear = ...;
ProductTwoYearsAgo = ...;
}
}
This is pretty much boilerplate for so little duplication. So it's a tradeoff if or of it not this is worth the afford.
Another opportunity is to introduce some static constant, like an enum that reflects the property to be used:
[TestCase(MyEnum.LastYear), Category("UpdateLastYear")]
[TestCase(MyEnum.TwoYearsAgo), Category("UpdateTwoYearsAgo")]
public void InstallPreProduct(MyEnum product)
{
var Product = product == MyEnum.LastYear ?
ProductLastYear :
ProductTwoYearsAgo ;
using (var controller = new BootstrapperController(product))
{
controller.Install();
}
var successfulInstallation = InstallationValidator.ValidateInstall(product);
Assert.That(successfulInstallation, Is.True);
}
An enum is a constant expression, so you can easily use it within the test-attributes.

C# How check if object is created in using statement [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have a class MyClass. I want to create MyClass instance in using statement and to operation something 1 in constructor, but if instance is created as nested in using statement with other instance of MyClass I want to do something 2. I have no idea how to check it. I thought about static class which check if current code statement was colled from using statement which other instance of MyClass, but i don't know how check it.
public class MyClass : IDisposable
{
public MyClass()
{
if(condition)
//do something 1
else
//do something 2
}
public void Dispose()
{
//do something
}
}
using (var mc = new MyClass()) //do something 1 in constructor
{
using (var mc1 = new MyClass()) //do something 2 in constructor
{
using (var mc2 = new MyClass()) //do something 2
{
}
}
using (var mc3 = new MyClass()) //do something 2 in constructor
{
}
Edit:
I try to do some kind of scope. It shoud be some bigger scope then TransactionScope. In my scope i want to have fiew TransactionScopes. I want to use in whole scope the same connection with database without returning it to connection pool. So when i create the major scope in using statement I want to get new connection from pool, but if i created nested using block with my scope i want to use connection from major scope. Nested is posiible because in may major using block i can run methods thats contain another using block with my scope.
The simple answer to your question is: you can't.
using is pure syntactic sugar. A typical using statement:
using (MyDisposableClass a = GetMyDisposableClass())
{
// ...
}
Gets translated directly into this:
MyDisposableClass a = null;
try {
a = GetMyDisposableClass();
// ...
}
finally {
if (a != null) a.Dispose();
}
In general, in .NET you can't know from whence your code has been called much beyond the function level by reflecting the callstack. The StackFrame object from [System.Diagnostics][1] will tell you the IL offset for the current method, so I suppose if you are really determined you could take apart the IL for the current method and try and figure out where you are within any try/finally code, but that's sounding really flimsy and gross to me.
What on earth are you trying to do that you feel you must manage it like this?
To me it feels like what you want is a factory object of some kind:
public interface IMyClass { int Level { get; } } // whatever
public class MyClassFactory {
private delegate void Notifier(int level);
public class MyClass : IDisposable
{
public MyClass(int level, Notifier notifier)
{
_level = level;
_notifier = notifier;
}
private int _level;
private Notifier _notifier;
~MyClass() { Dispose(false); }
public int Level { get { return level; } }
public Dispose() { Dispose(true); GC.SuppressFinalize(this); }
private bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed) {
if (disposing) {
notifier(Level);
_disposed = false;
}
else { throw new Exception("My class used outside using block."); }
}
}
}
private int _level = 0;
public IMyClass Make()
{
return new MyClass(_level++,
childLevel => {
if (childLevel == _level)
--_level;
else throw new Exception("Disposed out of order.");
});
}
}
What this does is build a factory that hides the constructor to MyClass and exposes a factory method to make IMyClass objects that you can use. It does a strict ordering such that objects are constructed and disposed in order, which more or less meets your nesting requirement. You can make the objects do something different based on their Level, if you so choose.
And I would still hesitate to use this code. It doesn't feel right, but at least it's harder to do something wrong as long as you only use one factory per method.
I'm guessing that what you really want to do is to have begin/end semantics in a code block that tracks nesting, but you want the compiler to manage that for you. That I've done with an object that takes two functions to call, one on begin and one on end:
public class BeginEnd<T> : IDisposable
{
private Action<T> _end;
private bool _disposed;
private T _val;
public BeginEnd(T val, Action<T> begin, Action<T> end)
{
_end = end;
_val = val;
begin(val);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~BeginEnd() { Dispose(false); }
protected virtual void Dispose(bool disposing)
{
if (!_disposed) {
if (disposing) {
_disposed = true;
_end(_val);
}
}
}
}
Which can then be used in a context like this:
public class Tracker {
private int _level;
public BeginEnd<int> Track()
{
return new BeginEnd<int>(_level++,
lev1 => {
Debug.WriteLine("Begin " + lev1);
},
lev2 => {
Debug.WriteLine("End " + lev2);
--_level;
});
}
}
//...
Tracker t = new Tracker();
using (var n0 = t.Track()) {
using (var n1 = t.Track()) {
}
using (var n2 = t.Track()) { }
}
// prints:
// Begin 0
// Begin 1
// End 1
// Begin 1
// End 1
// End 0
which correctly tracks the nesting of the using blocks by enforcing a begin/end rule.
Whether or not this is an appropriate use of the language construct has been debated before. I feel like your problem can be solved in another more appropriate way.

Automatically calling an init function whenever an object is used for the 1st time

I have an object that only initializes itself with barebones data when constructed (fast), and loads itself for real (slow) when first accessed. The idea is that I'm creating a lot of these barebones objects at startup and hash them into a map, then fully load each object whenever it is individually accessed for the first time. The problem is that I cannot guarantee how clients will interact with this object, there are multiple public methods that might be invoked.
Is there a good pattern to support this kind of situation? The obvious (and my current) solution is to track state with an internal bool, check against that bool in every function that might be invoked, and load that way. But that requires code duplication of that behavior across all public functions, and is vulnerable to errors.
I can imagine a single point-of-entry method that then dishes out behaviors based on a client request type etc., but before I go consider going down that road I want to see if there's a commonly accepted approach/pattern that I might not be aware of. I'm doing this in C#, but any insight is appreciated.
If I understood what you want to achieve, you are looking for the Proxy Design Pattern, more specifically, a virtual Proxy.
Refer to http://www.dofactory.com/net/proxy-design-pattern
A small example would be something like:
public abstract class IObjectProvider
{
public abstract IObjectProvider Object{get;}
public abstract void doStuff();
}
public class RealObject : IObjectProvider
{
public RealObject()
{
//Do very complicated and time taking stuff;
}
public override IObjectProvider Object
{
get { return this; }
}
public override void doStuff()
{
//do this stuff that these objects normally do
}
}
public class ObjectProxy : IObjectProvider
{
private IObjectProvider objectInstance = null;
public override IObjectProvider Object
{
get
{
if (objectInstance == null)
objectInstance = new RealObject();
return objectInstance;
}
}
public override void doStuff()
{
if(objectInstance!=null)
objectInstance.doStuff();
}
}
public class SkeletonClass
{
public IObjectProvider Proxy1 = new ObjectProxy();
public IObjectProvider Proxy2 = new ObjectProxy();
}
static void Main(String[] args)
{
//Objects Not Loaded
SkeletonClass skeleton = new SkeletonClass();
//Proxy1 loads object1 on demand
skeleton.Proxy1.Object.doStuff();
//Proxy2 not loaded object2 until someone needs it
}
Here's an example of dynamic proxy approach.
using System;
using System.Diagnostics;
using Castle.DynamicProxy; //Remember to include a reference, too. It's nugettable package is Castle.Core
namespace ConsoleApp
{
public class ActualClass
{
//Have static instances of two below for performance
private static ProxyGenerator pg = new ProxyGenerator();
private static ActualClassInterceptor interceptor = new ActualClassInterceptor();
//This is how we get ActualClass items that are wrapped in the Dynamic Proxy
public static ActualClass getActualClassInstance()
{
ActualClass instance = new ActualClass();
return pg.CreateClassProxyWithTarget<ActualClass>(instance, interceptor);
}
//Tracking whether init has been called
private bool initialized = false;
//Will be used as evidence of true initialization, i.e. no longer null
private int? someValue = null;
public void Initialize()
{
if (!initialized)
{
//do some initialization here.
someValue = -1; //Will only get set to non-null if we've run this line.
initialized = true;
}
}
//Any methods you want to intercept need to be virtual!
public virtual int replaceValue(int value)
{
//below will blow up, if someValue has not been set to -1 via Initialize();
int oldValue = someValue.Value;
someValue = value;
return oldValue;
}
//block off constructor from public to enforce use of getActualClassInstance
protected ActualClass() { }
}
public class ActualClassInterceptor : ActualClass, IInterceptor
{
public void Intercept(IInvocation invocation)
{
//Call initialize before proceeding to call the intercepted method
//Worth noting that this is the only place we actually call Initialize()
((ActualClass)invocation.InvocationTarget).Initialize();
invocation.Proceed();
}
}
class Program
{
static void Main(string[] args)
{
ActualClass instance1 = ActualClass.getActualClassInstance();
ActualClass instance2 = ActualClass.getActualClassInstance();
int x1 = instance1.replaceValue(41);
int x2 = instance2.replaceValue(42);
int y1 = instance1.replaceValue(82);
Debug.Assert(y1 == 41);
int y2 = instance2.replaceValue(84);
Debug.Assert(y2 == 42);
var read = Console.ReadKey();
}
}
}

How to implement Lazy loading with PostSharp?

I would like to implement lazy loading on properties with PostSharp.
To make it short, instead of writing
SomeType _field = null;
private SomeType Field
{
get
{
if (_field == null)
{
_field = LongOperation();
}
return _field;
}
}
I would like to write
[LazyLoadAspect]
private object Field
{
get
{
return LongOperation();
}
}
So, I identify that I need to emit some code in the class to generate the backing field, as well as inside the getter method in order to implement the test.
With PostSharp, I was considering overriding CompileTimeInitialize, but I am missing the knowledge to get a handle over the compiled code.
EDIT:
The question can be extended to any parameterless method like:
SomeType _lazyLoadedField = null;
SomeType LazyLoadableMethod ()
{
if(_lazyLoadedField ==null)
{
// Long operations code...
_lazyLoadedField = someType;
}
return _lazyLoadedField ;
}
would become
[LazyLoad]
SomeType LazyLoadableMethod ()
{
// Long operations code...
return someType;
}
After our comments, I think I know what you want now.
[Serializable]
public class LazyLoadGetter : LocationInterceptionAspect, IInstanceScopedAspect
{
private object backing;
public override void OnGetValue(LocationInterceptionArgs args)
{
if (backing == null)
{
args.ProceedGetValue();
backing = args.Value;
}
args.Value = backing;
}
public object CreateInstance(AdviceArgs adviceArgs)
{
return this.MemberwiseClone();
}
public void RuntimeInitializeInstance()
{
}
}
Test code
public class test
{
[LazyLoadGetter]
public int MyProperty { get { return LongOperation(); } }
}
Thanks to DustinDavis's answer and comments, I could work on my own implementation, and I just wanted here to share it to help other people.
The main differences from the original answer are:
Implement the suggested "only run the operation once" (purpose of the lock)
Made the initialization status of the backing field more reliable by passing this responsibility to a boolean.
Here is the code:
[Serializable]
public class LazyLoadAttribute : LocationInterceptionAspect, IInstanceScopedAspect
{
// Concurrent accesses management
private readonly object _locker = new object();
// the backing field where the loaded value is stored the first time.
private object _backingField;
// More reliable than checking _backingField for null as the result of the loading could be null.
private bool _hasBeenLoaded = false;
public override void OnGetValue(LocationInterceptionArgs args)
{
if (_hasBeenLoaded)
{
// Job already done
args.Value = _backingField;
return;
}
lock (_locker)
{
// Once the lock passed, we must check if the aspect has been loaded meanwhile or not.
if (_hasBeenLoaded)
{
args.Value = _backingField;
return;
}
// First call to the getter => need to load it.
args.ProceedGetValue();
// Indicate that we Loaded it
_hasBeenLoaded = true;
// store the result.
_backingField = args.Value;
}
}
public object CreateInstance(AdviceArgs adviceArgs)
{
return MemberwiseClone();
}
public void RuntimeInitializeInstance() { }
}
I think the requirement cannot be accurately described as 'lazy loading', but is a special case of a more general caching aspect with in-AppDomain storage but without eviction. A general caching aspect would be able to handle method parameters.

How to store Actions that do not prevent garbage collection of variables they use in scope

I'm trying to fix a garbage collection problem of a MVVM application which uses the following model of Undo stack.
The example is very minimalistic and real world code is much different, uses a factory class of undo lists per ViewModel instead of a single undolist but is representative:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel;
using System.Linq;
namespace ConsoleApplication9
{
public class UndoList
{
public bool IsUndoing { get; set; }
private Stack<Action> _undo = new Stack<Action>();
public Stack<Action> Undo
{
get { return _undo; }
set { _undo = value; }
}
private static UndoList _instance;
// singleton of the undo stack
public static UndoList Instance
{
get
{
if (_instance == null)
{
_instance = new UndoList();
}
return _instance;
}
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// execute the last undo operation
public void Undo()
{
UndoList.Instance.IsUndoing = true;
var action = UndoList.Instance.Undo.Pop();
action();
UndoList.Instance.IsUndoing = false;
}
// push an action into the undo stack
public void AddUndo(Action action)
{
if (UndoList.Instance.IsUndoing) return;
UndoList.Instance.Undo.Push(action);
}
// create push an action into the undo stack that resets a property value
public void AddUndo(string propertyName, object oldValue)
{
if (UndoList.Instance.IsUndoing) return;
var property = this.GetType().GetProperties().First(p => p.Name == propertyName);
Action action = () =>
{
property.SetValue(this, oldValue, null);
};
UndoList.Instance.Undo.Push(action);
}
}
public class TestModel : ViewModel
{
private bool _testProperty;
public bool TestProperty
{
get
{
return _testProperty;
}
set
{
base.AddUndo("TestProperty", _testProperty);
_testProperty = value;
}
}
// mock property indicating if a business action has been done for test
private bool _hasBusinessActionBeenDone;
public bool HasBusinessActionBeenDone
{
get
{
return _hasBusinessActionBeenDone;
}
set
{
_hasBusinessActionBeenDone = value;
}
}
public void DoBusinessAction()
{
AddUndo(() => { inverseBusinessAction(); });
businessAction();
}
private void businessAction()
{
// using fake property for brevity of example
this.HasBusinessActionBeenDone = true;
}
private void inverseBusinessAction()
{
// using fake property for brevity of example
this.HasBusinessActionBeenDone = false;
}
}
class Program
{
static void Test()
{
var vm = new TestModel();
// test undo of property
vm.TestProperty = true;
vm.Undo();
Debug.Assert(vm.TestProperty == false);
// test undo of business action
vm.DoBusinessAction();
vm.Undo();
Debug.Assert(vm.HasBusinessActionBeenDone == false);
// do it once more without Undo, so the undo stack has something
vm.DoBusinessAction();
}
static void Main(string[] args)
{
Program.Test();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
// at this point UndoList.Instance.Undo
// contains an Action which references the TestModel
// which will never be collected...
// in real world code knowing when to clear this is a problem
// because it is a singleton factory class for undolists per viewmodel type
// ideally would be to clear the list when there are no more references
// to the viewmodel type in question, but the Actions in the list prevent that
}
}
}
You see that when any viewModel goes out of scope the actions in the UndoList keep references to them. The real code groups various viewmodels into grouped undolists (viewModels that contain child viewmodels share the same undo stack), so it is difficult to know when and where to put the clearing.
I was wondering if there is some method to make those actions expire if they are the only one keeping references to the variables inside them?
Suggestions welcome!
I've got a solution for you. I don't like the use of the UndoList as a singleton, but I've kept it to provide you with a direct answer to your question. In practice I wouldn't use a singleton.
Now, you will find it very difficult to avoid capturing references to your view models in your actions. It would make your code very ugly if you tried. The best approach is to make your view models implement IDisposable and make sure that you dispose of them when they go out of scope. Remember that the garbage collector never calls Dispose so you must.
Using IDisposable is the standard model for cleaning up when an
instance is no longer needed.
So the first thing to define is a helper class that executes an action when it is disposed.
public sealed class AnonymousDisposable : IDisposable
{
private readonly Action _dispose;
private int _isDisposed;
public AnonymousDisposable(Action dispose)
{
_dispose = dispose;
}
public void Dispose()
{
if (Interlocked.Exchange(ref _isDisposed, 1) == 0)
{
_dispose();
}
}
}
Now I can write things like this to remove elements from lists:
var disposable = new AnonymousDisposable(() => list.Remove(item));
Later, when I call disposable.Dispose() the item is removed from the list.
Now here's your code re-implemented.
I've changed UndoList to be a static class, not a singleton. You can change it back if need be.
public static class UndoList
{
public static bool IsUndoing { get; private set; }
private static List<Action> _undos = new List<Action>();
public static IDisposable AddUndo(Action action)
{
var disposable = (IDisposable)null;
if (!IsUndoing)
{
disposable = new AnonymousDisposable(() => _undos.Remove(action));
_undos.Add(action);
}
return disposable ?? new AnonymousDisposable(() => { });
}
public static bool Undo()
{
IsUndoing = true;
var result = _undos.Count > 0;
if (result)
{
var action = _undos[_undos.Count - 1];
_undos.Remove(action);
action();
}
IsUndoing = false;
return result;
}
}
You'll notice that I've replaced the stack with a list. I did that because I need to remove items from inside the list.
Also, you can see that AddUndo now returns an IDisposable. Calling code needs to keep the return disposable and call Dispose when it wants to remove the action from the list.
I've also internalized the Undo action. It didn't make sense to have it in the view model. Calling Undo effectively pops the top item off of the list and executes the action and returns true. However, if the list is empty it returns false. You can use this for testing purposes.
The ViewModel class now looks like this:
public class ViewModel : IDisposable, INotifyPropertyChanged
{
public ViewModel()
{
_disposables = new List<IDisposable>();
_disposable = new AnonymousDisposable(() =>
_disposables.ForEach(d => d.Dispose()));
}
private readonly List<IDisposable> _disposables;
private readonly IDisposable _disposable;
public void Dispose()
{
_disposable.Dispose();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void AddUndo(Action action)
{ ... }
protected void SetUndoableValue<T>(Action<T> action, T newValue, T oldValue)
{ ... }
}
It implements IDisposable and internally, keeps track of a list of disposables and an anonymous disposable that will dispose of the items in the list when the view model itself is disposed of. Whew! A mouthful, but I hope that makes sense.
The AddUndo method body is this:
protected void AddUndo(Action action)
{
var disposable = (IDisposable)null;
Action inner = () =>
{
_disposables.Remove(disposable);
action();
};
disposable = UndoList.AddUndo(inner);
_disposables.Add(disposable);
}
Internally it calls UndoList.AddUndo passing in an action that will remove the returned IDisposable from the view model's list of undo actions when UndoList.Undo() is called - as well as, importantly, actually executing the action.
So this means that when the view model is disposed all of its outstanding undo actions are removed from the undo list and when Undo is called the associated disposable is removed from the view model. And this ensures that you are not keeping references to the view model when it is disposed of.
I created a helper function called SetUndoableValue that replaced your void AddUndo(string propertyName, object oldValue) method which wasn't strongly-typed and could cause you to have run-time errors.
protected void SetUndoableValue<T>(Action<T> action, T newValue, T oldValue)
{
this.AddUndo(() => action(oldValue));
action(newValue);
}
I made both of these methods protected as public seemed too promiscuous.
The TestModel is more-or-less the same:
public class TestModel : ViewModel
{
private bool _testProperty;
public bool TestProperty
{
get { return _testProperty; }
set
{
this.SetUndoableValue(v => _testProperty = v, value, _testProperty);
}
}
public bool HasBusinessActionBeenDone { get; set; }
public void DoBusinessAction()
{
this.AddUndo(this.inverseBusinessAction);
businessAction();
}
private void businessAction()
{
this.HasBusinessActionBeenDone = true;
}
private void inverseBusinessAction()
{
this.HasBusinessActionBeenDone = false;
}
}
And finally, here's the code that tests the UndoList functions correctly:
using (var vm = new TestModel())
{
Debug.Assert(UndoList.Undo() == false);
vm.TestProperty = true;
Debug.Assert(UndoList.Undo() == true);
Debug.Assert(UndoList.Undo() == false);
Debug.Assert(vm.TestProperty == false);
vm.DoBusinessAction();
Debug.Assert(UndoList.Undo() == true);
Debug.Assert(vm.HasBusinessActionBeenDone == false);
vm.DoBusinessAction();
}
Debug.Assert(UndoList.Undo() == false);
Please let me know if I can provide any more detail on anything.
If you can't clean it up any other way you could use WeakReference to hold property, but I think there would be other issues because this would still cause a Action instance to exist with a null reference attached to it.
As a quick look I would be more inclined to use the singleton to hold a registration to the model and let the model manage a instance list of all the undo actions attached to it. When the model goes out of scope call a clean-up method on it or implement a IDisposable type interface on it may if this fits. However depending on the implementation you may not need the singleton anyway.

Categories