Extend ASP.NET MVC for Asynchronous Action

Numerous new features come into people’s eyes since ASP.NET 2.0 and asynchronous request handling is one of the most important parts. It reduces the working thread number by the async mechanism based on IO completion port when processing IO bound request and improves throughput of web sites significantly (please refer to the theory analyzing and benchmarks if you can read Chinese :P). But the current version of ASP.NET MVC doesn’t support async actions and that’s just ‘the missing key feature’ I mentioned before. I gave a so-called solution that introduced the async action to ASP.NET MVC in my session of TechED 2008 China but it’s full of limitations and inappropriate for production use.

These days I’m preparing for the session in the coming .NET conference in Shanghai (that Jeffrey Richter would also attend) and I think it’s better to release a better solution at that time: straightforward, rather full-featured and light-weighted – the core funtion contains only about 200 lines of codes, which means it maximize the usage of existing framework functions to make the solution more stable, efficient and backward-compatible.

I built the the new solution based on ASP.NET MVC Beta at first but RC released when I was writing the post. When I was trying to change the implementation for RC I found the improvements in design that impressed me a lot. These improvements change my strategy of building the solution but seems it’s a little easier when building extensions for RC. The ASP.NET MVC team did a good job.

Now let’s get started.

Change the way of request handling

It should be clear how ASP.NET MVC handles a request before we make decisions:

  1. When the application starts (without any incoming requests), route strategies would be registered in ASP.NET Routing module. At that time each strategy object (Route instance) contains a route handler – which is an intance of MvcRouteHandler type in ASP.NET MVC framework.
  2. When the Routing module meets a request that fit one of the strategies, the route handler belongs to the corresponding Route object would be used (by calling GetHttpHandler method) to get an http handler that can process the request. The MvcRouteHandler always returns an MvcHandler object.
  3. When MvcHandler object is processing the request, the ‘controller’ value in RouteData would be retrieved to build a controller object (which implements IController) by a controller factory (which implements IControllerFactory). After that, the controller would be executed (by calling Execute method on it).
  4. Generally, in an ASP.NET MVC application a controller type inherits from the System.Web.Mvc.Controller class. When executing these kind of controllers, the ‘action’ value in RouteData would be retrieved and passed to the action invoker (which is an IActionInvoker object get by the ActionInvoker property) to execute the action.

If the way of processing requet needs to be async, we have to let it meet the architecture of ASP.NET. There’re several method to enabled the async mechanism such as async page and async http module, but the best one to use in our scenario is using async http handler. To implement an async http handler, we must make the handler type to implement IHttpAsyncHandler instead ot IHttpHandler. The BeginProcessRequest and EndProcessRequest methods compose the ‘two-phase’ processing style which meets the standard APM (Aynchronous Programming Model) pattern in .NET.

Now you may realized that an IHttpAsyncHandler should be used if we want to execute an action asynchronously, but the default choice of ASP.NET MVC framework is MvcHandler, which is always sync. It’s too late for us to decide whether the executing action is async or not in the handler. We have to move it to Routing. Fortunately, the route handler in ASP.NET Routing is just like IHttpHandlerFactory in ASP.NET architecture, which can be used to create an http handler object dynamiclly by context. So the first step we shoud do is to build a new route handler to replace the default MvcRouteHandler. Here comes the AsyncMvcRouteHandler – you can imagine that part of it is the same as MvcHandler since what we need to do is just to move some code to the earlier stage:

public class AsyncMvcRouteHandler : IRouteHandler
{
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        string controllerName = requestContext.RouteData.GetRequiredString("controller");

        var factory = ControllerBuilder.Current.GetControllerFactory();
        var controller = factory.CreateController(requestContext, controllerName);
        if (controller == null)
        {
            throw new InvalidOperationException(...);
        }

