Skip to content

Instantly share code, notes, and snippets.

@ifandelse
Created May 6, 2011 02:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ifandelse/958333 to your computer and use it in GitHub Desktop.
Save ifandelse/958333 to your computer and use it in GitHub Desktop.
Trying to work out a way for a Memento Reset's behavior to be unknown/transparent to target domain model
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ResetIdeas
{
class Program
{
static void Main(string[] args)
{
var person = new Person();
var memento = new PersonMemento()
{
FirstName = "Jim",
LastName = "Cowart",
PersonId = "12345",
Ssn = "555-55-5555"
};
memento.Reset( person );
Console.WriteLine(person.ToString());
Console.ReadLine();
}
}
public class Person : IPerson
{
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string PersonId { get; private set; }
public string Ssn { get; private set; }
public override string ToString()
{
return String.Format("FirstName:{0}, LastName:{1}, PersonId:{2}, Ssn:{3}", FirstName, LastName, PersonId, Ssn);
}
}
public class PersonMemento
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string PersonId { get; set; }
public string Ssn { get; set; }
public void Reset(Person instance)
{
dynamic dInstance = new PrivateSetterProxy(instance);
dInstance.FirstName = FirstName;
dInstance.LastName = LastName;
dInstance.PersonId = PersonId;
dInstance.Ssn = Ssn;
}
}
public interface IHaveFirstName
{
string FirstName { get; }
}
public interface IHaveLastName
{
string LastName { get; }
}
public interface IHavePersonId
{
string PersonId { get; }
}
public interface IHaveSsn
{
string Ssn { get; }
}
public interface IPerson : IHaveFirstName, IHaveLastName, IHavePersonId, IHaveSsn
{
}
public class PrivateSetterProxy : DynamicObject
{
private readonly object _instance;
private readonly Type _instanceType;
private const BindingFlags BindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
public PrivateSetterProxy(object instance)
{
_instance = instance;
_instanceType = _instance.GetType();
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if ( TrySettingProperty( binder, value ) ) return true;
return TrySettingField( binder, value );
}
private bool TrySettingField( SetMemberBinder binder, object value )
{
var fieldInfo = _instanceType.GetField(binder.Name, BindingFlags);
if (fieldInfo != null)
{
fieldInfo.SetValue(_instance, value);
return true;
}
return false;
}
private bool TrySettingProperty( SetMemberBinder binder, object value )
{
var propertyInfo = _instanceType.GetProperty(binder.Name, BindingFlags);
if (propertyInfo != null)
{
propertyInfo.SetValue(_instance, value, null);
return true;
}
return false;
}
}
}
@troydemonbreun
Copy link

By "Memento Reset's behavior to be unknown/transparent to target domain model", grammatically I would think you meant "Memento's Reset behavior to be unknown to Person", but I guess that's not what you mean since you already have the Memento pattern with the Memento accepting in the Originator vs. Originator accepting the Memento, so the Memento is transparent to Person.

Do you mean "Memento Reset's behavior to be unknowning of the target domain model"? Or am I completely off base?

@ifandelse
Copy link
Author

Hi there - I sheepishly admit that the above gist was a quickly mushed-up idea based on some conversations I was having with a co-worker regarding the challenges of implementing a Memento-style pattern in Actor frameworks (the particular framework we're using/working on is Symbiote - it's on github). The description - while I could have worded it much more clearly - is accurate. The constraint we're working under is trying to be as non-prescriptive to a consuming developer's domain model as possible. (For example - most ORMs these days are prescriptive in that they require the model to at the least implement virtual members like NHIbernate, whereas others require a particular base class, etc.). So - the above gist's goal was to keep the responsibility of resetting a Person object inside the memento, while still allowing the Person object to have private setters (we can't simply swap references in our case, due to some other Actor-related constraints). I'm aware that many Memento pattern examples typically tack the behavior to create and accept mementos onto the model. Personally, I'm convinced that in cases where the model does not need to be persistence-aware, then it violates SRP to make it so. Anyway - while the above ideas would technically work in C# 4, the performance hit using the dynamic approach would be significant at high volumes. We ended up opting to allow the model to implement a "Populate(x, y, z,.....) call that populates it from a memento. Beats the heck out of the performance hit of reflection under the hood.....

@troydemonbreun
Copy link

I like this approach, that is, the POCO/PI approach to the Memento. Too bad for the performance hit, though. It's great to see these Spikes, thanks for sharing.

@ifandelse
Copy link
Author

Troy - thanks a ton for looking at it. I hadn't heard Fowler's description of "PI" - but I think that's a great description for it especially when trying to explain it to peers who are used to seeing the persistence concern mixed in...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment