With the exception of when using Modernizr I always try to adhere to the web performance rules of css at the top of the page and javascript at the bottom. The razor @RenderSection(string name, bool required) helper class and the @Section keyword certainly helps with that goal within view pages.
I recently came across this problem in a project for partial views. For instance if I create a DateTime.cshtml to represent an EditorTemplate for DateTime properties on View Models. I can't just inject the relevant scripts for the jQuery UI Date picker extension using the @section Scripts method like you can do within normal views.
So how can a developer adhere to best practices for stylesheets and script files? By using an extension method.
Let's start with adding a couple of extension methods, Resource and RenderResources as described below.
As you can see the Resource method takes a signature of Func<object, dynamic> template which simply can be @ so when template is executed the script tag is written into the page. The other parameter type is the type of resource being rendered "css" or "js", I've created a static helper class to remove the magic string.
In the layout:
In the partial :
I haven't tested with Asp .net bundle as yet but with little or no tweaking that should work as well.
Friday, October 26, 2012
Monday, July 9, 2012
Why isn't this test passing? Must be FakeItEasy's fault.
This is a cautionary tale. Before trawling through the different communities trying to work out why something isn't working and step back and look at your code with a bit of common sense.
I had this code in my WorkoutController.
public ActionResult Edit(Guid id) {
WorkoutViewModel workout = _workoutService.FindById(id);
if(Request.IsAjaxRequest()) {
return PartialView("_WorkoutEditView", workout);
}
return View("Edit", workout);
}
A part of the Action was being tested by this Unit Test.
[Test]
public void EditCallsFindById() {
Guid newGuid = Guid.NewGuid();
_controller.Edit(newGuid);
A.CallTo(_workoutService.FindById(Guid.Empty)).WithAnyArguments().MustHaveHappened(Repeated.Exactly.Once); }
Every time I ran the test it failed and obviously continued to fail. It was driving me nuts. I start looking at the FakeItEasy wiki and started looking at StackOverflow.
I left the problem overnight and came back to it this morning. When I look at the test first thing it hit me. The signature of A.CallTo() that I was looking for was
Hence when I changed my test to:
That is I need to added the lambda expression.
The moral to this story is always cast a critical eye over your code before really getting into troubleshooting mode.
I had this code in my WorkoutController.
public ActionResult Edit(Guid id) {
WorkoutViewModel workout = _workoutService.FindById(id);
if(Request.IsAjaxRequest()) {
return PartialView("_WorkoutEditView", workout);
}
return View("Edit", workout);
}
A part of the Action was being tested by this Unit Test.
[Test]
public void EditCallsFindById() {
Guid newGuid = Guid.NewGuid();
_controller.Edit(newGuid);
A.CallTo(_workoutService.FindById(Guid.Empty)).WithAnyArguments().MustHaveHappened(Repeated.Exactly.Once); }
Every time I ran the test it failed and obviously continued to fail. It was driving me nuts. I start looking at the FakeItEasy wiki and started looking at StackOverflow.
I left the problem overnight and came back to it this morning. When I look at the test first thing it hit me. The signature of A.CallTo() that I was looking for was
public static IReturnValueArgumentValidationConfiguration CallTo(Expression<Func> callSpecification)
Hence when I changed my test to:
Code Snippet
[Test] public void EditCallsFindById() {
Guid newGuid = Guid.NewGuid();
_controller.Edit(newGuid);
A.CallTo(()=>_workoutService.FindById(Guid.Empty)).WithAnyArguments().MustHaveHappened(Repeated.Exactly.Once);
}
Guid newGuid = Guid.NewGuid();
_controller.Edit(newGuid);
A.CallTo(()=>_workoutService.FindById(Guid.Empty)).WithAnyArguments().MustHaveHappened(Repeated.Exactly.Once);
}
That is I need to added the lambda expression.
The moral to this story is always cast a critical eye over your code before really getting into troubleshooting mode.
Friday, March 9, 2012
WebApi - An Example with Database Persistance
As discussed in my last post the new Web Api feature which is packaged with the MVC4 beta rollout allows for the use of Inversion Of Control (Dependency Injection) similar to that of MVC3.
In this post I am going to step through a "real life" Web Api project called "Healthy Muscle Web", which will allow consumers of the API to interact with a set of data relating to workouts. Whether the consumers be mobile applications (iPhone, Android), mashups or other web platforms (Ruby, php, AspNet MVC).
Let's start with a data model
For the persistence I will be using NHibernate. Now onto the controller implementation. From my understanding in REST, there are 4 primary verbs that we are concerned with:
For some methods I am just returning an entity, in this case Workout, and in other instances I am returning a hopefully more informational HttpResponseMessage. For example, in the case of the Post of a new entity, I need to tell the REST Client the new location of the newly added product in the header. In other actions I am also throwing a HttpResponseException if the resource requested is not found as per the Get method.
The routing configuration in the Global.asax look like
Looking at the WorkoutController there is a dependency on the interface IUnitOfWork
which has a concrete implementation of
Nothing too out there at the moment (nor will there be)
I have two Structure Map Registries
and
To bootstrap the IOC configuration I have a class
The Global.asax is as follows
The StructureMap configuration along with the Dependency Resolver (as discussed in my last post) allows for dependency injection.
You can see the full implementation at GitHub.com
In this post I am going to step through a "real life" Web Api project called "Healthy Muscle Web", which will allow consumers of the API to interact with a set of data relating to workouts. Whether the consumers be mobile applications (iPhone, Android), mashups or other web platforms (Ruby, php, AspNet MVC).
Let's start with a data model
Code Snippet
public class Workout
{
public virtual Guid Id { get; set; }
public virtual string DateTime { get; set; }
public virtual string WorkoutName { get; set; }
}
{
public virtual Guid Id { get; set; }
public virtual string DateTime { get; set; }
public virtual string WorkoutName { get; set; }
}
For the persistence I will be using NHibernate. Now onto the controller implementation. From my understanding in REST, there are 4 primary verbs that we are concerned with:
- POST: Create a new entity
- PUT: Update an existing entity
- GET: Retrieve a specified entity
- DELETE: Delete a specified entity
Code Snippet
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using NHibernate.Linq;
using WebApi.Data.Configuration;
using WebApi.Data.Models;
namespace WebApi.Controllers
{
public class WorkoutController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public WorkoutController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// GET /api/workouts
[HttpGet]
public IEnumerable<Workout> Get()
{
return _unitOfWork.CurrentSession.Query<Workout>().AsEnumerable();
}
// GET /api/workout/5
[HttpGet]
public Workout Get(Guid id)
{
var workout = _unitOfWork.CurrentSession.Query<Workout>().SingleOrDefault(x => x.Id == id);
//If entity expected does not exist return 404.
if (workout == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
return workout;
}
// POST /api/workouts
[HttpPost]
public HttpResponseMessage<Workout> Post(Workout workout)
{
var id = _unitOfWork.CurrentSession.Save(workout);
_unitOfWork.Commit();
var response = new HttpResponseMessage<Workout>
(workout, HttpStatusCode.Created);
response.Headers.Location = new Uri(Request.RequestUri,
Url.Route(null, new { id }));
return response;
}
// PUT /api/workouts
[HttpPut]
public Workout Put(Workout workout)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().SingleOrDefault(x => x.Id == workout.Id);
//check to ensure update can occur
if(existingWorkout==null)
throw new HttpResponseException(HttpStatusCode.NotFound);
//merge detached entity into session
_unitOfWork.CurrentSession.Merge(workout);
_unitOfWork.Commit();
return workout;
}
// DELETE /api/workouts/5
[HttpDelete]
public HttpResponseMessage Delete(Guid id)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().SingleOrDefault(x => x.Id == id);
//check to ensure delete can occur
if(existingWorkout==null)
return new HttpResponseMessage(HttpStatusCode.NoContent);
_unitOfWork.CurrentSession.Delete(existingWorkout);
_unitOfWork.Commit();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using NHibernate.Linq;
using WebApi.Data.Configuration;
using WebApi.Data.Models;
namespace WebApi.Controllers
{
public class WorkoutController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public WorkoutController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// GET /api/workouts
[HttpGet]
public IEnumerable<Workout> Get()
{
return _unitOfWork.CurrentSession.Query<Workout>().AsEnumerable();
}
// GET /api/workout/5
[HttpGet]
public Workout Get(Guid id)
{
var workout = _unitOfWork.CurrentSession.Query<Workout>().SingleOrDefault(x => x.Id == id);
//If entity expected does not exist return 404.
if (workout == null)
throw new HttpResponseException(HttpStatusCode.NotFound);
return workout;
}
// POST /api/workouts
[HttpPost]
public HttpResponseMessage<Workout> Post(Workout workout)
{
var id = _unitOfWork.CurrentSession.Save(workout);
_unitOfWork.Commit();
var response = new HttpResponseMessage<Workout>
(workout, HttpStatusCode.Created);
response.Headers.Location = new Uri(Request.RequestUri,
Url.Route(null, new { id }));
return response;
}
// PUT /api/workouts
[HttpPut]
public Workout Put(Workout workout)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().SingleOrDefault(x => x.Id == workout.Id);
//check to ensure update can occur
if(existingWorkout==null)
throw new HttpResponseException(HttpStatusCode.NotFound);
//merge detached entity into session
_unitOfWork.CurrentSession.Merge(workout);
_unitOfWork.Commit();
return workout;
}
// DELETE /api/workouts/5
[HttpDelete]
public HttpResponseMessage Delete(Guid id)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().SingleOrDefault(x => x.Id == id);
//check to ensure delete can occur
if(existingWorkout==null)
return new HttpResponseMessage(HttpStatusCode.NoContent);
_unitOfWork.CurrentSession.Delete(existingWorkout);
_unitOfWork.Commit();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
For some methods I am just returning an entity, in this case Workout, and in other instances I am returning a hopefully more informational HttpResponseMessage. For example, in the case of the Post of a new entity, I need to tell the REST Client the new location of the newly added product in the header. In other actions I am also throwing a HttpResponseException if the resource requested is not found as per the Get method.
The routing configuration in the Global.asax look like
Code Snippet
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Looking at the WorkoutController there is a dependency on the interface IUnitOfWork
Code Snippet
public interface IUnitOfWork : IDisposable
{
ISession CurrentSession { get; }
void Commit();
void Rollback();
}
{
ISession CurrentSession { get; }
void Commit();
void Rollback();
}
which has a concrete implementation of
Code Snippet
public class UnitOfWork : IUnitOfWork
{
private readonly ISessionFactory _sessionFactory;
private readonly ITransaction _transaction;
public UnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
CurrentSession = _sessionFactory.OpenSession();
_transaction = CurrentSession.BeginTransaction();
}
public ISession CurrentSession { get; private set; }
public void Dispose()
{
CurrentSession.Close();
CurrentSession = null;
}
public void Commit()
{
if (_transaction.IsActive)
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
}
{
private readonly ISessionFactory _sessionFactory;
private readonly ITransaction _transaction;
public UnitOfWork(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
CurrentSession = _sessionFactory.OpenSession();
_transaction = CurrentSession.BeginTransaction();
}
public ISession CurrentSession { get; private set; }
public void Dispose()
{
CurrentSession.Close();
CurrentSession = null;
}
public void Commit()
{
if (_transaction.IsActive)
_transaction.Commit();
}
public void Rollback()
{
_transaction.Rollback();
}
}
Nothing too out there at the moment (nor will there be)
I have two Structure Map Registries
Code Snippet
public class NHibernateRegistry : Registry
{
public NHibernateRegistry()
{
var cfg = new NHibernate.Cfg.Configuration()
.SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.Dialect, typeof(MsSqlCe40Dialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionDriver, typeof(SqlServerCeDriver).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionStringName, "WebApi")
.AddAssembly(typeof(Workout).Assembly);
var sessionFactory = cfg.BuildSessionFactory();
For<NHibernate.Cfg.Configuration>().Singleton().Use(cfg);
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<ISession>().HybridHttpOrThreadLocalScoped()
.Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());
For<IUnitOfWork>().HybridHttpOrThreadLocalScoped()
.Use<UnitOfWork>();
}
}
{
public NHibernateRegistry()
{
var cfg = new NHibernate.Cfg.Configuration()
.SetProperty(Environment.ReleaseConnections, "on_close")
.SetProperty(Environment.Dialect, typeof(MsSqlCe40Dialect).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionDriver, typeof(SqlServerCeDriver).AssemblyQualifiedName)
.SetProperty(Environment.ConnectionStringName, "WebApi")
.AddAssembly(typeof(Workout).Assembly);
var sessionFactory = cfg.BuildSessionFactory();
For<NHibernate.Cfg.Configuration>().Singleton().Use(cfg);
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<ISession>().HybridHttpOrThreadLocalScoped()
.Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());
For<IUnitOfWork>().HybridHttpOrThreadLocalScoped()
.Use<UnitOfWork>();
}
}
and
Code Snippet
public class DomainRegistry : Registry
{
public DomainRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.WithDefaultConventions();
});
}
}
{
public DomainRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.WithDefaultConventions();
});
}
}
To bootstrap the IOC configuration I have a class
Code Snippet
public class DependencyRegistrar
{
protected static bool DependenciesRegistered;
private static void RegisterDependencies()
{
ObjectFactory.Initialize(x => x.Scan(y =>
{
y.AssemblyContainingType<DomainRegistry>();
y.AssemblyContainingType<NHibernateRegistry>();
y.LookForRegistries();
}));
}
private static readonly object Sync = new object();
public void ConfigureOnStartup()
{
RegisterDependencies();
}
public static bool Registered(Type type)
{
EnsureDependenciesRegistered();
return ObjectFactory.GetInstance(type) != null;
}
public static void EnsureDependenciesRegistered()
{
if (DependenciesRegistered) return;
lock (Sync)
{
if (DependenciesRegistered) return;
RegisterDependencies();
DependenciesRegistered = true;
}
}
}
{
protected static bool DependenciesRegistered;
private static void RegisterDependencies()
{
ObjectFactory.Initialize(x => x.Scan(y =>
{
y.AssemblyContainingType<DomainRegistry>();
y.AssemblyContainingType<NHibernateRegistry>();
y.LookForRegistries();
}));
}
private static readonly object Sync = new object();
public void ConfigureOnStartup()
{
RegisterDependencies();
}
public static bool Registered(Type type)
{
EnsureDependenciesRegistered();
return ObjectFactory.GetInstance(type) != null;
}
public static void EnsureDependenciesRegistered()
{
if (DependenciesRegistered) return;
lock (Sync)
{
if (DependenciesRegistered) return;
RegisterDependencies();
DependenciesRegistered = true;
}
}
}
The Global.asax is as follows
Code Snippet
protected void Application_Start()
{
new DependencyRegistrar().ConfigureOnStartup();
var container = ObjectFactory.Container;
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
}
{
new DependencyRegistrar().ConfigureOnStartup();
var container = ObjectFactory.Container;
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
}
EDIT: As Rob pointed out in the comments I'm not releasing the instances of IUnitOfWork so the disposing method is not being called, which could have an impact on the memory. Thanks Rob for the pick up. I've added the below code in the Global.asax which is called on the Appication_EndRequest event.
protected void Application_EndRequest(object sender, EventArgs e) {
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
The StructureMap configuration along with the Dependency Resolver (as discussed in my last post) allows for dependency injection.
You can see the full implementation at GitHub.com
Labels:
c#,
mvc 4,
mvc4,
programming,
structuremap,
web api,
web platforms
Monday, February 27, 2012
Using StructureMap with Web API
To inject dependencies into your ASP.NET Web API controller we need to use the Web API dependency resolver. It is similar to the MVC 3 IDependencyResolver interface definition.
In the blog post I'll create a custom dependency resolver using StructureMap.
Firstly we need to create a class that inherits from IDependencyResolver which resides in the namespace System.Web.Http.Services.
To register this with Web Api we use the Global.asax file with the Application_Start method insert the following code.
so that the Application_Start method looks like
Now we can have controllers that use dependency injection.
Happy Api creating.
In the blog post I'll create a custom dependency resolver using StructureMap.
Firstly we need to create a class that inherits from IDependencyResolver which resides in the namespace System.Web.Http.Services.
Code Snippet
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Services;
using StructureMap;
namespace WebApi.Dependency
{
public class StructureMapDependencyResolver:IDependencyResolver
{
public StructureMapDependencyResolver(IContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (serviceType.IsAbstract || serviceType.IsInterface)
return _container.TryGetInstance(serviceType);
return _container.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances<object>().Where(s => s.GetType() == serviceType);
}
private readonly IContainer _container;
}
}
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Services;
using StructureMap;
namespace WebApi.Dependency
{
public class StructureMapDependencyResolver:IDependencyResolver
{
public StructureMapDependencyResolver(IContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (serviceType.IsAbstract || serviceType.IsInterface)
return _container.TryGetInstance(serviceType);
return _container.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances<object>().Where(s => s.GetType() == serviceType);
}
private readonly IContainer _container;
}
}
To register this with Web Api we use the Global.asax file with the Application_Start method insert the following code.
Code Snippet
var container = ObjectFactory.Container;
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));
so that the Application_Start method looks like
Code Snippet
protected void Application_Start()
{
var container = ObjectFactory.Container;
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
}
{
var container = ObjectFactory.Container;
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
new StructureMapDependencyResolver(container));
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
}
Now we can have controllers that use dependency injection.
Code Snippet
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using NHibernate.Linq;
using WebApi.Data.Configuration;
using WebApi.Data.Models;
namespace WebApi.Controllers
{
public class WorkoutController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public WorkoutController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// GET /api/workouts
public IEnumerable<Workout> Get()
{
return _unitOfWork.CurrentSession.Query<Workout>().AsEnumerable();
}
// GET /api/workout/5
public Workout Get(Guid id)
{
return _unitOfWork.CurrentSession.Query<Workout>().FirstOrDefault(x => x.Id == id);
}
// POST /api/workouts
public HttpResponseMessage<Workout> Post(Workout workout)
{
var id = _unitOfWork.CurrentSession.Save(workout);
_unitOfWork.Commit();
var response = new HttpResponseMessage<Workout>
(workout, HttpStatusCode.Created);
response.Headers.Location = new Uri(Request.RequestUri,
Url.Route(null, new { id }));
return response;
}
// PUT /api/workouts/5
public Workout Put(Guid id, Workout workout)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().FirstOrDefault(x => x.Id == id);
if(existingWorkout==null)
throw new HttpResponseException(HttpStatusCode.NotFound);
_unitOfWork.CurrentSession.Update(workout);
_unitOfWork.Commit();
}
// DELETE /api/workouts/5
public HttpResponseMessage Delete(Guid id)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().FirstOrDefault(x => x.Id == id);
if(existingWorkout==null)
return new HttpResponseMessage(HttpStatusCode.NoContent);
_unitOfWork.CurrentSession.Delete(existingWorkout);
_unitOfWork.Commit();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using NHibernate.Linq;
using WebApi.Data.Configuration;
using WebApi.Data.Models;
namespace WebApi.Controllers
{
public class WorkoutController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public WorkoutController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// GET /api/workouts
public IEnumerable<Workout> Get()
{
return _unitOfWork.CurrentSession.Query<Workout>().AsEnumerable();
}
// GET /api/workout/5
public Workout Get(Guid id)
{
return _unitOfWork.CurrentSession.Query<Workout>().FirstOrDefault(x => x.Id == id);
}
// POST /api/workouts
public HttpResponseMessage<Workout> Post(Workout workout)
{
var id = _unitOfWork.CurrentSession.Save(workout);
_unitOfWork.Commit();
var response = new HttpResponseMessage<Workout>
(workout, HttpStatusCode.Created);
response.Headers.Location = new Uri(Request.RequestUri,
Url.Route(null, new { id }));
return response;
}
// PUT /api/workouts/5
public Workout Put(Guid id, Workout workout)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().FirstOrDefault(x => x.Id == id);
if(existingWorkout==null)
throw new HttpResponseException(HttpStatusCode.NotFound);
_unitOfWork.CurrentSession.Update(workout);
_unitOfWork.Commit();
}
// DELETE /api/workouts/5
public HttpResponseMessage Delete(Guid id)
{
var existingWorkout = _unitOfWork.CurrentSession.Query<Workout>().FirstOrDefault(x => x.Id == id);
if(existingWorkout==null)
return new HttpResponseMessage(HttpStatusCode.NoContent);
_unitOfWork.CurrentSession.Delete(existingWorkout);
_unitOfWork.Commit();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
Happy Api creating.
Friday, February 24, 2012
Using knockout.mapping Plugin with Asp .Net MVC – Part 1a
In part 1 of this series I explored knockoutjs and completed a simple display view in the UI.
A few people have spoken to me about the implementation and commentated on what they perceived as a leakage into the Separation of Concerns regarding how bindings are managed via the data-bind attributes. While the comments certainly led to some interesting debates, I thought how can I remove some of those Concerns away from the UI.
Some research and I came across Neil Kerkin's blog post Exploring TodoMVC and knockout.js with unobtrusive bindings where he discusses an approach using knockout.js supports custom binding providers that allow us to refactor the code to achieve a bit more separation.
I won't go too much into what I have done because Neil explains it a lot better than I could. Therefore I'll just post the newer code base that will be used for part 2.
The javascript which looked like the following:
becomes
/* File Created: February 5, 2;01;2 ;*/
(function (getworkouts, $, undefined) {
window.setUpBindings.SetCustomBindings();
//create view model and initialise with default data.
getworkouts.viewModel = function () {
this.workouts = ko.mapping.fromJS(
[],
{
key: function (workout) { return ko.utils.unwrapObservable(workout.Id); }
});
};
//use ajax call to populate the view model from the Action and also bootstraps knockout
getworkouts.getDataFromSource = function () {
$.ajax({
type: 'POST',
url: '/Home/GetWorkouts',
dataType: 'json',
success: function (data) {
var model = new getworkouts.viewModel();
model.workouts = ko.mapping.fromJS(data);
getworkouts.bindings = {
workouts: { foreach: model.workouts },
workoutsDateTime: function () { return { text: this.DateTime }; },
workoutsWorkoutName: function () { return { text: this.WorkoutName }; }
};
//set ko's current bindingProvider equal to our new binding provider
ko.bindingProvider.instance = new ko.customBindingProvider(getworkouts.bindings);
ko.applyBindings(model);
}
});
};
} (window.getworkouts = window.getworkouts || {}, jQuery));
$(document).ready(function () {
//bootstrap
getworkouts.getDataFromSource();
});
With an additional javascript snippet of
/* File Created: February 16, 2012 */
(function (setupBindings, $, undefined) {
setupBindings.SetCustomBindings = function() {
ko.customBindingProvider = function(bindingObject) {
this.bindingObject = bindingObject;
//determine if an element has any bindings
this.nodeHasBindings = function(node) {
return node.getAttribute ? node.getAttribute("data-class") : false;
};
//return the bindings given a node and the bindingContext
this.getBindings = function(node, bindingContext) {
var result = { };
var classes = node.getAttribute("data-class");
if (classes) {
classes = classes.split(' ');
//evaluate each class, build a single object to return
for (var i = 0, j = classes.length; i < j; i++) {
var bindingAccessor = this.bindingObject[classes[i]];
if (bindingAccessor) {
var binding = typeof bindingAccessor == "function" ? bindingAccessor.call(bindingContext.$data) : bindingAccessor;
ko.utils.extend(result, binding);
}
}
}
return result;
};
};
};
} (window.setUpBindings = window.setUpBindings || {}, jQuery));
The view then becomes
<tableclass="gridtable">
<thead>
<tr>
<th>
Date Of Workout
</th>
<th>
Name Of Workout
</th>
</tr>
</thead>
<tbody data-class="workouts">
<tr>
<td>
<span data-class="workoutsDateTime"></span>
</td>
<td>
<span data-class="workoutsWorkoutName"></span>
</td>
</tr>
</tbody>
</table>
Once again the code is available at Github.
I've rushed over the implementation mainly because I don't want to any credit away from Neil Kerkin's solution to the Separation of Concerns problem.
See you at part 2.
A few people have spoken to me about the implementation and commentated on what they perceived as a leakage into the Separation of Concerns regarding how bindings are managed via the data-bind attributes. While the comments certainly led to some interesting debates, I thought how can I remove some of those Concerns away from the UI.
Some research and I came across Neil Kerkin's blog post Exploring TodoMVC and knockout.js with unobtrusive bindings where he discusses an approach using knockout.js supports custom binding providers that allow us to refactor the code to achieve a bit more separation.
I won't go too much into what I have done because Neil explains it a lot better than I could. Therefore I'll just post the newer code base that will be used for part 2.
The javascript which looked like the following:
Code Snippet
(function (getworkouts, $, undefined) { //create view model and initialise with default data.
getworkouts.viewModel = function() {
this.workouts = ko.mapping.fromJS(
[],
{
key: function (workout) { return ko.utils.unwrapObservable(workout.Id); }
});
}; //use ajax call to populate the view model from the Action and also bootstraps knockout
getworkouts.getDataFromSource = function () {
$.ajax({
type: 'POST',
url: '/Home/GetWorkouts',
dataType: 'json',
success: function (data) {
var model = new getworkouts.viewModel();
model.workouts = ko.mapping.fromJS(data);
ko.applyBindings(model);
}
});
};
} (window.getworkouts = window.getworkouts || {}, jQuery));$(document).ready(function () {
//bootstrap call
getworkouts.getDataFromSource();
});
getworkouts.viewModel = function() {
this.workouts = ko.mapping.fromJS(
[],
{
key: function (workout) { return ko.utils.unwrapObservable(workout.Id); }
});
}; //use ajax call to populate the view model from the Action and also bootstraps knockout
getworkouts.getDataFromSource = function () {
$.ajax({
type: 'POST',
url: '/Home/GetWorkouts',
dataType: 'json',
success: function (data) {
var model = new getworkouts.viewModel();
model.workouts = ko.mapping.fromJS(data);
ko.applyBindings(model);
}
});
};
} (window.getworkouts = window.getworkouts || {}, jQuery));$(document).ready(function () {
//bootstrap call
getworkouts.getDataFromSource();
});
becomes
Code Snippet
/* File Created: February 5, 2;01;2 ;*/
(function (getworkouts, $, undefined) {
window.setUpBindings.SetCustomBindings();
//create view model and initialise with default data.
getworkouts.viewModel = function () {
this.workouts = ko.mapping.fromJS(
[],
{
key: function (workout) { return ko.utils.unwrapObservable(workout.Id); }
});
};
//use ajax call to populate the view model from the Action and also bootstraps knockout
getworkouts.getDataFromSource = function () {
$.ajax({
type: 'POST',
url: '/Home/GetWorkouts',
dataType: 'json',
success: function (data) {
var model = new getworkouts.viewModel();
model.workouts = ko.mapping.fromJS(data);
getworkouts.bindings = {
workouts: { foreach: model.workouts },
workoutsDateTime: function () { return { text: this.DateTime }; },
workoutsWorkoutName: function () { return { text: this.WorkoutName }; }
};
//set ko's current bindingProvider equal to our new binding provider
ko.bindingProvider.instance = new ko.customBindingProvider(getworkouts.bindings);
ko.applyBindings(model);
}
});
};
} (window.getworkouts = window.getworkouts || {}, jQuery));
$(document).ready(function () {
//bootstrap
getworkouts.getDataFromSource();
});
With an additional javascript snippet of
Code Snippet
/* File Created: February 16, 2012 */
(function (setupBindings, $, undefined) {
setupBindings.SetCustomBindings = function() {
ko.customBindingProvider = function(bindingObject) {
this.bindingObject = bindingObject;
//determine if an element has any bindings
this.nodeHasBindings = function(node) {
return node.getAttribute ? node.getAttribute("data-class") : false;
};
//return the bindings given a node and the bindingContext
this.getBindings = function(node, bindingContext) {
var result = { };
var classes = node.getAttribute("data-class");
if (classes) {
classes = classes.split(' ');
//evaluate each class, build a single object to return
for (var i = 0, j = classes.length; i < j; i++) {
var bindingAccessor = this.bindingObject[classes[i]];
if (bindingAccessor) {
var binding = typeof bindingAccessor == "function" ? bindingAccessor.call(bindingContext.$data) : bindingAccessor;
ko.utils.extend(result, binding);
}
}
}
return result;
};
};
};
} (window.setUpBindings = window.setUpBindings || {}, jQuery));
The view then becomes
Code Snippet
<tableclass="gridtable">
<thead>
<tr>
<th>
Date Of Workout
</th>
<th>
Name Of Workout
</th>
</tr>
</thead>
<tbody data-class="workouts">
<tr>
<td>
<span data-class="workoutsDateTime"></span>
</td>
<td>
<span data-class="workoutsWorkoutName"></span>
</td>
</tr>
</tbody>
</table>
Once again the code is available at Github.
I've rushed over the implementation mainly because I don't want to any credit away from Neil Kerkin's solution to the Separation of Concerns problem.
See you at part 2.
Labels:
asp net,
asp. net,
c#,
clientside,
knockoutjs,
mvc
Subscribe to:
Posts (Atom)