Ninject equivalent to Autofac IIndex - c#

Is there a Ninject equivalent to Autofac IIndex. In other words I need to get a collections of all registered items that have the name of registration.
In Autofac you would do it like so
EDIT: made a change to make things more clear.
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<Add>().Keyed<ICommand>("add");
builder.RegisterType<Add>().Keyed<ICommand>("sub");
builder.RegisterType<Calculator>().As<Calculator>();
var container = builder.Build();
var calc = container.Resolve<Calculator>();
calc.Run();
}
public class Calculator
{
IIndex<string, ICommand> commands;
public Calculator(IIndex<string, ICommand> commands)
{
this.commands = commands;
}
public void Run()
{
var result = true;
do
{
var cmd = Console.ReadLine().Split(' ');
ICommand command;
if (commands.TryGetValue(cmd[0], out command))
{
result = command.Do(cmd.Skip(1).ToArray());
}
else
{
Console.WriteLine("Command not found");
}
} while (!result);
}
}
public interface ICommand
{
bool Do(string[] data);
}
public class Add : ICommand
{
public bool Do(string[] data)
{
Console.WriteLine(double.Parse(data[0]) + double.Parse(data[1]));
return false;
}
}
public class Substract : ICommand
{
public bool Do(string[] data)
{
Console.WriteLine(double.Parse(data[0]) - double.Parse(data[1]));
return false;
}
}
But I was unable to find an how to do this in Ninject.

As in the link provided, you are intending to look up a value by Key.
What you are searching for is called named binding. Bindings are Identified by a Name. How ever the Problem here is, that you need to use the common injection strategies of ninject.
Bind<IDeviceState>().To<OnlineState>().Named("Online");
Bind<IDeviceState>().To<OfflineState>().Named("Offline");
public Modem([Named("Online")] IDeviceState state){
_state = state;
}
This way, you can choose which implementation of IDeviceState will be injected by the NamedAttribute, which is added right before the injection parameter. But this doesn't fully work out for you, because you want to create IDeviceStates on the fly. Thats why you need to use the Ninject.Extensions.Factory.
This will allow you to Inject a IDeviceStateFactory.
public interface IDeviceStateFactory
{
IDeviceState GetOnline();
IDeviceState GetOffline();
}
Those Factorys underlie a very specific syntax, where Get-Methods will resolve a Named Binding as described above. So GetOnline() will match the Bind<IDeviceState>().To<OnlineState>().Named("Online") binding and return an instance of OnlineState.
Just tell the IoCContainer that IDeviceStateFactory is a factory. An implementation of that interface will then automatically be provided by Ninject's Proxy system.
Bind<IDeviceStateFactory>().ToFactory();
So finally this is how you could imlpement your class Modem using this technique
public class Modem : IHardwareDevice
{
IDeviceStateFactory _stateFactory;
IDeviceState _currentState;
public Modem(IDeviceStateFactory stateFactory)
{
_stateFactory = stateFactory;
SwitchOn();
}
void SwitchOn()
{
_currentState = _stateFactory.GetOnline();
}
}
Using a real Key
However if you really want to use your DeviceState-Enumeration like before, you could use the approach described here.
The Binding Would look pretty much like this:
const string EnumKey = "EnumKey";
Bind<IDeviceState>().To<OnlineState>()
.WithMetadata(EnumKey, DeviceState.Online);
Use this Method to resolve using DeviceState.Online Key
IResolutionRoot.Get<IDeviceState>(x => x.Get<DeviceState>(EnumKey) == DeviceState.Online);
But in my opinion, there is no reason to do such complicated thing. I recommend you to just forget about the whole IIndex<TKey, TSource> mechanics from Autofac and do it the ninject way.
Your Edit: preserving IIndex (Solution involving Factory Extension)
Well, as IIndex and no such thing do exist in Ninject, you could create your own implementation of it, Inheriting from IDictionary, or just define it much alike it and only defining it as follows:
public interface IIndex<TKey, TValue>
{
bool TryGetValue(TKey key, out TValue value);
TValue this[TKey key] { get; set; }
}
The Factory from above then gets ICommandFactory
public interface ICommandFactory
{
ICommand GetAdd();
ICommand GetSubtract();
}
then you only need to create an implementation of IIndex using the ICommandFactory to resolve the actual command instances.
public class CommandIndex : IIndex<string, ICommand>
{
private readonly ICommandFactory _factory;
public CommandIndex(ICommandFactory factory)
{
_factory = factory;
}
public bool TryGetValue(string key, out ICommand value)
{
switch (key)
{
case "Add":
value = _factory.GetAdd();
break;
case "Subtract":
value = _factory.GetSubtract();
break;
default:
value = null;
break;
}
return value != null;
}
public ICommand this[string key]
{
get
{
ICommand value;
TryGetValue(key, out value);
return value;
}
set { throw new NotSupportedException();}
}
}
But I really need to ask you: Is this whole thing necessary? Man just use the factory. It really gets much less complicated.
Another attempt of preserving IIndex without Factory Extension
Another Attempt with propably less coding is directly using the IKernel which is propably a slightly bad practice. However you can then resolve named bindings like this:
public class NamedBindingIndex<TValue> : IIndex<string, TValue>
{
private readonly IKernel _kernel;
public NamedBindingIndex(IKernel kernel)
{
_kernel = kernel;
}
public bool TryGetValue(string key, out TValue value)
{
value = _kernel.Get<TValue>(key); // eventually catch an Exception here
return value != null;
}
public TValue this[string key]
{
get
{
TValue value;
TryGetValue(key, out value);
return value;
}
set { throw new NotSupportedException(); }
}
}
Just bind it like this
Bind<IIndex<string, ICommand>>().To<NamedBindingIndex<ICommand>>();
And then your Solution should be working pretty much as same. Another plus is, that this very last attempt doesn't need no Ninject.Extensions.Factory.

