How can I make method signature caching? - c#

I'm building an app in .NET and C#, and I'd like to cache some of the results by using attributes/annotations instead of explicit code in the method.
I'd like a method signature that looks a bit like this:
[Cache, timeToLive=60]
String getName(string id, string location)
It should make a hash based on the inputs, and use that as the key for the result.
Naturally, there'd be some config file telling it how to actually put in memcached, local dictionary or something.
Do you know of such a framework?
I'd even be interested in one for Java as well

With CacheHandler in Microsoft Enterprise Library you can easily achieve this.
For instance:
[CacheHandler(0, 30, 0)]
public Object GetData(Object input)
{
}
would make all calls to that method cached for 30 minutes. All invocations gets a unique cache-key based on the input data and method name so if you call the method twice with different input it doesn't get cached but if you call it >1 times within the timout interval with the same input then the method only gets executed once.
I've added some extra features to Microsoft's code:
My modified version looks like this:
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.Unity.InterceptionExtension;
namespace Middleware.Cache
{
/// <summary>
/// An <see cref="ICallHandler"/> that implements caching of the return values of
/// methods. This handler stores the return value in the ASP.NET cache or the Items object of the current request.
/// </summary>
[ConfigurationElementType(typeof (CacheHandler)), Synchronization]
public class CacheHandler : ICallHandler
{
/// <summary>
/// The default expiration time for the cached entries: 5 minutes
/// </summary>
public static readonly TimeSpan DefaultExpirationTime = new TimeSpan(0, 5, 0);
private readonly object cachedData;
private readonly DefaultCacheKeyGenerator keyGenerator;
private readonly bool storeOnlyForThisRequest = true;
private TimeSpan expirationTime;
private GetNextHandlerDelegate getNext;
private IMethodInvocation input;
public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest)
{
keyGenerator = new DefaultCacheKeyGenerator();
this.expirationTime = expirationTime;
this.storeOnlyForThisRequest = storeOnlyForThisRequest;
}
/// <summary>
/// This constructor is used when we wrap cached data in a CacheHandler so that
/// we can reload the object after it has been removed from the cache.
/// </summary>
/// <param name="expirationTime"></param>
/// <param name="storeOnlyForThisRequest"></param>
/// <param name="input"></param>
/// <param name="getNext"></param>
/// <param name="cachedData"></param>
public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest,
IMethodInvocation input, GetNextHandlerDelegate getNext,
object cachedData)
: this(expirationTime, storeOnlyForThisRequest)
{
this.input = input;
this.getNext = getNext;
this.cachedData = cachedData;
}
/// <summary>
/// Gets or sets the expiration time for cache data.
/// </summary>
/// <value>The expiration time.</value>
public TimeSpan ExpirationTime
{
get { return expirationTime; }
set { expirationTime = value; }
}
#region ICallHandler Members
/// <summary>
/// Implements the caching behavior of this handler.
/// </summary>
/// <param name="input"><see cref="IMethodInvocation"/> object describing the current call.</param>
/// <param name="getNext">delegate used to get the next handler in the current pipeline.</param>
/// <returns>Return value from target method, or cached result if previous inputs have been seen.</returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
lock (input.MethodBase)
{
this.input = input;
this.getNext = getNext;
return loadUsingCache();
}
}
public int Order
{
get { return 0; }
set { }
}
#endregion
private IMethodReturn loadUsingCache()
{
//We need to synchronize calls to the CacheHandler on method level
//to prevent duplicate calls to methods that could be cached.
lock (input.MethodBase)
{
if (TargetMethodReturnsVoid(input) || HttpContext.Current == null)
{
return getNext()(input, getNext);
}
var inputs = new object[input.Inputs.Count];
for (int i = 0; i < inputs.Length; ++i)
{
inputs[i] = input.Inputs[i];
}
string cacheKey = keyGenerator.CreateCacheKey(input.MethodBase, inputs);
object cachedResult = getCachedResult(cacheKey);
if (cachedResult == null)
{
var stopWatch = Stopwatch.StartNew();
var realReturn = getNext()(input, getNext);
stopWatch.Stop();
if (realReturn.Exception == null && realReturn.ReturnValue != null)
{
AddToCache(cacheKey, realReturn.ReturnValue);
}
return realReturn;
}
var cachedReturn = input.CreateMethodReturn(cachedResult, input.Arguments);
return cachedReturn;
}
}
private object getCachedResult(string cacheKey)
{
//When the method uses input that is not serializable
//we cannot create a cache key and can therefore not
//cache the data.
if (cacheKey == null)
{
return null;
}
object cachedValue = !storeOnlyForThisRequest ? HttpRuntime.Cache.Get(cacheKey) : HttpContext.Current.Items[cacheKey];
var cachedValueCast = cachedValue as CacheHandler;
if (cachedValueCast != null)
{
//This is an object that is reloaded when it is being removed.
//It is therefore wrapped in a CacheHandler-object and we must
//unwrap it before returning it.
return cachedValueCast.cachedData;
}
return cachedValue;
}
private static bool TargetMethodReturnsVoid(IMethodInvocation input)
{
var targetMethod = input.MethodBase as MethodInfo;
return targetMethod != null && targetMethod.ReturnType == typeof (void);
}
private void AddToCache(string key, object valueToCache)
{
if (key == null)
{
//When the method uses input that is not serializable
//we cannot create a cache key and can therefore not
//cache the data.
return;
}
if (!storeOnlyForThisRequest)
{
HttpRuntime.Cache.Insert(
key,
valueToCache,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
expirationTime,
CacheItemPriority.Normal, null);
}
else
{
HttpContext.Current.Items[key] = valueToCache;
}
}
}
/// <summary>
/// This interface describes classes that can be used to generate cache key strings
/// for the <see cref="CacheHandler"/>.
/// </summary>
public interface ICacheKeyGenerator
{
/// <summary>
/// Creates a cache key for the given method and set of input arguments.
/// </summary>
/// <param name="method">Method being called.</param>
/// <param name="inputs">Input arguments.</param>
/// <returns>A (hopefully) unique string to be used as a cache key.</returns>
string CreateCacheKey(MethodBase method, object[] inputs);
}
/// <summary>
/// The default <see cref="ICacheKeyGenerator"/> used by the <see cref="CacheHandler"/>.
/// </summary>
public class DefaultCacheKeyGenerator : ICacheKeyGenerator
{
private readonly LosFormatter serializer = new LosFormatter(false, "");
#region ICacheKeyGenerator Members
/// <summary>
/// Create a cache key for the given method and set of input arguments.
/// </summary>
/// <param name="method">Method being called.</param>
/// <param name="inputs">Input arguments.</param>
/// <returns>A (hopefully) unique string to be used as a cache key.</returns>
public string CreateCacheKey(MethodBase method, params object[] inputs)
{
try
{
var sb = new StringBuilder();
if (method.DeclaringType != null)
{
sb.Append(method.DeclaringType.FullName);
}
sb.Append(':');
sb.Append(method.Name);
TextWriter writer = new StringWriter(sb);
if (inputs != null)
{
foreach (var input in inputs)
{
sb.Append(':');
if (input != null)
{
//Diffrerent instances of DateTime which represents the same value
//sometimes serialize differently due to some internal variables which are different.
//We therefore serialize it using Ticks instead. instead.
var inputDateTime = input as DateTime?;
if (inputDateTime.HasValue)
{
sb.Append(inputDateTime.Value.Ticks);
}
else
{
//Serialize the input and write it to the key StringBuilder.
serializer.Serialize(writer, input);
}
}
}
}
return sb.ToString();
}
catch
{
//Something went wrong when generating the key (probably an input-value was not serializble.
//Return a null key.
return null;
}
}
#endregion
}
}
Microsoft deserves most credit for this code. We've only added stuff like caching at request level instead of across requests (more useful than you might think) and fixed some bugs (e.g. equal DateTime-objects serializing to different values).