        var coreController = controller as Controller;
        if (coreController == null)
        {
            return new SyncMvcHandler(controller, factory, requestContext);
        }
        else
        {
            string actionName = requestContext.RouteData.GetRequiredString("action");
            return IsAsyncAction(coreController, actionName, requestContext) ?
                (IHttpHandler)new AsyncMvcHandler(coreController, factory, requestContext) :
                (IHttpHandler)new SyncMvcHandler(controller, factory, requestContext);
        }
    }

    internal static bool IsAsyncAction(
        Controller controller, string actionName, RequestContext requestContext)
    {
        ...
    }
}

In GetHttpHandler method, we get the controller name from RouteData and create a controller object by the factory registered in ControllerBuilder. We try to cast the controller object as Controller type since we’ll use the action invoker in it to decide whether the action is async or not. If the controller object is in type Controller, we’ll get the action name from RouteData and try to return an AsyncMvcHandler (which implements IHttpAsyncHandler) or SyncMvcHandler (which implements IHttpHandler) object by the result of IsAsyncAction method.

To enable the AyncMvcRouteHandler, we should use it to replace the default one while mapping route strategies when application starts.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",                                              // Route name
        "{controller}/{action}/{id}",                           // URL with parameters
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    ).RouteHandler = new AsyncMvcRouteHandler();
}

Decide whether the action is async or not

We made a convention in code above: an async action could only be defined in an controller type which inherits from Controller. Here’s another one: the action invoker in the controller must be an object of ControllerActionInvoker or its child type.

There’re several ‘helper’ methods in ControllerActionInvoker which can be used to get the descriptors for controller and actions. We can get the action’s information from its descriptor object, such as whether it has been marked with AsyncActionAttribute – that’s the sign of an async action:

private static object s_methodInvokerMutex = new object();
private static MethodInvoker s_controllerDescriptorGetter;

internal static bool IsAsyncAction(
    Controller controller, string actionName, RequestContext requestContext)
{
    var actionInvoker = controller.ActionInvoker as ControllerActionInvoker;
    if (actionInvoker == null) return false;

    if (s_controllerDescriptorGetter == null)
    {
        lock (s_methodInvokerMutex)
        {
            if (s_controllerDescriptorGetter == null)
            {
                BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
                MethodInfo method = typeof(ControllerActionInvoker).GetMethod(
                    "GetControllerDescriptor", bindingFlags);
                s_controllerDescriptorGetter = new MethodInvoker(method);
            }
        }
    }

    var controllerContext = new ControllerContext(requestContext, controller);
    var controllerDescriptor = (ControllerDescriptor)s_controllerDescriptorGetter.Invoke(
        actionInvoker, controllerContext);
    var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
    return actionDescriptor == null ? false :
        actionDescriptor.GetCustomAttributes(typeof(AsyncActionAttribute), false).Any();
}

There’s a protected method named ‘GetControllerDescriptor’. It accepts a ControllerContext object as parameter and returns a ControllerDescriptor object to describe the controller. From the descriptor of controller we can get the ActionDescriptor object as the descriptor of the executing action by calling FindAction method with the action name as one of the parameters. IsAsyncAction returns false for a non existing action so that SyncMvcHandler would be used to process the request with the default behavior. If and only if the action is marked with AsyncActionAttribute it would be identified as an async one. BTW, a special class ‘MethodInvoker’ is used above. It is a replacement for the function of MethodInfo.Invoke method and provide huge performance improvement. That’s a helper class from Fast Reflection Library project (I also wrote a post about it) that you can use for your own need.

Let’s talk about the change of design in ASP.NET MVC RC. There’s nothing like the descriptors but a method to get the MethodInfo of the action in previous ControllerActionInvoker. The current design uses the implementations based on reflection for the abstraction of descriptors by default. e.g., the descriptor of an action we used is an object of RelfectedActionDescriptor class, which implements the abstract class ActionDescriptor. That’s a great improvement. Since we’re using descriptors for elements (controller/action/parameter, etc.),

  • we could use different concrete implementations to describe elements. e.g., we can build descriptors based on configurations rather than the default behavior – reflection – which is more like ‘convention’.
  • there’s no limitations for the underly form of action. e.g., an async action could be composed by two methods.
  • it’s easy to add more information on descriptor types. e.g., in the future we could know whether the action disabled session state from an action descriptor.