For anyone else with a similar problem I was still unable to find anything similar to IIndex and this was as close as I could do.
public static class NinjectExtender
{
public const string Index = "INDEX";
public static IBindingWithOrOnSyntax<TImplementation> Keyed<T, TImplementation, TKey>(this IKernel binding, TKey key) where TImplementation : T
{
if (!binding.CanResolve<IIndex<TKey, T>>())
{
binding.Bind<IIndex<TKey, T>>().ToMethod(context => new Index<TKey,T>(context));
}
return binding.Bind<T>().To<TImplementation>().WithMetadata(Index, key);
}
}
public class Index<T, M> : IIndex<T, M>
{
private readonly IContext context;
public Index(IContext context)
{
this.context = context;
}
public bool TryGetValue(T key, out M value)
{
try
{
value = this[key];
return true;
}
catch (KeyNotFoundException)
{
value = default(M);
return false;
}
}
public M this[T key]
{
get
{
try
{
var service = context.Kernel.Get<M>(binding => binding.Get<T>(NinjectExtender.Index).Equals(key));
if (!service.Equals(default(M)))
{
return service;
}
else
{
throw new KeyNotFoundException();
}
}
catch (Exception)
{
throw new KeyNotFoundException();
}
}
}
}
I am a bit unsure in if the context is handled in the same way as Autofac but I am not having issues as of now.
kernel.Keyed<ICommand, Add, string>("add");

Related

WCF Custom OperationConext Lifetimemanager for EF DbContext

container.RegisterType<IDataContextFactory<MyDataContext>, DefaultDataContextFactory<MyDataContext>>(new PerRequestLifetimeManager());
Created a PerRequestLifetimeManager using OperationContext but it does not seem call setValue function at all, it always trys to go to GetValue() function which always retruns null since nothing has been set.
My goal is to create a lifetimeManager for dbconetxt that will give me a new dbContext per method call. transient is not an option since it won;t work for join query.
public class WcfOperationContext : IExtension<OperationContext>
{
private readonly IDictionary<string, object> items;
private WcfOperationContext()
{
items = new Dictionary<string, object>();
}
public IDictionary<string, object> Items
{
get { return items; }
}
public static WcfOperationContext Current
{
get
{
WcfOperationContext context = OperationContext.Current.Extensions.Find<WcfOperationContext>();
if (context == null)
{
context = new WcfOperationContext();
OperationContext.Current.Extensions.Add(context);
}
return context;
}
}
public void Attach(OperationContext owner) { }
public void Detach(OperationContext owner) { }
}
public class PerRequestLifetimeManager : LifetimeManager
{
private string key;
public PerRequestLifetimeManager()
{
key = Guid.NewGuid().ToString();
}
public override object GetValue()
{
if (WcfOperationContext.Current == null)
{
return null;
}
else
{
return WcfOperationContext.Current.Items[key];
}
}
public override void RemoveValue()
{
if (WcfOperationContext.Current != null)
{
WcfOperationContext.Current.Items.Remove(key);
}
}
public override void SetValue(object newValue)
{
if (WcfOperationContext.Current != null)
{
WcfOperationContext.Current.Items.Add(key, newValue);
}
}
}
My solution for this was to use this nuget package: UnityWCF
The Service should be instantiated by Unity and new instance per call.
For this use this settings on the service:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ...
Inject DbContext where you need. And register in Unity like this:
container.RegisterType<DbContext, YourDbContext>(new HierarchicalLifetimeManager(), ...);

