Skip to content

Instantly share code, notes, and snippets.

@ilyapalkin
Last active December 1, 2018 18:25
Show Gist options
  • Save ilyapalkin/c82904208bd529814713 to your computer and use it in GitHub Desktop.
Save ilyapalkin/c82904208bd529814713 to your computer and use it in GitHub Desktop.
"Can I refactor to Model View Query Handler?" answer
public class QueryFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
object query = filterContext.ActionParameters["query"];
Type queryType = query.GetType();
Type modelType = queryType.GetInterfaces()[0].GetGenericArguments()[0];
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, modelType);
// Here you should resolve your IQueryHandler<,> using IoC
// 'Service Locator' pattern is used as quick-and-dirty solution to show that code works.
IQueryHandler handler = ComponentLocator.GetComponent(handlerType) as IQueryHandler;
object model = handler.Handle(query);
filterContext.Controller.ViewData.Model = model;
}
}
[QueryFilter]
public class ConferenceController : Controller
{
public ActionResult Index(IndexQuery query)
{
return View();
}
public ViewResult Show(ShowQuery query)
{
return View();
}
public ActionResult Edit(EditQuery query)
{
return View();
}
}
/// <summary>
/// Generic query
/// </summary>
/// <typeparam name="TResponse">The type of the model to response.</typeparam>
public interface IQuery<out TResponse> { }
public class IndexModel
{
public string A { get; set; }
}
public class IndexQuery : IQuery<IndexModel>
{
public string EventName { get; set; }
}
public class ShowModel
{
public string B { get; set; }
}
public class ShowQuery : IQuery<ShowModel>
{
public string EventName { get; set; }
}
public class EditModel
{
public string C { get; set; }
}
public class EditQuery : IQuery<EditModel>
{
public string EventName { get; set; }
}
/// <summary>
/// All derived handlers can be refactored using generics. But in the real world handling logic can be completely different.
/// </summary>
/// <typeparam name="TQuery">The type of the query.</typeparam>
/// <typeparam name="TResponse">The type of the response.</typeparam>
public interface IQueryHandler<in TQuery, out TResponse> : IQueryHandler
where TQuery : IQuery<TResponse>
{
TResponse Handle(TQuery query);
}
/// <summary>
/// This interface is used in order to invoke 'Handle' for any query type.
/// </summary>
public interface IQueryHandler
{
object Handle(object query);
}
/// <summary>
/// Implements 'Handle' of 'IQueryHandler' interface explicitly to restrict its invocation.
/// </summary>
/// <typeparam name="TQuery">The type of the query.</typeparam>
/// <typeparam name="TResponse">The type of the response.</typeparam>
public abstract class QueryHandlerBase<TQuery, TResponse> : IQueryHandler<TQuery, TResponse>
where TQuery : IQuery<TResponse>
{
public abstract TResponse Handle(TQuery query);
object IQueryHandler.Handle(object query)
{
return Handle((TQuery)query);
}
}
public class IndexQueryHandler : QueryHandlerBase<IndexQuery, IndexModel>
{
private readonly ISession _session;
public IndexQueryHandler(ISession session)
{
_session = session;
}
public override IndexModel Handle(IndexQuery query)
{
var model = new IndexModel
{
A = query.GetType().Name
};
return model;
}
}
public class ShowQueryHandler : QueryHandlerBase<ShowQuery, ShowModel>
{
private readonly ISession _session;
public ShowQueryHandler(ISession session)
{
_session = session;
}
public override ShowModel Handle(ShowQuery query)
{
var model = new ShowModel
{
B = query.GetType().Name
};
return model;
}
}
public class EditQueryHandler : QueryHandlerBase<EditQuery, EditModel>
{
private readonly ISession _session;
public EditQueryHandler(ISession session)
{
_session = session;
}
public override EditModel Handle(EditQuery query)
{
var model = new EditModel
{
C = query.GetType().Name
};
return model;
}
}
#region ISession and FakeSession to show that dependencies can be injected
public interface ISession
{
}
public class FakeSession : ISession
{
}
#endregion
[QueryFilter]
public class ConferenceController : Controller
{
public ActionResult Index(IndexQuery query)
{
return View();
}
public ViewResult Show(ShowQuery query)
{
return View();
}
public ActionResult Edit(EditQuery query)
{
return View();
}
}
@model WebUI.Controllers.EditModel
@{
ViewBag.Title = Model.GetType();
}
<h2>@Model.C</h2>
public class MvcApplication : HttpApplication
{
private readonly IWindsorContainer container;
/// <summary>
/// Method executing on application start.
/// </summary>
protected void Application_Start()
{
XmlInterpreter xmlInterpreter = new XmlInterpreter(
new ConfigResource("castle"));
container = new WindsorContainer(xmlInterpreter);
container.Register(Component.For<ISession>().ImplementedBy<FakeSession>());
container.Register(
Classes.FromThisAssembly()
.BasedOn(typeof(IQueryHandler<,>))
.WithService.Base()
.LifestylePerWebRequest());
}
/// <summary>
/// Method executing on application end.
/// </summary>
protected void Application_End()
{
container.Dispose();
}
}
@model WebUI.Controllers.IndexModel
@{
ViewBag.Title = Model.GetType();
}
<h2>@Model.A</h2>
/// <summary>
/// Generic query
/// </summary>
/// <typeparam name="TResponse">The type of the model to response.</typeparam>
public interface IQuery<out TResponse> { }
public class QueryFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
object query = filterContext.ActionParameters["query"];
Type queryType = query.GetType();
Type modelType = queryType.GetInterfaces()[0].GetGenericArguments()[0];
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, modelType);
// Here you should resolve your IQueryHandler<,> using IoC
// 'Service Locator' pattern is used as quick-and-dirty solution to show that code works.
IQueryHandler handler = ComponentLocator.GetComponent(handlerType) as IQueryHandler;
object model = handler.Handle(query);
filterContext.Controller.ViewData.Model = model;
}
}
/// <summary>
/// All derived handlers can be refactored using generics. But in the real world handling logic can be completely different.
/// </summary>
/// <typeparam name="TQuery">The type of the query.</typeparam>
/// <typeparam name="TResponse">The type of the response.</typeparam>
public interface IQueryHandler<in TQuery, out TResponse> : IQueryHandler
where TQuery : IQuery<TResponse>
{
TResponse Handle(TQuery query);
}
/// <summary>
/// This interface is used in order to invoke 'Handle' for any query type.
/// </summary>
public interface IQueryHandler
{
object Handle(object query);
}
/// <summary>
/// Implements 'Handle' of 'IQueryHandler' interface explicitly to restrict its invocation.
/// </summary>
/// <typeparam name="TQuery">The type of the query.</typeparam>
/// <typeparam name="TResponse">The type of the response.</typeparam>
public abstract class QueryHandlerBase<TQuery, TResponse> : IQueryHandler<TQuery, TResponse>
where TQuery : IQuery<TResponse>
{
public abstract TResponse Handle(TQuery query);
object IQueryHandler.Handle(object query)
{
return Handle((TQuery)query);
}
}
@model WebUI.Controllers.ShowModel
@{
ViewBag.Title = Model.GetType();
}
<h2>@Model.B</h2>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment