Andrew Rea - For February 2010
-
A BinaryContentResult for ASP.NET MVC
Today in work I had a requirement where I wanted to output binary content to the response output stream. I also want to stay with the MVC Controller for serving this content and so wanted a result I could add my data to and return it. To my knowledge there is a ContentResult, but with this the Content property is of type string, and is not what I wanted. I this particular case, I am physically GZipping content and then writing out to the Response.OutputStream.
Previous to this I have been seeing and using examples where you assign a GZipOutputStream as a Filter on the Response dynamically. For some strange reason on the Live environment the header and filter where being ignored on the response yet on the test, builds and sandbox everything worked as expected and the dynamic resources got GZipped.
So I had to go another way and what I came up with was to physically GZip the output content and send it to the response with the accompanying header. I cannot see why this would not work on the server as the content is physically being served already GZipped opposed to applying a filter which can obviously be removed somewhere in the pipeline. Any way after some reading I found some evidence that a library, http://www.icsharpcode.net/OpenSource/SharpZipLib/ , yields far better results than the out of the box one from .NET, so for this example I have a dependency on this assembly. Apart from that I have added a couple of properties which allow for the conditional Compression and also a list of headers to give a little more flexibility. So here is the code:
/// <summary> /// A content result which can accept binart data and will write to the output /// stream. If GZip is set to true the content will be GZipped and the relevant /// header added to the response HTTP Headers /// </summary> public class BinaryContentResult : ActionResult { public byte[] Data { get; set; } public NameValueCollection Headers { get; set; } public bool Gzip { get; set; } public override void ExecuteResult(ControllerContext context) { foreach (string s in Headers.Keys) { context.HttpContext.Response.AddHeader(s, Headers[s]); } if (Gzip) { using (var os = new GZipOutputStream(context.HttpContext.Response.OutputStream)) { os.Write(Data, 0, Data.Length); } context.HttpContext.Response.AddHeader("Content-Encoding", "gzip"); context.HttpContext.Response.AddHeader("X-Compressed-By", "Custom-Compressor"); } else { context.HttpContext.Response.BinaryWrite(Data); } context.HttpContext.Response.End(); } }Posted by Andrew Rea on February 16 at 1:29 PMComments (0)
-
A StructureMap Container For Agatha
If you have not read about it yet or seen it or used it, please visit Davy Brion's Blog here and download the Agatha project. Another link I need to mention is the following, Integrating Strcuture Map With WCF. Basically what I have attempted to do is create an Agatha.Common.InversionOfControl.IContainer implementation for Agatha which uses StrcutureMap as the IOC Container. I am also not hosting the WCF Service inside a Web Application but in an actual WCF Service Application which I wanted to use the Service Host Factory as demoed in the above link.
The implementation for the Container was relatively straight forward in most parts and allowed me to use some newer features of StructureMap which I do not currently use yet in my job; well I say newer, newer to me!!. I have read on Davy Brion's blog that he is intending to write this Agatha IOC Container for Structure Map, and I look forward to it, but in the mean time I thought I would have a crack at it myself.
While writing the Container for StructureMap I had to make a small change/addition to the Agatha source code, on the interface for the IContainer to make sure that a method had a generic type constraint. The method:
void Register<TComponent,TImplementation>()
A compile time error was raised which stated that there was no information to allow for boxing/unboxing. To solve this I added the following constraint:
void Register<TComponent,TImplementation>() where TImplementation : TComponent
Once added the compile time error went, but I was still left with another issue which at this stage of writing this post I am of the opinion that I need to also make a small addition to StructureMap. I look forward to it, if it is not already available but it would be good to find out that it is already accounted for. The operation which I am talking about is the removal of an instance from the IOC Container. The interface for the Agatha IOC IContainer has a method called Release which takes an object instance as a parameter and with the Castle WInsor you can release an instance.
public interface IContainer { void Register<TComponent, TImplementation>(Lifestyle lifestyle) where TImplementation : TComponent; void Register(Type componentType, Type implementationType, Lifestyle lifeStyle); void RegisterInstance<TComponent>(TComponent instance); void RegisterInstance(Type componentType, object instance); void Release(object component); TComponent Resolve<TComponent>(); object Resolve(Type componentType); }…
After doing a bit more searching I cannot find a function which fits this criteria exactly. There is the EjectAllInstancesOf<T> method, but the one I ended up using is the following:
public void Release(object component) { //throw new NotImplementedException(); _strcutureMapContainer.Model.EjectAndRemove(component.GetType()); }This is a method on the IContainer interface and I am using the component to get its type and releasing all instances of it within the IOC Container. I am not 100% sure this is what other implementation of IOC are doing with this particular type of function. Never the less, this does work fine and I am now up and running with the Agatha library inside a WCF Service Application Project using a ServiceHostFactory and configuring a StructureMap IOC Container for use with Agatha.
I have not made the most consistent approach to the StructureMap usage as I have mixed preferred and deprecated methods. This is because I am not 100% sure how to map the new style of Lifeycle management between Castle Windsor and StructureMap. I look forward to seeing the actual implementation which goes into the Agatha project so I can see where I could have improved my attempt. Here is the finished code of my attempt.
Hope this is of some help,
Cheers,
Andrew
using System; using System.Collections.Generic; using System.Linq; using System.Web; using sm = StructureMap; using Agatha.Common.InversionOfControl; namespace Agatha.StructureMap { public class Container : Agatha.Common.InversionOfControl.IContainer { private readonly sm.IContainer _strcutureMapContainer; private Dictionary<Lifestyle, sm.InstanceScope> _lifeStyleMappings = new Dictionary<Lifestyle, sm.InstanceScope> { {Lifestyle.Singleton, sm.InstanceScope.Singleton}, {Lifestyle.Transient, sm.InstanceScope.Transient} }; private Dictionary<Lifestyle, sm.Pipeline.ILifecycle> _lifeStyleLifeCycleMappings = new Dictionary<Lifestyle, sm.Pipeline.ILifecycle> { {Lifestyle.Singleton, new sm.Pipeline.SingletonLifecycle()}, {Lifestyle.Transient, new sm.Pipeline.UniquePerRequestLifecycle()} }; public Container() : this(new sm.Container()) { } public Container(sm.IContainer structureMapContainer) { _strcutureMapContainer = structureMapContainer; } public void Register(Type componentType, Type implementationType, Lifestyle lifeStyle) { _strcutureMapContainer.Configure(x => x.For(componentType).LifecycleIs(_lifeStyleMappings[lifeStyle]).Use(implementationType)); } public void Register<TComponent, TImplementation>(Lifestyle lifestyle) where TImplementation:TComponent { _strcutureMapContainer.Configure(x => x.ForRequestedType<TComponent>() .CacheBy(_lifeStyleMappings[lifestyle]) .TheDefaultIsConcreteType<TImplementation>()); } public void RegisterInstance(Type componentType, object instance) { _strcutureMapContainer.Configure(x => x.ForRequestedType(componentType).Use(instance)); } public void RegisterInstance<TComponent>(TComponent instance) { _strcutureMapContainer.Configure(x => x.For<TComponent>().Use(instance)); } public TComponent Resolve<TComponent>() { return _strcutureMapContainer.GetInstance<TComponent>(); } public object Resolve(Type componentType) { return _strcutureMapContainer.GetInstance(componentType); } public void Release(object component) { //throw new NotImplementedException(); _strcutureMapContainer.Model.EjectAndRemove(component.GetType()); } } }Posted by Andrew Rea on February 10 at 4:16 PMComments (0)