Call code once when controller is first accessed [duplicate]

I have read lots of information about page caching and partial page caching in a MVC application. However, I would like to know how you would cache data.
In my scenario I will be using LINQ to Entities (entity framework). On the first call to GetNames (or whatever the method is) I want to grab the data from the database. I want to save the results in cache and on the second call to use the cached version if it exists.
Can anyone show an example of how this would work, where this should be implemented (model?) and if it would work.
I have seen this done in traditional ASP.NET apps , typically for very static data.
Here's a nice and simple cache helper class/service I use:
using System.Runtime.Caching;
public class InMemoryCache: ICacheService
{
public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
{
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
}
return item;
}
}
interface ICacheService
{
T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}
Usage:
cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));
Cache provider will check if there's anything by the name of "cache id" in the cache, and if there's not, it will call a delegate method to fetch data and store it in cache.
Example:
var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
Reference the System.Web dll in your model and use System.Web.Caching.Cache
public string[] GetNames()
{
string[] names = Cache["names"] as string[];
if(names == null) //not in cache
{
names = DB.GetNames();
Cache["names"] = names;
}
return names;
}
A bit simplified but I guess that would work. This is not MVC specific and I have always used this method for caching data.
I'm referring to TT's post and suggest the following approach:
Reference the System.Web dll in your model and use System.Web.Caching.Cache
public string[] GetNames()
{
var noms = Cache["names"];
if(noms == null)
{
noms = DB.GetNames();
Cache["names"] = noms;
}
return ((string[])noms);
}
You should not return a value re-read from the cache, since you'll never know if at that specific moment it is still in the cache. Even if you inserted it in the statement before, it might already be gone or has never been added to the cache - you just don't know.
So you add the data read from the database and return it directly, not re-reading from the cache.
For .NET 4.5+ framework
add reference: System.Runtime.Caching
add using statement:
using System.Runtime.Caching;
public string[] GetNames()
{
var noms = System.Runtime.Caching.MemoryCache.Default["names"];
if(noms == null)
{
noms = DB.GetNames();
System.Runtime.Caching.MemoryCache.Default["names"] = noms;
}
return ((string[])noms);
}
In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications.
More info:
https://msdn.microsoft.com/en-us/library/dd997357(v=vs.110).aspx
https://learn.microsoft.com/en-us/dotnet/framework/performance/caching-in-net-framework-applications
Steve Smith did two great blog posts which demonstrate how to use his CachedRepository pattern in ASP.NET MVC. It uses the repository pattern effectively and allows you to get caching without having to change your existing code.
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
In these two posts he shows you how to set up this pattern and also explains why it is useful. By using this pattern you get caching without your existing code seeing any of the caching logic. Essentially you use the cached repository as if it were any other repository.
I have used it in this way and it works for me.
https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx
parameters info for system.web.caching.cache.add.
public string GetInfo()
{
string name = string.Empty;
if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
{
name = GetNameMethod();
System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
}
else
{
name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
}
return name;
}
AppFabric Caching is distributed and an in-memory caching technic that stores data in key-value pairs using physical memory across multiple servers. AppFabric provides performance and scalability improvements for .NET Framework applications. Concepts and Architecture
Extending #Hrvoje Hudo's answer...
Code:
using System;
using System.Runtime.Caching;
public class InMemoryCache : ICacheService
{
public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
{
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
{
string cacheKey = string.Format(cacheKeyFormat, id);
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback(id);
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
}
interface ICacheService
{
TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}
Examples
Single item caching (when each item is cached based on its ID because caching the entire catalog for the item type would be too intensive).
Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
Caching all of something
IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
Why TId
The second helper is especially nice because most data keys are not composite. Additional methods could be added if you use composite keys often. In this way you avoid doing all sorts of string concatenation or string.Formats to get the key to pass to the cache helper. It also makes passing the data access method easier because you don't have to pass the ID into the wrapper method... the whole thing becomes very terse and consistant for the majority of use cases.
Here's an improvement to Hrvoje Hudo's answer. This implementation has a couple of key improvements:
Cache keys are created automatically based on the function to update data and the object passed in that specifies dependencies
Pass in time span for any cache duration
Uses a lock for thread safety
Note that this has a dependency on Newtonsoft.Json to serialize the dependsOn object, but that can be easily swapped out for any other serialization method.
ICache.cs
public interface ICache
{
T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}
InMemoryCache.cs
using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;
public class InMemoryCache : ICache
{
private static readonly object CacheLockObject = new object();
public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
{
string cacheKey = GetCacheKey(getItemCallback, dependsOn);
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
lock (CacheLockObject)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
}
}
return item;
}
private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
{
var serializedDependants = JsonConvert.SerializeObject(dependsOn);
var methodType = itemCallback.GetType();
return methodType.FullName + serializedDependants;
}
}
Usage:
var order = _cache.GetOrSet(
() => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
, new { id = orderId }
, new TimeSpan(0, 10, 0)
);
public sealed class CacheManager
{
private static volatile CacheManager instance;
private static object syncRoot = new Object();
private ObjectCache cache = null;
private CacheItemPolicy defaultCacheItemPolicy = null;
private CacheEntryRemovedCallback callback = null;
private bool allowCache = true;
private CacheManager()
{
cache = MemoryCache.Default;
callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);
defaultCacheItemPolicy = new CacheItemPolicy();
defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
defaultCacheItemPolicy.RemovedCallback = callback;
allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
}
public static CacheManager Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new CacheManager();
}
}
}
return instance;
}
}
public IEnumerable GetCache(String Key)
{
if (Key == null || !allowCache)
{
return null;
}
try
{
String Key_ = Key;
if (cache.Contains(Key_))
{
return (IEnumerable)cache.Get(Key_);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}
public void ClearCache(string key)
{
AddCache(key, null);
}
public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
{
if (!allowCache) return true;
try
{
if (Key == null)
{
return false;
}
if (cacheItemPolicy == null)
{
cacheItemPolicy = defaultCacheItemPolicy;
}
String Key_ = Key;
lock (Key_)
{
return cache.Add(Key_, data, cacheItemPolicy);
}
}
catch (Exception)
{
return false;
}
}
private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
{
String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
LogManager.Instance.Info(strLog);
}
}
I use two classes. First one the cache core object:
public class Cacher<TValue>
where TValue : class
{
#region Properties
private Func<TValue> _init;
public string Key { get; private set; }
public TValue Value
{
get
{
var item = HttpRuntime.Cache.Get(Key) as TValue;
if (item == null)
{
item = _init();
HttpContext.Current.Cache.Insert(Key, item);
}
return item;
}
}
#endregion
#region Constructor
public Cacher(string key, Func<TValue> init)
{
Key = key;
_init = init;
}
#endregion
#region Methods
public void Refresh()
{
HttpRuntime.Cache.Remove(Key);
}
#endregion
}
Second one is list of cache objects:
public static class Caches
{
static Caches()
{
Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
{
using (var context = new WordsContext())
{
return context.Languages.ToList();
}
});
}
public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}
I will say implementing Singleton on this persisting data issue can be a solution for this matter in case you find previous solutions much complicated
public class GPDataDictionary
{
private Dictionary<string, object> configDictionary = new Dictionary<string, object>();
/// <summary>
/// Configuration values dictionary
/// </summary>
public Dictionary<string, object> ConfigDictionary
{
get { return configDictionary; }
}
private static GPDataDictionary instance;
public static GPDataDictionary Instance
{
get
{
if (instance == null)
{
instance = new GPDataDictionary();
}
return instance;
}
}
// private constructor
private GPDataDictionary() { }
} // singleton
HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
You can also try and use the caching built into ASP MVC:
Add the following attribute to the controller method you'd like to cache:
[OutputCache(Duration=10)]
In this case the ActionResult of this will be cached for 10 seconds.
More on this here