Execute an action

The SyncMvcHandler type which used to execute a normal action is quite an easy one:

public class SyncMvcHandler : IHttpHandler, IRequiresSessionState
{
    public SyncMvcHandler(
        IController controller,
        IControllerFactory controllerFactory,
        RequestContext requestContext)
    {
        this.Controller = controller;
        this.ControllerFactory = controllerFactory;
        this.RequestContext = requestContext;
    }

    public IController Controller { get; private set; }
    public RequestContext RequestContext { get; private set; }
    public IControllerFactory ControllerFactory { get; private set; }

    public virtual bool IsReusable { get { return false; } }

    public virtual void ProcessRequest(HttpContext context)
    {
        try
        {
            this.Controller.Execute(this.RequestContext);
        }
        finally
        {
            this.ControllerFactory.ReleaseController(this.Controller);
        }
    }
}

But I thought a lot for dealing with an async action, or how I can change the default execution style into ‘two-phase’ (BeginXxx/EndXxx). I tried to implement a new action invoker before but found it would take a lot of work. If I want to keep all the current features (action filter, action selector, etc.), it seems the best way is to build a child class of ControllerActionInvoker and use te exsiting functions as much as possible. But I think it’s nealy impossible after reading the code. For example, one of the characters of an action method is that it returns ActionResult (or its child), but the BeginXxx method for an async action returns IAsyncResult so we cannot use the FindAction method in ControllerActionInvoker directly; and, if we want to use FindAction to get the ‘EndAbc’ method by passing the action name ‘Abc’ with ‘End’ before it, what would happen if there’s a request to execute a sync action ‘EndAbc’?

Since the problems described above, I almost rewrite the whole invoker last year and it brings a lot of complexities and limitations. Developers have to compromise in some aspect when using it. When I introduced the solution in my session of TechED 2008 China I said it should not be used in production environment.

The current solution is much better. It uses an interesting work around to solve the problems. It’s not perfect but usable. The reason we meet such difficulties is that we break the design of framework, from single action method to a ‘APM-style’ execution. Wait a minute, have you idetified the source of the problems? That’s right, it’s the ‘APM-style’.

‘APM-style’ separates a single method into a pair of BeginXxx/EndXxx method, but we can just implement an async execute with an ‘two-phase’ style. Since the framework force an action to return an ActionResult, why can’t we keep the reference(s) of method(s) in the object? It’s not ‘APM-style’ but it’s really ‘async-style’, isn’t it?

public class AsyncActionResult : ActionResult
{
    public AsyncActionResult(
        IAsyncResult asyncResult,
        Func<IAsyncResult, ActionResult> endDelegate)
    {
        this.AsyncResult = asyncResult;
        this.EndDelegate = endDelegate;
    }

    public IAsyncResult AsyncResult { get; private set; }

    public Func<IAsyncResult, ActionResult> EndDelegate { get; private set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.Controller
            .SetAsyncResult(this.AsyncResult)
            .SetAsyncEndDelegate(this.EndDelegate);
    }
}

We execute the BeginXxx method in the action method and return an AsyncActionResult instance contains the IAsyncResut object and the reference to the EndXxx method. These two objects will be saved when the result executes and we can get them back in AsyncMvcHandler.EndProcessRequest method. Generally we should build an extension method to help the developers to return an AsyncActionResult object from an action method. Now we get the style of an async action:

[AsyncAction]
public ActionResult AsyncAction(AsyncCallback asyncCallback, object asyncState)
{
    SqlConnection conn = new SqlConnection("...;Asynchronous Processing=true");
    SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:03';", conn);
    conn.Open();

    return this.Async(
        cmd.BeginExecuteNonQuery(asyncCallback, asyncState),
        (ar) =>
        {
            int value = cmd.EndExecuteNonQuery(ar);
            conn.Close();
            return this.View();
        });
}