To do exactly what you are describing, i.e. writing
public class MyClass {
[Cache, timeToLive=60]
string getName(string id, string location){
return ExpensiveCall(id, location);
}
}
// ...
MyClass c = new MyClass();
string name = c.getName("id", "location");
string name_again = c.getName("id", "location");
and having only one invocation of the expensive call and without needing to wrap the class with some other code (f.x. CacheHandler<MyClass> c = new CacheHandler<MyClass>(new MyClass());) you need to look into an Aspect Oriented Programming framework. Those usually work by rewriting the byte-code, so you need to add another step to your compilation process - but you gain a lot of power in the process. There are many AOP-frameworks, but PostSharp for .NET and AspectJ are among the most popular. You can easily Google how to use those to add the caching-aspect you want.

Related

IMemoryCache - Singleton or Dependency injection

public class CacheController
{
public IMemoryCache _memoryCache {get; set;}
public string getCacheMethodOne(string token)
{
string cacheValue = null;
string cacheKey = null;
if (!_memoryCache.TryGetValue<string>("123456", out cacheValue))
{
cacheValue = token;
cacheKey = "123456";
var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(2));
_memoryCache.Set<string>("123456", cacheValue, cacheEntryOptions);
}
return cacheKey;
}
}
Problem with this line of code:
string otp = new
CacheController().getCacheMethodOne(ClientJsonOtp.ToString());
throws exception.
Object reference not set to an instance of an object.
Should i create new instances of IMemorycahce.
If i do so, will it affect the cache. as it may lose the previous cache instance.
try
{
var finalResult = result.Content.ReadAsStringAsync().Result;
var ClientJsonOtp = JsonConvert.DeserializeObject(finalResult);
string otp = new CacheController().getCacheMethodOne(ClientJsonOtp.ToString());
return Json(ClientJsonOtp, JsonRequestBehavior.AllowGet);
}
You need to create one, at least once. Otherwise it will always be null.
You can do that when you call the empty constructor:
public CacheController()
{
this._memoryCache = new // whatever memory cache you choose;
}
You can even better inject it somewhere using dependency injection. The place depends on application type.
But best of all, try to have only once cache. Each time you create one you lose the previous, so you will either try the singleton pattern, or inject using a single instance configuration and let the DI container handle the rest.
An example for the singleton implementation: here
You can access by using:
Cache.Instance.Read(//what)
Here is the cache implementation
using System;
using System.Configuration;
using System.Runtime.Caching;
namespace Client.Project.HelperClasses
{
/// <summary>
/// Thread Safe Singleton Cache Class
/// </summary>
public sealed class Cache
{
private static volatile Cache instance; // Locks var until assignment is complete for double safety
private static MemoryCache memoryCache;
private static object syncRoot = new Object();
private static string settingMemoryCacheName;
private static double settingCacheExpirationTimeInMinutes;
private Cache() { }
/// <summary>
/// Singleton Cache Instance
/// </summary>
public static Cache Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
InitializeInstance();
}
}
}
return instance;
}
}
private static void InitializeInstance()
{
var appSettings = ConfigurationManager.AppSettings;
settingMemoryCacheName = appSettings["MemoryCacheName"];
if (settingMemoryCacheName == null)
throw new Exception("Please enter a name for the cache in app.config, under 'MemoryCacheName'");
if (! Double.TryParse(appSettings["CacheExpirationTimeInMinutes"], out settingCacheExpirationTimeInMinutes))
throw new Exception("Please enter how many minutes the cache should be kept in app.config, under 'CacheExpirationTimeInMinutes'");
instance = new Cache();
memoryCache = new MemoryCache(settingMemoryCacheName);
}
/// <summary>
/// Writes Key Value Pair to Cache
/// </summary>
/// <param name="Key">Key to associate Value with in Cache</param>
/// <param name="Value">Value to be stored in Cache associated with Key</param>
public void Write(string Key, object Value)
{
memoryCache.Add(Key, Value, DateTimeOffset.Now.AddMinutes(settingCacheExpirationTimeInMinutes));
}
/// <summary>
/// Returns Value stored in Cache
/// </summary>
/// <param name="Key"></param>
/// <returns>Value stored in cache</returns>
public object Read(string Key)
{
return memoryCache.Get(Key);
}
/// <summary>
/// Returns Value stored in Cache, null if non existent
/// </summary>
/// <param name="Key"></param>
/// <returns>Value stored in cache</returns>
public object TryRead(string Key)
{
try
{
return memoryCache.Get(Key);
}
catch (Exception)
{
return null;
}
}
}
}