Should I inject ServiceStack's ICacheManager?

I'm looking to implement a caching tier in our application and accidentally came across ServiceStack's ICacheManager.
ICacheManager.Resolve looks as though it's exactly what I'm after (try and get, if it's not in the cache then call the function to get and store it). All documentation I can find however is about using ICacheClient.
How I can wire up ICacheManager using AutoFac? I assume I need to wire up a client e.g.:
_builder.Register(c => new MemoryCacheClient())
.As<ICacheClient>();
But then I'm not sure what ICacheManager should resolve to.
Is this a good idea or am I abusing ServiceStack?
I've added a custom cache manager for the time being but it feels wrong for some reason:
public class CacheManager : ICacheManager
{
public CacheManager(ICacheClient cacheClient)
{
CacheClient = cacheClient;
}
public void Clear(IEnumerable<string> cacheKeys)
{
Clear(cacheKeys.ToArray());
}
public void Clear(params string[] cacheKeys)
{
CacheClient.ClearCaches(cacheKeys.ToArray());
}
public ICacheClient CacheClient { get; private set; }
public T Resolve<T>(string cacheKey, Func<T> createCacheFn) where T : class
{
return Resolve(cacheKey, new TimeSpan(0, 15, 0), createCacheFn);
}
public T Resolve<T>(string cacheKey, TimeSpan expireIn, Func<T> createCacheFn) where T : class
{
var cacheResult = CacheClient.Get<T>(cacheKey);
if (cacheResult != null)
return cacheResult;
var item = createCacheFn();
CacheClient.Set(cacheKey, item, expireIn);
return item;
}
}

Can you make a method part of the default setter action in a property variable?

If you have multiple properties that implement the same method in the setter, is there a way to make it part of the default setter?
If I have multiple properties that call a Filter() when they are set, is there a way to push it into a "base setter" so that I don't have to have the Filter() call in every setter?
private string _MyVal1;
public string MyVal1 {
get {
return _MyVal1;
}
set {
_MyVal1 = value;
Filter();
OnPropertyChanged("MyVal1");
}
}
private string _MyVal2;
public string MyVal2 {
get {
return _MyVal2;
}
set {
_MyVal2 = value;
Filter();
OnPropertyChanged("MyVal2");
}
}
private string _MyValN;
public string MyValN {
get {
return _MyValN;
}
set {
_MyValN = value;
Filter();
OnPropertyChanged("MyValN");
}
}
So it turns into this:
private string _MyValN;
public string MyValN {
get {
return _MyValN;
}
set : FilterSetter {
_MyValN = value;
OnPropertyChanged("MyValN");
}
}
A different way of doing this is to use interception like that provided by the Unity framework. With interception your class implements an interface and you would tell the framework that everytime a method is called on classes implementing that interface, run these interceptors. Your interceptors code can look to see if the method being called is prefixed with set_. Interceptor code executes once on the way to the function and once on the way back. On the way back, you can then have the interceptor call the filter method (assuming it is defined on the interface of course).
Concrete example:
Get Prerequisite Library
Use NuGet to add Unity and Unity extensions to your project
Define your interface to be intercepted: SomeObject.cs
using System;
namespace InterceptSetter
{
interface ISomeObject
{
string SomeProperty { get; set; }
void Filter();
}
public class SomeObject : ISomeObject
{
public string SomeProperty { get; set; }
public void Filter()
{
Console.Out.WriteLine("Filter Called");
}
}
}
Define your Interception Behavior: SetterCallsFilterMethodBehavior.cs
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
using System.Collections.Generic;
using System.Linq;
namespace InterceptSetter
{
/// <summary>
/// See http://msdn.microsoft.com/en-us/library/ff660871(v=pandp.20).aspx
/// See http://msdn.microsoft.com/en-us/library/ff647107.aspx
/// </summary>
class SetterCallsFilterMethodBehavior : IInterceptionBehavior
{
public IEnumerable<Type> GetRequiredInterfaces()
{
// we dont need anything
return new[] { typeof(ISomeObject) };
}
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{ // Do not intercept non-setter methods
if (!input.MethodBase.Name.StartsWith("set_"))
return getNext()(input, getNext);
IMethodReturn msg = getNext()(input, getNext);
// post processing. this is where we call filter
if (input.Target is ISomeObject)
{
(input.Target as ISomeObject).Filter();
}
return msg;
}
/// <summary>
/// We always execute
/// </summary>
public bool WillExecute
{
get { return true; }
}
}
}
Write a test console program: Program.cs
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using System;
namespace InterceptSetter
{
class Program
{
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();
container.AddNewExtension<Interception>();
container.RegisterType<ISomeObject, SomeObject>(
new Interceptor<TransparentProxyInterceptor>(),
new InterceptionBehavior<SetterCallsFilterMethodBehavior>());
// we must get our instance from unity for interception to occur
ISomeObject myObject = container.Resolve<ISomeObject>();
myObject.SomeProperty = "Hello Setter";
Console.ReadLine();
}
}
}
Running this you will see that the interceptor does in fact call the filter method (which prints to the console).
Unity is not the only dependency injection / interception framework out there (google PostSharp). Unity is the one i am familiar with so thats what this example uses.
Sources / See Also:
http://msdn.microsoft.com/en-us/library/ff660871(v=pandp.20).aspx - Good diagram depicting the flow of interception
http://msdn.microsoft.com/en-us/library/ff647107.aspx - overkill of detail showing different interception techniques
You can create generic setter method and call that from each property setter:
private void Set<T>(ref T field, T value, string propertyName)
{
field = value;
Filter();
OnPropertyChanged(propertyName);
}
Then your properties look like:
public string SomeProperty
{
get { return this.someField; }
set
{
Set(ref this.someField, value, "SomeProperty");
}
}