No secret for AsyncMvcHandler now:

public class AsyncMvcHandler : IHttpAsyncHandler, IRequiresSessionState
{
    public AsyncMvcHandler(
        Controller controller,
        IControllerFactory controllerFactory,
        RequestContext requestContext)
    {
        this.Controller = controller;
        this.ControllerFactory = controllerFactory;
        this.RequestContext = requestContext;
    }

    public Controller Controller { get; private set; }
    public RequestContext RequestContext { get; private set; }
    public IControllerFactory ControllerFactory { get; private set; }
    public HttpContext Context { get; private set; }

    public IAsyncResult BeginProcessRequest(
        HttpContext context,
        AsyncCallback cb,
        object extraData)
    {
        this.Context = context;
        this.Controller.SetAsyncCallback(cb).SetAsyncState(extraData);

        try
        {
            (this.Controller as IController).Execute(this.RequestContext);
            return this.Controller.GetAsyncResult();
        }
        catch
        {
            this.ControllerFactory.ReleaseController(this.Controller);
            throw;
        }
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        try
        {
            HttpContext.Current = this.Context;
            ActionResult actionResult = this.Controller.GetAsyncEndDelegate()(result);
            if (actionResult != null)
            {
                actionResult.ExecuteResult(this.Controller.ControllerContext);
            }
        }
        finally
        {
            this.ControllerFactory.ReleaseController(this.Controller);
        }
    }
}

We save the current HttpContext object in BeginProcessRequest – that’s important since HttpContext.Current is based on the call context and it would be thrown after an async callback, which means we shoud set it back in EndProcessRequest for the rest functions. After saving the HttpContext object, we should also save the async callback and async state parameters, and next, execute the controller. The whole progress is finished after Execute method’s return, which means the IAsyncResult object and the reference to the EndXxx method haved been saved. We get the IAsyncResult object back and return from BeginProcessRequest. In EndProcessRequest method, we retrive the saved reference to the EndXxx method and get another ActionResult by calling it. Finally we execute the new result and complete the whole async processing.

The code above only considers the logic of normal state. You can get more (e.g. logic to deal with the exceptional state) in the source code of the solution. Both the BeignProcessRequest and EndProcessRequest methods take care of error handling to make the controller released at a property time.

ModelBinder support

Actually you cannot use async action so far, since the default model binder don’t know how to bind an AsyncCallback object and the AsyncCallback parameter in the action method is always null. That’s easy. We could build a AsyncCallbackModelBinder whose only purpose is to return the AsyncCallback object saved in controller:

public sealed class AsyncCallbackModelBinder : IModelBinder
{
    public object BindModel(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        return controllerContext.Controller.GetAsyncCallback();
    }
}

We should register the model binder for AsyncCallback type when application starts.

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders[typeof(AsyncCallback)] = new AsyncCallbackModelBinder();
}

It’s inappropriate to do so for async state parameter in an action method since object is the base class of all the types and it’s not specific to the async state. I suggest that you mark an attribute for the async state parameter in each action method. Here’s the AsyncStateAttribute, which has been built into the solution with AsyncStateModelBinder:

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class AsyncStateAttribute : CustomModelBinderAttribute
{
    private static AsyncStateModelBinder s_modelBinder = new AsyncStateModelBinder();

    public override IModelBinder GetBinder()
    {
        return s_modelBinder;
    }
}

Here’s the way to use it:

[AsyncAction]
public ActionResult AsyncAction(AsyncCallback cb, [AsyncState]object state) { ... }

Actually it won’t be a big deal even if you ignore the async state parameter since it’s always useless when processing a request aynchronously. And, you can also get the async callback and async state object by the extensions methods (GetAsyncCallback and GetAsyncState) defined for Controller type, but it reduces the testability.