Use MiniProfiler to capture slow requests

I want to use MiniProfiler to call a function once a set time limit has gone by. This is how MiniProfiler is set up. After that I've included the profiling script which we use to profile whatever needs profiling. My problem is to create some sort of middleware that can intercept this "MiniProfiler.Current.Step" call when the timing is greater than 1000ms.
app.UseMiniProfiler(new MiniProfilerOptions
{
ResultsAuthorize = x => false,
ResultsListAuthorize = x => false,
Storage = new SqlServerStorage(Configuration.GetConnectionString("MiniProfiler"))
});
MiniProfilerEF6.Initialize();
app.Use(async (context, next) =>
{
MiniProfiler.Current.Name = context.Request.GetDisplayUrl();
await next.Invoke();
});
/// <summary>
/// The main profiler, useage:
/// using(this.Profile("more context"))
/// {
/// Do things that needs profiling, and you may nest it.
/// }
/// </summary>
/// <param name="profiled">The object that is profiled</param>
/// <param name="subSectionName">More context for the output result</param>
/// <param name="methodName">Possible override for the method name called</param>
/// <param name="profiledTypeName">Possible override for the profiled type</param>
/// <returns>An IDisposable, to help with scoping</returns>
public static IDisposable Profile(this object profiled,
string subSectionName = null,
[CallerMemberName] string methodName = "",
string profiledTypeName = null)
{
if (profiled == null)
throw new ArgumentNullException(nameof(profiled));
var profiledType = profiledTypeName ?? profiled.GetType().Name;
return Profile(methodName, profiledType, subSectionName);
}
public static IDisposable Profile(string methodName,
string profiledTypeName,
string subSectionName = null)
{
var name = subSectionName != null
? $"{profiledTypeName}.{methodName}:{subSectionName}"
: $"{profiledTypeName}.{methodName}";
return MiniProfiler.Current?.Step(name);
}

Is there a newer way to accomplish what this article recommends?

I like the idea of this Agent Service that allows me to add jobs without affecting existing code, but the article is pretty old. Has something come along since 2005 that would be a better way of accomplishing the same thing?
I am limited to .Net, but I can use any version of the framework.
http://visualstudiomagazine.com/articles/2005/05/01/create-a-net-agent.aspx
Edit: Here is a summary of what I'm trying to do
Have a .Net service that can read a configuration file to dynamically load DLLs to perform tasks.
The service has a dynamic object loader that maps XML to classes in the DLLs to perform the tasks.
An example of the xml file and the class it maps is below. Basically, the service can read the Job from the xml, instantiate an instance of the CustomerAttendeeCreateNotificationWorker and call the Run() method. As a developer, I can make any number of these classes and implement whatever I want in the Run() method. Also note that the entry for "CustomerAttendanceNotificationTemplateName" gets assigned to a property with the same name on the instantiated object.
<?xml version="1.0" encoding="utf-8"?>
<Manager type="Task.RemotingManager, TaskClasses">
<Jobs type="Task.TaskJob, TaskBase" Name="CustomerAttendeeCreateNotificationWorker Worker">
<Worker type="TaskWorkers.CustomerAttendeeCreateNotificationWorker, TaskWorkers"
Description="Inserts records into NotificationQueue for CustomerAttendees"
CustomerAttendanceNotificationTemplateName ="CustomerAttendanceNotificationTemplateName"
/>
<Schedulers type="Task.FixedIntervalScheduler, TaskClasses"
DaysOfWeek="Everyday"
Frequency="00:05:00"
StartTime="00:00:00"
EndTime="23:55:00"
/>
</Jobs>
<Jobs type="Task.TaskJob, TaskBase" Name="SendNotificationWorker Worker">
<Worker type="TaskWorkers.SendNotificationWorker, TaskWorkers"
Description="Sends messages from NotificationQueue"
/>
<Schedulers type="Task.FixedIntervalScheduler, TaskClasses"
DaysOfWeek="Everyday"
Frequency="00:05:00"
StartTime="00:00:00"
EndTime="23:55:00"
/>
</Jobs>/>
</Manager>
/// <summary>
/// The customer attendee create notification worker.
/// </summary>
[Serializable]
public class CustomerAttendeeCreateNotificationWorker : TaskWorker
{
#region Constants
/// <summary>
/// The stat e_ critical.
/// </summary>
private const int StateCritical = 1;
/// <summary>
/// The stat e_ ok.
/// </summary>
private const int StateOk = 0;
#endregion
#region Fields
/// <summary>
/// The customer notification setting name.
/// </summary>
private string customerAttendanceNotificationTemplateName;
#endregion
#region Public Properties
/// <summary>
/// The customer notification setting name.
/// </summary>
public string CustomerAttendanceNotificationTemplateName
{
get
{
return this.customerAttendanceNotificationTemplateName;
}
set
{
this.customerAttendanceNotificationTemplateName = value;
}
}
/// <summary>
/// Gets or sets the logger.
/// </summary>
public ILogger Logger { get; set; }
#endregion
#region Public Methods and Operators
/// <summary>
/// The run.
/// </summary>
/// <returns>
/// The <see cref="TaskResult" />.
/// </returns>
public override TaskResult Run()
{
StringBuilder errorStringBuilder = new StringBuilder();
string errorLineTemplate = "Time: {0} Database: {1} Error Message: {2}" + Environment.NewLine;
bool isError = false;
IUnitOfWork configUnitOfWork = new GenericUnitOfWork<DCCShowConfigContext>();
TradeshowService tradeshowService = new TradeshowService(configUnitOfWork);
List<Tradeshow> tradeshows = tradeshowService.GetAll().Where(t => t.Status == "OPEN").ToList();
string connectionStringTemplate = ConfigurationManager.ConnectionStrings["DCCTradeshow"].ConnectionString;
int customerNotificationSettingGroupId = Convert.ToInt32(ConfigurationManager.AppSettings["NotificationSettingGroupId"]);
foreach (Tradeshow tradeshow in tradeshows)
{
try
{
string connectionString = string.Format(connectionStringTemplate, tradeshow.DbName);
IUnitOfWork unitOfWork = new TradeshowUnitOfWork(connectionString);
NotificationService notificationService = new NotificationService(unitOfWork);
notificationService.InsertCustomerAttendanceNotifications(tradeshow.TradeshowId, customerNotificationSettingGroupId, this.customerAttendanceNotificationTemplateName);
}
catch (Exception exception)
{
isError = true;
errorStringBuilder.AppendLine(string.Format(errorLineTemplate, DateTime.Now, tradeshow.DbName, exception.Message));
}
}
TaskResult result;
if (isError)
{
result = new TaskResult(StateOk, TaskResultStatus.Exception, "Errors Occured", errorStringBuilder.ToString());
}
else
{
result = new TaskResult(StateOk, TaskResultStatus.Ok,"All Good", "All Good");
}
return result;
}
#endregion
}`

How to inject property dependencies on a .net attribute?

I'm trying to apply some behavior using a home grown type of "aspect", really a .net Attribute. I have a base class (BankingServiceBase) that reflects on itself at startup to see what "aspects" are applied to it. It then can execute custom behavior before or after operations. I'm using Autofac as my IOC container. I'm trying to apply the PropertiesAutowired method to the aspect's registration. In the below sample code I want Autofac to inject an ILog instance to my aspect/attribute. It isn't doing that however. My guess is that when I call GetCustomAttributes, it's creating a new instance instead of getting the registered instance from Autofac. Thoughts? Here is some usable sample code to display the problem:
internal class Program
{
private static void Main()
{
var builder = new ContainerBuilder();
builder
.RegisterType<ConsoleLog>()
.As<ILog>();
builder
.RegisterType<BankingService>()
.As<IBankingService>();
builder
.RegisterType<LogTransfer>()
.As<LogTransfer>()
.PropertiesAutowired();
var container = builder.Build();
var bankingService = container.Resolve<IBankingService>();
bankingService.Transfer("ACT 1", "ACT 2", 180);
System.Console.ReadKey();
}
public interface IBankingService
{
void Transfer(string from, string to, decimal amount);
}
public interface ILog
{
void LogMessage(string message);
}
public class ConsoleLog : ILog
{
public void LogMessage(string message)
{
System.Console.WriteLine(message);
}
}
[AttributeUsage(AttributeTargets.Class)]
public abstract class BankingServiceAspect : Attribute
{
public virtual void PreTransfer(string from, string to, decimal amount)
{
}
public virtual void PostTransfer(bool success)
{
}
}
public class LogTransfer : BankingServiceAspect
{
// Note: this is never getting set from Autofac!
public ILog Log { get; set; }
public override void PreTransfer(string from, string to, decimal amount)
{
Log.LogMessage(string.Format("About to transfer from {0}, to {1}, for amount {2}", from, to, amount));
}
public override void PostTransfer(bool success)
{
Log.LogMessage(success ? "Transfer completed!" : "Transfer failed!");
}
}
public abstract class BankingServiceBase : IBankingService
{
private readonly List<BankingServiceAspect> aspects;
protected BankingServiceBase()
{
// Note: My guess is that this "GetCustomAttributes" is happening before the IOC dependency map is built.
aspects =
GetType().GetCustomAttributes(typeof (BankingServiceAspect), true).Cast<BankingServiceAspect>().
ToList();
}
void IBankingService.Transfer(string from, string to, decimal amount)
{
aspects.ForEach(a => a.PreTransfer(from, to, amount));
try
{
Transfer(from, to, amount);
aspects.ForEach(a => a.PostTransfer(true));
}
catch (Exception)
{
aspects.ForEach(a => a.PostTransfer(false));
}
}
public abstract void Transfer(string from, string to, decimal amount);
}
[LogTransfer]
public class BankingService : BankingServiceBase
{
public override void Transfer(string from, string to, decimal amount)
{
// Simulate some latency..
Thread.Sleep(1000);
}
}
}
You're correct that GetCustomAttributes doesn't resolve the custom attributes via Autofac - if you think about it, how could FCL code such as GetCustomAttributes know about Autofac? The custom attributes are actually retrieved from assembly metadata, so they never go through Autofac's resolution process and therefore your registration code is never used.
What you can do is to inject the services into the attribute instance yourself. Begin with the code in Oliver's answer to generate the list of aspect attributes. However, before returning the list, you can process each attribute and inject services into any dependent fields and properties. I have a class called AttributedDependencyInjector, which I use via an extension method. It uses reflection to scan for fields and properties that are decorated with the InjectDependencyAttribute and then set the value of those properties. There's rather a lot of code to cope with various scenarios, but here it is.
The attribute class:
/// <summary>
/// Attribute that signals that a dependency should be injected.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class InjectDependencyAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref = "InjectDependencyAttribute" /> class.
/// </summary>
public InjectDependencyAttribute()
{
this.PreserveExistingValue = false;
}
/// <summary>
/// Gets or sets a value indicating whether to preserve an existing non-null value.
/// </summary>
/// <value>
/// <c>true</c> if the injector should preserve an existing value; otherwise, <c>false</c>.
/// </value>
public bool PreserveExistingValue { get; set; }
}
The injector class:
public class AttributedDependencyInjector
{
/// <summary>
/// The component context.
/// </summary>
private readonly IComponentContext context;
/// <summary>
/// Initializes a new instance of the <see cref="AttributedDependencyInjector"/> class.
/// </summary>
/// <param name="context">The context.</param>
public AttributedDependencyInjector(IComponentContext context)
{
this.context = context;
}
/// <summary>
/// Injects dependencies into an instance.
/// </summary>
/// <param name="instance">The instance.</param>
public void InjectDependencies(object instance)
{
this.InjectAttributedFields(instance);
this.InjectAttributedProperties(instance);
}
/// <summary>
/// Gets the injectable fields.
/// </summary>
/// <param name="instanceType">
/// Type of the instance.
/// </param>
/// <param name="injectableFields">
/// The injectable fields.
/// </param>
private static void GetInjectableFields(
Type instanceType, ICollection<Tuple<FieldInfo, InjectDependencyAttribute>> injectableFields)
{
const BindingFlags BindingsFlag =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
IEnumerable<FieldInfo> fields = instanceType.GetFields(BindingsFlag);
// fields
foreach (FieldInfo field in fields)
{
Type fieldType = field.FieldType;
if (fieldType.IsValueType)
{
continue;
}
// Check if it has an InjectDependencyAttribute
var attribute = field.GetAttribute<InjectDependencyAttribute>(false);
if (attribute == null)
{
continue;
}
var info = new Tuple<FieldInfo, InjectDependencyAttribute>(field, attribute);
injectableFields.Add(info);
}
}
/// <summary>
/// Gets the injectable properties.
/// </summary>
/// <param name="instanceType">
/// Type of the instance.
/// </param>
/// <param name="injectableProperties">
/// A list into which are appended any injectable properties.
/// </param>
private static void GetInjectableProperties(
Type instanceType, ICollection<Tuple<PropertyInfo, InjectDependencyAttribute>> injectableProperties)
{
// properties
foreach (var property in instanceType.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
Type propertyType = property.PropertyType;
// Can't inject value types
if (propertyType.IsValueType)
{
continue;
}
// Can't inject non-writeable properties
if (!property.CanWrite)
{
continue;
}
// Check if it has an InjectDependencyAttribute
var attribute = property.GetAttribute<InjectDependencyAttribute>(false);
if (attribute == null)
{
continue;
}
// If set to preserve existing value, we must be able to read it!
if (attribute.PreserveExistingValue && !property.CanRead)
{
throw new BoneheadedException("Can't preserve an existing value if it is unreadable");
}
var info = new Tuple<PropertyInfo, InjectDependencyAttribute>(property, attribute);
injectableProperties.Add(info);
}
}
/// <summary>
/// Determines whether the <paramref name="propertyType"/> can be resolved in the specified context.
/// </summary>
/// <param name="propertyType">
/// Type of the property.
/// </param>
/// <returns>
/// <c>true</c> if <see cref="context"/> can resolve the specified property type; otherwise, <c>false</c>.
/// </returns>
private bool CanResolve(Type propertyType)
{
return this.context.IsRegistered(propertyType) || propertyType.IsAssignableFrom(typeof(ILog));
}
/// <summary>
/// Injects dependencies into the instance's fields.
/// </summary>
/// <param name="instance">
/// The instance.
/// </param>
private void InjectAttributedFields(object instance)
{
Type instanceType = instance.GetType();
// We can't get information about the private members of base classes through reflecting a subclass,
// so we must walk up the inheritance hierarchy and reflect at each level
var injectableFields = new List<Tuple<FieldInfo, InjectDependencyAttribute>>();
var type = instanceType;
while (type != null)
{
GetInjectableFields(type, injectableFields);
type = type.BaseType;
}
// fields
foreach (var fieldDetails in injectableFields)
{
var field = fieldDetails.Item1;
var attribute = fieldDetails.Item2;
if (!this.CanResolve(field.FieldType))
{
continue;
}
// Check to preserve existing value
if (attribute.PreserveExistingValue && (field.GetValue(instance) != null))
{
continue;
}
object fieldValue = this.Resolve(field.FieldType, instanceType);
field.SetValue(instance, fieldValue);
}
}
/// <summary>
/// Injects dependencies into the instance's properties.
/// </summary>
/// <param name="instance">
/// The instance.
/// </param>
private void InjectAttributedProperties(object instance)
{
Type instanceType = instance.GetType();
// We can't get information about the private members of base classes through reflecting a subclass,
// so we must walk up the inheritance bierarchy and reflect at each level
var injectableProperties = new List<Tuple<PropertyInfo, InjectDependencyAttribute>>();
var type = instanceType;
while (type != typeof(object))
{
Debug.Assert(type != null, "type != null");
GetInjectableProperties(type, injectableProperties);
type = type.BaseType;
}
// Process the list and inject properties as appropriate
foreach (var details in injectableProperties)
{
var property = details.Item1;
var attribute = details.Item2;
// Check to preserve existing value
if (attribute.PreserveExistingValue && (property.GetValue(instance, null) != null))
{
continue;
}
var propertyValue = this.Resolve(property.PropertyType, instanceType);
property.SetValue(instance, propertyValue, null);
}
}
/// <summary>
/// Resolves the specified <paramref name="propertyType"/> within the context.
/// </summary>
/// <param name="propertyType">
/// Type of the property that is being injected.
/// </param>
/// <param name="instanceType">
/// Type of the object that is being injected.
/// </param>
/// <returns>
/// The object instance to inject into the property value.
/// </returns>
private object Resolve(Type propertyType, Type instanceType)
{
if (propertyType.IsAssignableFrom(typeof(ILog)))
{
return LogManager.GetLogger(instanceType);
}
return this.context.Resolve(propertyType);
}
}
The extension method:
public static class RegistrationExtensions
{
/// <summary>
/// Injects dependencies into the instance's properties and fields.
/// </summary>
/// <param name="context">
/// The component context.
/// </param>
/// <param name="instance">
/// The instance into which to inject dependencies.
/// </param>
public static void InjectDependencies(this IComponentContext context, object instance)
{
Enforce.ArgumentNotNull(context, "context");
Enforce.ArgumentNotNull(instance, "instance");
var injector = new AttributedDependencyInjector(context);
injector.InjectDependencies(instance);
}
}
Try to implement a lazy loading of the aspects
private readonly List<BankingServiceAspect> _aspects;
private List<BankingServiceAspect> Aspects
{
get
{
if (_aspects == null) {
_aspects = GetType()
.GetCustomAttributes(typeof(BankingServiceAspect), true)
.Cast<BankingServiceAspect>()
.ToList();
}
return _aspects;
}
}
Then use it like this
Aspects.ForEach(a => a.PreTransfer(from, to, amount));
...

Stored Procedure loses connection

I have an ASP.NET MVC project in which the model is managed through .NET entities and it seems that some times it loses the connection, but this happens only on stored procedures.
I get the following error:
Execution of the command requires an open and available connection. The connection's current state is broken.
Why is this happening?
Code
public ObjectResult<Categories> GetCategoriesStructure() {
return ObjectContext.getCategoriesStructure();
}
var catss = GetCategoriesStructure().ToList();
this exception occurs when I am trying to assign the List to catss variable
Object Context Instantiation
public abstract class ObjectContextManager {
/// <summary>
/// Returns a reference to an ObjectContext instance.
/// </summary>
public abstract TObjectContext GetObjectContext<TObjectContext>()
where TObjectContext : ObjectContext, new();
}
public abstract class BaseDAO<TObjectContext, TEntity> : IBaseDAO<TObjectContext, TEntity>
where TObjectContext : System.Data.Objects.ObjectContext, new()
where TEntity : System.Data.Objects.DataClasses.EntityObject {
private ObjectContextManager _objectContextManager;
/// <summary>
/// Returns the current ObjectContextManager instance. Encapsulated the
/// _objectContextManager field to show it as an association on the class diagram.
/// </summary>
private ObjectContextManager ObjectContextManager {
get { return _objectContextManager; }
set { _objectContextManager = value; }
}
/// <summary>
/// Returns an ObjectContext object.
/// </summary>
protected internal TObjectContext ObjectContext {
get {
if (ObjectContextManager == null)
this.InstantiateObjectContextManager();
return ObjectContextManager.GetObjectContext<TObjectContext>();
}
}
/// <summary>
/// Default constructor.
/// </summary>
public BaseDAO() { }
/// <summary>
/// Instantiates a new ObjectContextManager based on application configuration settings.
/// </summary>
private void InstantiateObjectContextManager() {
/* Retrieve ObjectContextManager configuration settings: */
Hashtable ocManagerConfiguration = ConfigurationManager.GetSection("ObjectContextManagement.ObjectContext") as Hashtable;
if (ocManagerConfiguration != null && ocManagerConfiguration.ContainsKey("managerType")) {
string managerTypeName = ocManagerConfiguration["managerType"] as string;
if (string.IsNullOrEmpty(managerTypeName))
throw new ConfigurationErrorsException("The managerType attribute is empty.");
else
managerTypeName = managerTypeName.Trim().ToLower();
try {
/* Try to create a type based on it's name: */
Assembly frameworkAssembly = Assembly.GetAssembly(typeof(ObjectContextManager));
Type managerType = frameworkAssembly.GetType(managerTypeName, true, true);
/* Try to create a new instance of the specified ObjectContextManager type: */
this.ObjectContextManager = Activator.CreateInstance(managerType) as ObjectContextManager;
} catch (Exception e) {
throw new ConfigurationErrorsException("The managerType specified in the configuration is not valid.", e);
}
} else
throw new ConfigurationErrorsException("ObjectContext tag or its managerType attribute is missing in the configuration.");
}
/// <summary>
/// Persists all changes to the underlying datastore.
/// </summary>
public void SaveAllObjectChanges() {
this.ObjectContext.SaveChanges();
}
/// <summary>
/// Adds a new entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Add(TEntity newObject) {
this.ObjectContext.AddObject(newObject.GetType().Name, newObject);
}
/// <summary>
/// Deletes an entity object.
/// </summary>
/// <param name="obsoleteObject">An obsolete object.</param>
public virtual void Delete(TEntity obsoleteObject) {
this.ObjectContext.DeleteObject(obsoleteObject);
}
public void Detach(TEntity obsoleteObject) {
this.ObjectContext.Detach(obsoleteObject);
}
/// <summary>
/// Updates the changed entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Update(TEntity newObject) {
ObjectContext.ApplyPropertyChanges(newObject.GetType().Name, newObject);
ObjectContext.Refresh(RefreshMode.ClientWins, newObject);
}
public virtual TEntity LoadByKey(String propertyName, Object keyValue) {
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>(propertyName, keyValue) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey(this.ObjectContext.GetType().Name + "." + typeof(TEntity).Name, entityKeyValues);
return (TEntity)this.ObjectContext.GetObjectByKey(key);
}
#region IBaseDAO<TObjectContext,TEntity> Members
public bool validation(TEntity newObject) {
return newObject.GetType().Name.ToString() == "Int32";
}
#endregion
}
Without knowing how you are instantiating your ObjectContext, I'll throw something in the answer bucket here.
This is how I do my Entity Framework commands and connections (for small simple projects at least):
using (MyEntities context = new MyEntities())
{
return context.getCategoriesStructure();
}
You can also optionally pass in a connection string when instantiating your context (if not, it will use the one in your app.config):
new MyEntities("...connection string...")
If this does not help your issue, please help us understand your code a little better by posting how you are creating your ObjectContext. You could at least attempt to do it this way to see if it works; that will tell you whether it is an issue with your connection string or not.

Categories

Resources