Design a mutable class that after it's consumed becomes immutable

Suppose that the scenario doesn't allow to implement an immutable type. Following that assumption, I'd like opinions / examples on how to properly design a type that after it's consumed, becomes immutable.
public class ObjectAConfig {
private int _valueB;
private string _valueA;
internal bool Consumed { get; set; }
public int ValueB {
get { return _valueB; }
set
{
if (Consumed) throw new InvalidOperationException();
_valueB = value;
}
}
public string ValueA {
get { return _valueA; }
set
{
if (Consumed) throw new InvalidOperationException();
_valueA = value;
}
}
}
When ObjectA consumes ObjectAConfig:
public ObjectA {
public ObjectA(ObjectAConfig config) {
_config = config;
_config.Consumed = true;
}
}
I'm not satisfied that this simply works, I'd like to know if there's a better pattern (excluded, as said, making ObjectAConfig immutable by design from begin).
For example:
can make sense define a monad like Once<T> that allow the wrapped value to be initialized only once?
can make sense define a type that returns the type itself changing a private field?
What you are implementing sometimes goes under the name "popsicle immutability" - i.e. you can freeze it. Your current approach will work - indeed I use that pattern myself in numerous places.
You can probably reduce some duplication via something like:
private void SetField<T>(ref T field, T value) {
if (Consumed) throw new InvalidOperationException();
field = value;
}
public int ValueB {
get { return _valueB; }
set { SetField(ref _valueB, value); }
}
public string ValueA {
get { return _valueA; }
set { SetField(ref _valueA, value); }
}
There is another related approach, though: a builder. For example, taking your existing class:
public interface IConfig
{
string ValueA { get; }
int ValueB { get; }
}
public class ObjectAConfig : IConfig
{
private class ImmutableConfig : IConfig {
private readonly string valueA;
private readonly int valueB;
public ImmutableConfig(string valueA, int valueB)
{
this.valueA = valueA;
this.valueB = valueB;
}
}
public IConfig Build()
{
return new ImmutableConfig(ValueA, ValueB);
}
... snip: implementation of ObjectAConfig
}
Here there is a truly immutable implementation of IConfig, and your original implementation. If you want the frozen version, call Build().

Categories

Resources