Limitations and drawbacks

The solution definitely has limitations and drawbacks:

  1. It doesn’t follow the standard APM pattern.
  2. Since the solution is based on the existing functions in framework, all the filters would be executed when BeginXxx method finished.
  3. Some features are missed since the filters cannot be applied to EndXxx method and the final action result.

ASP.NET MVC will introduce the ‘async controller’ in the future according to the official roadmap. Actually the MvcFuture project in the source of ASP.NET MVC RC has already contains the code about async features. You can found serveral new types such as IAsyncController, AsyncController, IAsyncActionInvoker, AsyncControllerActionInvoker, etc.. All types are inherited from exsiting type but as I predicted before, the AsyncControllerActionInvoker class rewrites almost all the code. I cannot tell the quality of design before reading it – hope it is as good as the current one.

I’m going do more for the drawbacks listed above. I hope it would become a solution good enough for production. The whole solution, source code and benchmark have been published in MSDN Code Gallery. Please feel free to contact me for any comments and suggestions.

87 Comments

  • Wow, fantastic blog layout! How long have you been blogging for?
    you make blogging look easy. The overall look
    of your site is great, let alone the content!

  • Hello, this weekend is nice designed for me, for the reason that this moment i am reading this impressive informative article
    here at my residence.

  • Ahaa, its good conversation about this piece of writing here at this
    web site, I have read all that, so now me also commenting here.

  • Your mode of explaining all in this paragraph is in fact pleasant, every one
    be able to effortlessly understand it, Thanks a lot.

  • This post will assist the internet users for building up new
    blog or even a blog from start to end.

  • certainly like your web site however you need to
    check the spelling on quite a few of your posts. A number of them are rife with spelling problems and I in finding
    it very troublesome to inform the reality then again I will certainly come back again.

  • It's very simple to find out any matter on web as compared to textbooks, as I found this post at this web site.

  • I like what you guys are up too. This type of clever work and exposure!
    Keep up the superb works guys I've added you guys to blogroll.

  • My developer is trying to convince me to move to .
    net from PHP. I have always disliked the idea because of the
    expenses. But he's tryiong none the less. I've been using Movable-type on various websites for about a
    year and am nervous about switching to another platform.

    I have heard good things about blogengine.net. Is there a
    way I can transfer all my wordpress posts into it?
    Any help would be greatly appreciated!

  • If some one wishes expert view concerning blogging and site-building then i propose him/her to pay a visit this blog,
    Keep up the pleasant job.

  • Really no matter if someone doesn't understand then its up to other visitors that they will help, so here it occurs.

  • At this time it looks like Drupal is the best blogging platform available right now.
    (from what I've read) Is that what you are using on your blog?

  • Hey! I just wanted to ask if you ever have any problems with hackers?
    My last blog (wordpress) was hacked and I ended up losing
    several weeks of hard work due to no data backup.
    Do you have any solutions to protect against hackers?

  • Hi there everyone, it's my first pay a visit at this site, and piece of writing is actually fruitful for me, keep up posting these articles.

  • If some one desires to be updated with newest technologies then he must be
    visit this website and be up to date all the time.

  • Hi there would you mind letting me know which hosting company you're using? I've loaded your
    blog in 3 different browsers and I must say this blog loads a lot quicker then most.
    Can you recommend a good internet hosting provider at a fair price?

    Thanks a lot, I appreciate it!

  • That is a very good tip especially to those new to
    the blogosphere. Simple but very precise information… Many thanks for sharing this one.
    A must read post!

  • I constantly emailed this web site post page to all
    my contacts, for the reason that if like to read
    it then my contacts will too.

  • An outstanding share! I have just forwarded this onto a friend who had
    been conducting a little homework on this.
    And he actually bought me breakfast because I stumbled upon it for him.
    .. lol. So let me reword this.... Thanks for the meal!!
    But yeah, thanx for spending the time to discuss this matter here on your internet site.

  • I am in fact glad to read this website posts which consists of tons of helpful facts, thanks for providing such information.

  • What i do not realize is in fact how you are
    no longer actually much more well-preferred than you might be now.
    You're so intelligent. You realize therefore significantly in relation to this subject, produced me for my part consider it from a lot of numerous angles. Its like women and men are not interested until it's something
    to accomplish with Lady gaga! Your individual stuffs excellent.
    All the time deal with it up!

  • If you wish for to increase your experience just keep visiting this website and be updated
    with the newest news update posted here.

  • There's certainly a lot to learn about this topic. I really like all of the points you made.

  • Wow that was odd. I just wrote an extremely long comment but after I clicked submit my comment didn't show up. Grrrr... well I'm
    not writing all that over again. Anyways, just wanted
    to say fantastic blog!

  • Asking questions are actually pleasant thing if you
    are not understanding something entirely, however this paragraph
    offers good understanding even.

  • This design is wicked! You certainly know how to
    keep a reader amused. Between your wit and your videos,
    I was almost moved to start my own blog (well, almost...HaHa!
    ) Fantastic job. I really loved what you had to say, and more than that, how you presented it.
    Too cool!

  • I am really thankful to the holder of this website who has shared this impressive piece of
    writing at at this time.

  • Hi to all, it's truly a nice for me to visit this web page, it consists of useful Information.

  • I am regular visitor, how are you everybody? This post posted at this site is genuinely
    fastidious.

  • What's up every one, here every person is sharing such know-how, therefore it's pleasant to read this
    webpage, and I used to visit this blog daily.

  • Having read this I thought it was really enlightening.
    I appreciate you spending some time and energy to
    put this informative article together. I once again find myself personally
    spending a lot of time both reading and posting comments.
    But so what, it was still worth it!

  • Thanks for finally writing about >Extend ASP.NET MVC for Asynchronous
    Action - Happy Coding <Liked it!

  • I am really glad to glance at this weblog posts which consists of lots of valuable facts, thanks for providing these statistics.

  • Great info. Lucky me I came across your website by accident (stumbleupon).
    I have saved as a favorite for later!

  • Hello there! I just want to give you a huge thumbs up for your great information
    you've got here on this post. I will be coming back to your web site for more soon.

  • I seriously love your blog.. Very nice colors

  • Hi there, I log on to your blogs daily. Your humoristic style is witty, keep it up!

  • We are a bunch of volunteers and opening a brand new scheme in our community.

    Your site provided us with helpful info to work on. You have done an impressive activity
    and our whole community will probably be grateful to you.

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • What a data of un-ambiguity and preserveness of valuable know-how concerning unpredicted emotions.

  • I am in fact grateful to the owner of this web page who has
    shared this wonderful piece of writing at here.

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • This is my first time pay a visit at here and i am truly
    happy to read everthing at alone place.

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • Hello Dear, are you in fact visiting this web page on a regular
    basis, if so afterward you will without doubt obtain fastidious know-how.

  • Good day! I know this is somewhat off topic but I was wondering which blog
    platform are you using for this website? I'm getting sick and tired of Wordpress because I've had problems with
    hackers and I'm looking at alternatives for another platform. I would be awesome if you could point me in the direction of a good platform.

  • Sweet blog! I found it while searching on Yahoo News.
    Do you have any tips on how to get listed in Yahoo News?
    I've been trying for a while but I never seem to get there! Thanks

  • I was wondering if you ever thought of changing the structure of your blog?
    Its very well written; I love what youve got to say.

    But maybe you could a little more in the way of content so people could connect with it better.
    Youve got an awful lot of text for only having 1
    or two pictures. Maybe you could space it out better?

  • You are so interesting! I do not think I've read through something like this before. So nice to discover somebody with a few unique thoughts on this issue. Seriously.. thanks for starting this up. This site is one thing that is needed on the web, someone with some originality!

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • I seriously love your blog.. Very nice colors

  • I'm extremely impressed with your writing skills as well as with the layout on your weblog. Is this a paid theme or did you modify it yourself? Anyway keep up the nice quality writing, it is rare to see a great blog like this one nowadays.

  • I absolutely love your blog and find almost all of your post's to be just what I'm looking for.
    Does one offer guest writers to write content in your case?
    I wouldn't mind composing a post or elaborating on a lot of the subjects you write about here. Again, awesome website!

  • Just want to say your article is as astonishing. The clarity in your post is just excellent and i could assume you're an expert on this subject. Fine with your permission let me to grab your feed to keep updated with forthcoming post. Thanks a million and please continue the enjoyable work.

  • This site was... how do you say it? Relevant!
    ! Finally I've found something that helped me. Appreciate it!

  • Hello, its fastidious post on the topic of media print,
    we all know media is a great source of information.

  • I'm really enjoying the design and layout of your blog. It's a very easy on the eyes
    which makes it much more pleasant for me to come here and visit more often.
    Did you hire out a designer to create your theme? Fantastic work!

  • You could certainly see your enthusiasm within the work you write.
    The sector hopes for more passionate writers like you who are not afraid to mention how they believe.
    At all times follow your heart.

  • Excellent post but I was wondering if you could write a litte more on this subject?
    I'd be very thankful if you could elaborate a little bit further. Many thanks!

  • I'm extremely inspired along with your writing talents and also with the layout in your weblog. Is this a paid topic or did you customize it yourself? Either way stay up the nice quality writing, it is rare to see a nice weblog like this one nowadays..

  • Nice respond in return of this matter with real arguments and explaining
    all regarding that.

  • I every time emailed this weblog post page to all my contacts, for the
    reason that if like to read it next my friends will
    too.

  • At this moment I am going to do my breakfast, after having my
    breakfast coming again to read additional news.

  • I have read so many posts on the topic of the blogger
    lovers except this piece of writing is in fact a fastidious paragraph, keep it up.

  • I am really impressed with your writing skills and also with the layout
    on your weblog. Is this a paid theme or did you modify it yourself?
    Anyway keep up the excellent quality writing, it's rare to see a nice blog like this one today.

  • Hi to all, how is everything, I think every one is getting more from this web page, and your views are good in support of new people.

  • It's an awesome paragraph in favor of all the online people; they will take advantage from it I am sure.

  • It's awesome to visit this site and reading the views of all mates concerning this piece of writing, while I am also keen of getting knowledge.

  • Just want to say your article is as astounding. The clearness to your post is simply great and i could suppose you are
    an expert in this subject. Fine with your permission let me to grab
    your feed to stay up to date with coming near near post.

    Thanks one million and please carry on the rewarding
    work.

  • As the admin of this site is working, no uncertainty very shortly
    it will be well-known, due to its feature contents.

  • Since the admin of this website is working, no uncertainty very rapidly it will be well-known, due to its feature contents.

  • Write more, thats all I have to say. Literally, it seems
    as though you relied on the video to make your point.
    You obviously know what youre talking about, why
    waste your intelligence on just posting videos to your site when you could be giving us something informative to read?

  • This page definitely has all the information
    and facts I wanted concerning this subject and didn't know who to ask.

  • If you want to obtain much from this paragraph then you have to apply these strategies to your won blog.

  • Post writing is also a fun, if you be acquainted with then you can write if not
    it is complex to write.

  • What's up mates, its wonderful post regarding cultureand completely explained, keep it up all the time.

  • Hi there, the whole thing is going fine here and ofcourse every
    one is sharing facts, that's truly excellent, keep up writing.

  • Hi mates, its great article regarding tutoringand fully
    defined, keep it up all the time.

  • Hi, after reading this amazing piece of writing i am too delighted to share my
    know-how here with mates.

  • If you would like to get much from this post then you have to apply these strategies to your won blog.

Comments have been disabled for this content.