Skip to content

Instantly share code, notes, and snippets.

@danielwertheim
Last active September 26, 2015 06:12
Show Gist options
  • Save danielwertheim/074a7a9f22b3cb977319 to your computer and use it in GitHub Desktop.
Save danielwertheim/074a7a9f22b3cb977319 to your computer and use it in GitHub Desktop.
Some MongoDB and Proxies
using System;
using System.Collections.Concurrent;
using Castle.DynamicProxy;
using MongoDB.Bson;
namespace FunnyBunny
{
class Program
{
static void Main(string[] args)
{
var interceptor = new EntityInterceptor();
var generator = new ProxyGenerator();
var p = (Person)generator.CreateClassProxy(typeof(Person), new[] { typeof(IMongoDynamicEntityProxy) }, interceptor);
p.Name = "Daniel";
p.Age = 35;
p.MakeAdmin();
var pp = (IMongoDynamicEntityProxy) p;
DumpChanges(pp);
pp.ClearChanges();
p.Age += 1;
DumpChanges(pp);
}
private static void DumpChanges(IMongoDynamicEntityProxy pp)
{
Console.WriteLine();
Console.WriteLine("===============");
Console.WriteLine("BSON Document with changes");
Console.WriteLine("===============");
var changes = pp.GetChangesAsDocument();
foreach (var entry in changes)
Console.WriteLine($"{entry.Name}:{entry.Value}");
}
}
public class Person
{
private int _age;
public virtual string Name { get; set; }
public virtual int Age
{
get
{
Console.WriteLine("Getting Age");
return _age;
}
set
{
Console.WriteLine("Setting Age");
_age = value;
}
}
public virtual bool IsAdmin { get; protected set; }
public virtual void MakeAdmin()
{
IsAdmin = true;
}
}
public interface IMongoDynamicEntityProxy
{
BsonDocument GetChangesAsDocument();
void ClearChanges();
}
public class State
{
public object Value { get; }
public bool IsDirty { get; private set; }
public State(object value, bool isDirty)
{
Value = value;
IsDirty = isDirty;
}
public State Mutate(object newValue)
{
return new State(newValue, true);
}
public void Commit()
{
IsDirty = false;
}
}
public class EntityInterceptor : IInterceptor
{
private readonly ConcurrentDictionary<string, object> _updatedStateBag = new ConcurrentDictionary<string, object>();
public void Intercept(IInvocation invocation)
{
var name = invocation.MethodInvocationTarget?.Name ?? invocation.Method.Name;
if (IsSetter(name))
{
var key = name.Remove(0, 4);
SetValue(key, invocation.Arguments[0]);
invocation.Proceed();
return;
}
if (invocation.Method.DeclaringType == typeof(IMongoDynamicEntityProxy))
{
switch (name)
{
case "ClearChanges":
ClearChanges();
break;
case "GetChangesAsDocument":
GetChangesAsDocument(invocation);
break;
}
return;
}
invocation.Proceed();
}
private void ClearChanges()
{
_updatedStateBag.Clear();
}
private void GetChangesAsDocument(IInvocation invocation)
{
var document = new BsonDocument();
foreach (var keyValue in _updatedStateBag)
document.Set(keyValue.Key, BsonTypeMapper.MapToBsonValue(keyValue.Value));
invocation.ReturnValue = document;
}
private void SetValue(string key, object value)
{
_updatedStateBag.AddOrUpdate(key, value, (k, v) => value);
}
private bool IsSetter(string name)
{
return name.StartsWith("set_");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment