Skip to content

Instantly share code, notes, and snippets.

@jpolvora
Created May 19, 2012 00:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpolvora/2728270 to your computer and use it in GitHub Desktop.
Save jpolvora/2728270 to your computer and use it in GitHub Desktop.
InterceptWithAttributeEntity
using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Dynamic;
using System.Reflection;
using System.Linq.Expressions;
static void InterceptWithAttributeEntity()
{
var myContext = new MyContext();
//will look for an [Aspect] attribute on myContext and apply defined aspects
IMyContext proxyContext = ObjectProxyFactory.CreateUsingAttribute<IMyContext>(myContext);
var customer = proxyContext.Customers.FirstOrDefault();
if (customer == null)
{
System.Console.WriteLine("No customers in Db");
System.Console.ReadKey();
customer = new Customer { Name = "Jone" };
proxyContext.Customers.Add(customer);
proxyContext.SaveChanges(); // intercepted
return;
}
//will look for an an [Aspect] attribute on customer type definition and apply defined aspects
ICustomer proxyCustomer = ObjectProxyFactory.CreateUsingAttribute<ICustomer>(customer);
//view notifications in console
proxyCustomer.PropertyChanged += (s, e) =>
{
System.Console.WriteLine("Property Changed: {0} on {1}",
e.PropertyName, s);
};
System.Console.WriteLine(proxyCustomer.Id);
proxyCustomer.Id = 0;
System.Console.WriteLine(proxyCustomer.Id);
proxyCustomer.Id = 1; //property changed
System.Console.WriteLine(proxyCustomer.Id);
proxyCustomer.Id = 2; // changed
System.Console.WriteLine(proxyCustomer.Id);
proxyCustomer.Id = 2; //not changed
System.Console.WriteLine(proxyCustomer.Name);
proxyCustomer.Name = null;
System.Console.WriteLine(proxyCustomer.Name);
proxyCustomer.Name = "Teste";
System.Console.WriteLine(proxyCustomer.Name);
}
public class DbContextBehavior : IAspectBehavior<IMyContext>
{
public DbContextBehavior()
{
MethodsToIntercept = Helpers.GetMethodNames<IUnitOfWork>(c => c.SaveChanges());
}
#region Implementation of IAspectBehavior<IContext>
public string[] MethodsToIntercept { get; private set; }
public void PreAspect(AspectContext<IMyContext> ctx)
{
Debug.WriteLine("Entering {0}", ctx.CallCtx.MethodName);
}
public void PostAspect(AspectContext<IMyContext> ctx)
{
Debug.WriteLine("Exiting {0}", ctx.CallCtx.MethodName);
}
#endregion
}
//This aspect definition assumes that your POCO class implements INotifyPropertyChanged, but doesn't need to inherit
//from base classes or to create an invoke method for the event.
//All work is done here through reflection.
//The reflection ocurrs only in when property changes and it's cached.
public abstract class PropertyChangedBehavior<T> : IAspectBehavior<T>
where T : class, INotifyPropertyChanged
{
//always call this function to refresh the invocation list of the event handler
private readonly Func<MulticastDelegate> _refreshSubscribers;
protected PropertyChangedBehavior(T visitInstance)
{
if (visitInstance == null)
throw new ArgumentNullException("visitInstance");
Func<FieldInfo> propertyChanged = () => visitInstance.GetType()
.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
_refreshSubscribers = () => propertyChanged().GetValue(visitInstance) as MulticastDelegate;
MethodsToIntercept = Helpers.GetMethodNamesFromPropertyInfo(getters: false, setters: true, propertyInfos: typeof(T).GetProperties());
}
static void NotifyToSubscribers(MulticastDelegate multicastDelegate, T sender, string propertyName)
{
//multicastDelegate will come null if there are no subscribers to the propertychanged event
if (multicastDelegate == null)
return;
var eventArgs = new PropertyChangedEventArgs(propertyName);
var delegates = multicastDelegate.GetInvocationList();
foreach (var @delegate in delegates)
{
@delegate.Method.Invoke(@delegate.Target, new object[] { sender, eventArgs });
}
}
#region Implementation of IAspectBehavior<T>
public string[] MethodsToIntercept { get; private set; }
public virtual void PreAspect(AspectContext<T> ctx)
{
var currMethod = ctx.CallCtx.MethodName.Substring(4);
var propertyInfo = ctx.Target.GetType().GetProperty(currMethod);
ctx.Parameters = propertyInfo.GetValue(ctx.Target, null);
}
public virtual void PostAspect(AspectContext<T> ctx)
{
if (Equals(ctx.Parameters, ctx.CallCtx.Args[0])) return;
var propertyName = ctx.CallCtx.MethodName.Substring(4);
NotifyToSubscribers(_refreshSubscribers(), ctx.Target, propertyName);
}
#endregion
}
public class CustomerBehavior : PropertyChangedBehavior<ICustomer>
{
public CustomerBehavior(ICustomer visitInstance)
: base(visitInstance)
{
}
}
public interface ICustomer : INotifyPropertyChanged
{
int Id { get; set; }
string Name { get; set; }
}
[Aspect(typeof(CustomerBehavior))]
public class Customer : DynamicObject, ICustomer
{
public int Id { get; set; }
public string Name { get; set; }
#region Implementation of INotifyPropertyChanged
//doesn't need to create an invocation handler.
//the aspect will invoke the event through reflection
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public interface IUnitOfWork : IDisposable
{
int SaveChanges();
}
public interface IMyContext: IUnitOfWork
{
IDbSet<Customer> Customers { get; set; }
}
[Aspect(typeof(DbContextBehavior))]
public class MyContext : DbContext, IMyContext
{
public MyContext()
: base("MyContext")
{
Configuration.AutoDetectChangesEnabled = false;
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
Configuration.ValidateOnSaveEnabled = false;
}
public IDbSet<Customer> Customers { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment