.NET

Improve UX with SharePoint Client-side Rendering

0

The standard SharePoint rendered views generally score low for originality with limited points for user-experience. The following is an example of a standard list view, which is a table structure with text-based field values.

What if we add a little style to represent the Approval Status column? We can override the standard field control rendering and inject our own client-side logic. This can be accomplished using HTML and JavaScript technologies with the SharePoint 2013 client-side rendering feature. So…let’s look at a simple style enhancement to the above list view and Approval Status column.

This is a very simple change, which utilizes color (red, yellow and green) and a minor value change (Approved is now Live) to improve the view. At a glance, the user can associate the color with the status using the common traffic stop light palette. So…let’s look at the changes required to implement the rendering logic.

 First, we can create the rendering logic.

 Javascript |  copy code |? 
01
(function () {
02
    var fieldContext = {}; 
03
    fieldContext.Templates = {}; 
04
    fieldContext.Templates.Fields = { 
05
        "_ModerationStatus": { "View": approvalStatusFieldTemplate, "ViewForm": approvalStatusFieldTemplate }
06
    };
07
 
08
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(fieldContext); 
09
 
10
})(); 
11

This function contains the field context and registers the templates. The key is fields and _ModerationStatus, which is the internal field name for Approval Status. You can include templates for View, DisplayForm, EditForm and NewForm. In this example, the View and ViewForm templates are defined to call the approvalStatusFieldTemplate function.

 Javascript |  copy code |? 
01
function approvalStatusFieldTemplate(ctx) { 
02
 var approvalStatus = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];
03
 
04
    switch (approvalStatus) { 
05
        case "Draft": 
06
            return "<span style='color:red'><strong>" + approvalStatus + "</strong></span>"; 
07
            break; 
08
        case "Scheduled": 
09
            return "<span style='color:orange'><strong>" + approvalStatus + "</strong></span>"; 
10
            break; 
11
        case "Approved": 
12
            return "<span style='color:green'><strong>Live</strong></span>";
13
 }
14
 
15
 return approvalStatus; 
16
}
17

This function will return the injected mark-up for the target field. The first statement assigns the current field value to approvalStatus. Next, a switch statement returns a span element with the appropriate style color. The result is a colorized value based on the approval status.

The next example replaces the text value with an image, which also represents the approval status. The result is mark-up to render an image with the approval status value as a tooltip.

The following is the modified approvalStatusFieldTemplate function. In this example, we are borrowing a few SharePoint provided images.

 Javascript |  copy code |? 
01
function approvalStatusFieldTemplate(ctx) { 
02
 var approvalStatus = ctx.CurrentItem[ctx.CurrentFieldSchema.Name];
03
 var icon;
04
 
05
    switch (approvalStatus) { 
06
        case "Draft": 
07
            icon = "hlthwrn.png"; 
08
            break; 
09
        case "Scheduled": 
10
            icon =  "calendar.gif"; 
11
            break; 
12
        case "Approved":
13
            icon = "hlthsucc.png";
14
            break;
15
        default:
16
            icon = "hltherr.png";
17
    }
18
 
19
    return "<img src='/_layouts/15/images/" + icon + "' title='" + approvalStatus + "'/>";
20
}
21

The client-side rendering is also referred to as JSLink, since this is the new property that instructs the field rendering process to replace the result using the JavaScript contained in the JSLink resource. The following PowerShell script will assign the JSLink value to the JavaScript file containing our rendering logic for the Approval Status field.

 DOS |  copy code |? 
1
$web = Get-SPWeb http://myserver
2
$field = $web.Fields["Approval Status"]
3
$field.JSLink = "~layouts/15/ApprovalStatusViewDecorate.js"
4
$field.Update($true)
5

If you would like to explore client-side rendering for your sites then I would recommend additional tutorials. This should include handling multiple instances of the field on a page, which would cause issues without additional configuration. The following are a few helpful resources:

Good Examples

Good Overview

This new powerful feature will provide another tool to improve the user experience with endless possibilities. I hope this information provides a quick start for SharePoint 2013 Client-side rendering.

TFS Build Process Packaging

0

In previous posts, we discussed various techniques to tailor your Team Foundation Server (TFS) Build Process. The TFS Versioning article introduced a library, which enables you to set the assembly versions during the build. The TFS Merge post demonstrates the steps necessary to provide a merge process within the TFS Build Process Template. In this article, we explore the Build Process Parameters of the TFS Build Process Definition.

After you successfully compile and test, the final step is packaging and/or publishing the product for deployment. If your build process definition includes multiple source solutions then collecting the assets required to deploy is tricky. In most scenarios, you will move the packages and artifacts to a share or drop location. The versioning process provides a unique identifier, so you can properly label or tag the deployment release. If you create a share folder name with the generated version then you can catalog and track the deployment releases.

The MSBuild command-line provides a series of parameters instructing the build to perform one or more defined operations. The parameters will override any solution or project specific values, which is helpful to ensure the results are consistent for packaging and publishing.

The MSBuild Parameters is an Advanced section option, which provides the override instructions to the build process. The web projects are packaged to include the required assets to deploy, which appear as a “_PublishedWebsites” folder. A SharePoint Solution Package (.wsp) and SQL Server DAC Package (.dacpac) are other examples of deployment packages.

The following MSBuild parameters will ensure the above packages are created during the build process.

TFS Build Definition MSBuild Arguments

The OutputPath parameter is optional, but will move results to the bin folder. This includes the web, SharePoint and SQL Server deployment packages discussed previously. The WarningLevel parameter is set to zero, so all warning messages are suppressed.

I hope this information is helpful, so you can customize your build process and capture the deployment packages without writing significant custom logic.

ASP.NET MVC and Kendo UI Grid

0

A grid provides a common presentation to represent a collection of data, which is a table containing rows and columns. Although creating this experience is not difficult, the features to deliver a quality user experience will require a significant investment. The alternative is a widget library, which provides advanced features requiring minimal effort.

In this post, I will demonstrate a few features of the Kendo UI Grid, which is one of many widgets included in the Kendo UI products. After several years of experience with Telerik products, the next generation Kendo UI is HTML5 and CSS3. I also elected to include the Kendo UI Complete, which packages convenient server extensions for the JavaScript framework. You can learn more about the products and better understand the licensing, which is a huge obstacle for many budget conscious organization. And yes…many alternatives exist including the JQuery.UI library.

Kendo UI provides a handy Visual Studio plugin, which can convert and prepare projects for Kendo UI. I converted an existing ASP.NET MVC4 project, which saves several manual steps. I was also able to select one of the Kendo themes, which will be applied to all Kendo widgets.

You can read several of my previous posts, which discuss design patterns to build enterprise applications. In this article, my goal is to provide a quick start for the Kendo UI Grid implementation. So…I will not create a fully functioning repository, but instead a static memory based to provide the basics for the demonstration.

I will modify the standard ASP.NET MVC4 starter project and add a new menu option called Contacts. This will require a controller, view model and service - all the basics. We’ll start with a simple read-only grid, which will display the current contacts with paging, filtering and sorting features. The following is the read-only Contacts grid.

Contacts Read-Only Grid

The grid is a table containing the Name, Mode of Contact, Phone and Email columns. The sort, filter and paging features are enabled, so the user can interact with the grid and data using several helpful options. So…let’s look at the view containing the grid configuration.

 C# |  copy code |? 
01
@using MvcKendoUIGrid.Models
02
 
03
@(Html.Kendo().Grid<Contact>()
04
      .Name("contactGrid")
05
      .Columns(columns =>
06
 
07
          {
08
               columns.Bound(c => c.Name).Title("Name").Width(150);
09
              columns.Bound(c => c.PreferredContactMode).Title("Mode of Contact").Width(150);
10
              columns.Bound(c => c.Phone).Title("Phone").Width(120);
11
              columns.Bound(c => c.Email).Title("Email").Width(120);
12
          })
13
      .Pageable()
14
      .Filterable()
15
      .Sortable()
16
      .Scrollable()
17
      .AutoBind(true)
18
      .HtmlAttributes(new {style = "height:250px;"})
19
      .DataSource(dataSource => dataSource
20
                                    .Ajax()
21
                                    .ServerOperation(false)
22
                                    .Model(model =>
23
                                        {
24
                                            model.Id(c => c.Id);
25
                                            model.Field(c => c.PreferredContactMode).Editable(true).DefaultValue(new ContactMode {Id = 0, Name = string.Empty});
26
                                        })
27
                                    .Read(read => read.Action("Read", "Contact"))
28
)      )

The Pageable, Sortable, Filterable and Scrollable provide the advanced grid features with just one line of code. The DataSource defines the data and actions to read. The model is the Contact, so the fields and id are defined. The Read action is an ajax call to the Contact controller and Read method, which will return a collection of Contact objects. The Columns define the layout of columns, headers and binding to the model.

The Contact model contains the Id, NamePhone and Email properties. It also includes the ContactMode, which is a complex object including an Id and Name. We’ll see more on the complex object in the next section.

 C# |  copy code |? 
01
using System.Collections.Generic;
02
using System.Web.Mvc;
03
using Kendo.Mvc.Extensions;
04
using Kendo.Mvc.UI;
05
using MvcKendoUIGrid.Models;
06
using MvcKendoUIGrid.Services;
07
 
08
namespace MvcKendoUIGrid.Controllers
09
{
10
    /// <summary>
11
    /// Contact controller providing support for views
12
    /// </summary>
13
    public class ContactController : Controller
14
    {
15
        private readonly IContactService _service;
16
 
17
        /// <summary>
18
        /// Default Constructor
19
        /// </summary>
20
        public ContactController()
21
        {
22
            _service = new ContactService();
23
        }
24
 
25
        /// <summary>
26
        /// Default action to render Contact grid
27
        /// </summary>
28
        /// <returns></returns>
29
        public ActionResult Index()
30
        {
31
            TempData["ContactMode"] = new List<ContactMode>
32
                {
33
                    new ContactMode {Id = 1, Name = "Phone"},
34
                    new ContactMode {Id = 2, Name = "Email"}
35
                };
36
 
37
            return View();
38
        }
39
 
40
        /// <summary>
41
        ///     Read action to populate grid
42
        /// </summary>
43
        /// <param name="dsRequest">Kendo Datasource Request</param>
44
        /// <returns>ActionResult</returns>
45
        [HttpPost]
46
        public ActionResult Read([DataSourceRequest] DataSourceRequest dsRequest)
47
        {
48
            try
49
            {
50
                return Json(_service.GetAllContacts().ToDataSourceResult(dsRequest));
51
            }
52
            catch
53
            {
54
                ModelState.AddModelError("Contacts", "Unable to load contacts.");
55
                return Json(ModelState.ToDataSourceResult());
56
            }
57
        }
58

The Index action is called on the initially, which will save the possible ContactMode selections as TempData. The Read action invokes the service GetAllContacts method to return a collection of Contacts.

The next step is providing the ability to edit the contacts. The following is the revised view, which includes the changes to enable editing.

 C# |  copy code |? 
01
@using MvcKendoUIGrid.Models
02
 
03
@(Html.Kendo().Grid<Contact>()
04
      .Name("contactGrid")
05
      .Columns(columns =>
06
 
07
          {
08
              columns.Command(command =>
09
                  {
10
                      command.Edit();
11
                   }).Width(180);
12
              columns.Bound(c => c.Name).Title("Name").Width(150);
13
              columns.Bound(c => c.PreferredContactMode).Title("Mode of Contact").Width(150);
14
              columns.Bound(c => c.Phone).Title("Phone").Width(120);
15
              columns.Bound(c => c.Email).Title("Email").Width(120);
16
          })
17
      .Pageable()
18
      .Filterable()
19
      .Sortable()
20
      .Scrollable()
21
      .AutoBind(true)
22
      .HtmlAttributes(new {style = "height:250px;"})
23
      .Editable(ed => ed.Mode(GridEditMode.InLine))
24
      .DataSource(dataSource => dataSource
25
                                    .Ajax()
26
                                    .ServerOperation(false)
27
                                    .Model(model =>
28
                                        {
29
                                            model.Id(c => c.Id);
30
                                            model.Field(c => c.PreferredContactMode).Editable(true).DefaultValue(new ContactMode {Id = 0, Name = string.Empty});
31
                                        })
32
                                    .Read(read => read.Action("Read", "Contact"))
33
                                    .Update(update => update.Action("Edit", "Contact"))
34
      )
35
      )

The DataSource now includes the Update, which calls the Contact controller Edit method. The Editable supports inline and popup edit modes, so we will select inline to allow editing within the grid. We also add a column command, which injects the edit button in the first column. We also add the Edit method to the controller, which is responsible for calling the service to update the contract.

 C# |  copy code |? 
01
        /// <summary>
02
        /// Edit action to update existing contact
03
        /// </summary>
04
        /// <param name="dsRequest">Kendo Datasource Request</param>
05
        /// <param name="viewmodel">Contact model containing values to update</param>
06
        /// <returns>ActionResult</returns>
07
        [HttpPost]
08
        public ActionResult Edit([DataSourceRequest] DataSourceRequest dsRequest, Contact viewmodel)
09
        {
10
            try
11
            {
12
                if (ModelState.IsValid && viewmodel != null)
13
                    _service.Update(viewmodel);
14
                else //model error
15
                    ModelState.AddModelError("Contacts", "Unable to edit contact - not a valid contact.");
16
            }
17
            catch
18
            {
19
                ModelState.AddModelError("Contacts", "Unable to edit contact.");
20
            }
21
 
22
            return Json(ModelState.ToDataSourceResult());
23
        }
24

If we test the changes then we see the addition of the edit button. Clicking the edit button will allow the editing of the selected row.

Contact Grid Edit

You will notice the Mode of Contact column displays the Id and Name textbox, which is not the best experience for selecting the contact mode.

Contact Grid Edit Complex Object

Since ContactMode is a complex object, the editor includes the Id and Name. We would like to provide a dropdown list of available contact modes, so the user must select a valid option. We previously saved a collection of ContactMode objects representing the valid options in the TempData, so this will be the dropdown list source. The solution is creating a new editor for the ContactMode type, so when the correct editor is presented. The following is the ContactMode editor, which should be added to the Shared Editor Templates folder.

 C# |  copy code |? 
1
@model MvcKendoUIGrid.Models.ContactMode
2
@(Html.Kendo().DropDownList()
3
      .Name("PreferredContactMode")
4
      .BindTo((System.Collections.IEnumerable)TempData["ContactMode"])
5
      .OptionLabel("Select a Mode")
6
      .DataTextField("Name")
7
      .DataValueField("Id")
8
      .HtmlAttributes(new { required = true })
9
      )

We are using the Kendo UI DropDownList widget, which is a dropdown list. The BindTo is the list data source, which is referencing the TempData we populated in our initial Index method. The DataTextField and DataValueField are the value and text property names. We also set the required attribute, which ensures the contact mode is required. It is important to note the Name must be the name of the grid model property, so the two-way binding is correct. Let’s see the results of the changes to the edit mode.

Contacts Grid Edit Dropdown

The final features are Add and Delete. The following is the view containing the updates for Add and Delete. You will also notice the ClientTemplate applied to the ContactMode, which forces the view to include the Name property in the display.

 C# |  copy code |? 
01
@using MvcKendoUIGrid.Models
02
 
03
@(Html.Kendo().Grid<Contact>()
04
      .Name("contactGrid")
05
      .Columns(columns =>
06
 
07
          {
08
              columns.Command(command =>
09
                  {
10
                      command.Edit();
11
                      command.Destroy();
12
                  }).Width(180);
13
              columns.Bound(c => c.Name).Title("Name").Width(150);
14
              columns.Bound(c => c.PreferredContactMode).Title("Mode of Contact").ClientTemplate("#=PreferredContactMode.Name#").Width(150);
15
              columns.Bound(c => c.Phone).Title("Phone").Width(120);
16
              columns.Bound(c => c.Email).Title("Email").Width(120);
17
          })
18
      .ToolBar(toolbar => toolbar.Create().Text("Add new contact"))
19
      .Pageable()
20
      .Filterable()
21
      .Sortable()
22
      .Scrollable()
23
      .AutoBind(true)
24
      .HtmlAttributes(new {style = "height:250px;"})
25
      .Editable(ed => ed.Mode(GridEditMode.InLine))
26
      .DataSource(dataSource => dataSource
27
                                    .Ajax()
28
                                    .ServerOperation(false)
29
                                    .Model(model =>
30
                                        {
31
                                            model.Id(c => c.Id);
32
                                            model.Field(c => c.PreferredContactMode).Editable(true).DefaultValue(new ContactMode {Id = 0, Name = string.Empty});
33
                                        })
34
                                    .Read(read => read.Action("Read", "Contact"))
35
                                    .Update(update => update.Action("Edit", "Contact"))
36
                                    .Destroy(delete => delete.Action("Delete", "Contact"))
37
                                    .Create(create => create.Action("Create", "Contact"))
38
      )
39
      )

We add the Destroy to the command, which provides the Delete button. The DataSource Destroy defines the Contact controller delete action, which will be responsible for deleting the contact. We add the create command to the Toolbar, which is positioned above the grid. The DataSource Create is the action invoked when the “Add new contact” button is clicked.

The Create and Delete controller methods appear below.

 C# |  copy code |? 
01
        /// <summary>
02
        /// Create action to add new contact
03
        /// </summary>
04
        /// <param name="dsRequest">Kendo Datasource Request</param>
05
        /// <param name="viewmodel">Contact model containing values to add</param>
06
        /// <returns>ActionResult</returns>
07
        [HttpPost]
08
        public ActionResult Create([DataSourceRequest] DataSourceRequest dsRequest, Contact viewmodel)
09
        {
10
            try
11
            {
12
                if (ModelState.IsValid && viewmodel != null)
13
                {
14
                    _service.Add(viewmodel);
15
                    return Json(new[] {viewmodel}.ToDataSourceResult(dsRequest, ModelState));
16
                }
17
                ModelState.AddModelError("Contacts", "Unable to add contact - not a valid contact.");
18
            }
19
            catch
20
            {
21
                ModelState.AddModelError("Contacts", "Unable to add contact.");
22
            }
23
 
24
            return Json(ModelState.ToDataSourceResult());
25
        }
26
 
27
        /// <summary>
28
        /// Delete action to remove existing contact
29
        /// </summary>
30
        /// <param name="dsRequest">Kendo Datasource Request</param>
31
        /// <param name="viewmodel">Contact model containing values to delete</param>
32
        /// <returns>ActionResult</returns>
33
        [HttpPost]
34
        public ActionResult Delete([DataSourceRequest] DataSourceRequest dsRequest, Contact viewmodel)
35
        {
36
            try
37
            {
38
                if (viewmodel != null)
39
                    _service.Delete(viewmodel);
40
                else
41
                    ModelState.AddModelError("Contacts", "Unable to delete contact - not a valid contact.");
42
            }
43
            catch
44
            {
45
                ModelState.AddModelError("Contacts", "Unable to delete contact.");
46
            }
47
 
48
            return Json(ModelState.ToDataSourceResult());
49
        }
50

The final version with the Edit, Add and Delete actions enabled.

Contacts Grid Complete

Contacts Grid AddContacts Grid Delete

I hope this provides a quick overview of the Kendo UI Grid widget and Kendo UI MVC server extensions, which together provide powerful features with minimal effort.

Castle Windsor IoC and AOP with ASP.NET MVC 4

0

It is no secret that I am a huge supporter of IoC (Inversion of Control) and AOP (Aspect-oriented Programming), which is evident with several earlier posts focused on building Enterprise applications. My first IoC experience was a Java portal project, which implemented Spring for many enterprise services. My recent projects have been .NET, which include several Spring.NET and Castle implementations. When evaluating IoC products, Ninject and more recently Microsoft Unity have also been candidates. IoC is gaining Microsoft community acceptance, especially with the release of ASP.NET MVC 4 and improved support/integration. See previous article for more information on design patterns including Dependency Injection (DI), Single Responsibility Principle (SRP) and Separation of Concerns (SoC).

In this post, I will provide an overview of the MVC 4 Castle Windsor IoC and Castle Core Dynamic Proxy features. Gasp…after several articles and posts with Spring.NET? This is true, but every project should evaluate several IoC (also referred to as Dependency Injection) containers and select the one that fits.

I would recommend NuGet for packages, so you can simplify the management of 3rd party libraries and tools.

Once you create a new ASP.NET MVC 4 solution and add the Castle packages to your solution, we can start building the application framework and scaffolding. Since this is a quick-start for Castle IoC and AOP features, we’ll build the essential components for a working MVC 4 solution.

First, create a Framework folder for the IoC and AOP classes. I would recommend Framework or Core, which are descriptive names. I created a Windsor sub-folder, which indicates the items will be supporting the IoC and AOP. The next level is Installers and Interceptors at the same level, so the following is an example of the project structure.

Framework Folder Structure

First, we will create the boilerplate WindsorControllerFactory to handle the IoC plumbing. The following is a snippet of the code, which you can add to the Framework.Windsor folder.

01
using System;
02
using System.Web;
03
using System.Web.Routing;
04
using Castle.MicroKernel;
05
using System.Web.Mvc;
06
 
07
namespace Mvc4Castle.Framework.Windsor
08
{
09
    /// <summary>
10
    /// Castle Windsor MVC4 Controller Factory Implementation for IoC
11
    /// </summary>
12
    public class WindsorControllerFactory : DefaultControllerFactory
13
    {
14
        private IKernel _kernel;
15
 
16
        public WindsorControllerFactory(IKernel kernel)
17
        {
18
            _kernel = kernel;
19
        }
20
 
21
        public override void ReleaseController(IController controller)
22
        {
23
            _kernel.ReleaseComponent(controller);
24
        }
25
 
26
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
27
        {
28
            if (controllerType == null)
29
            {
30
                throw new HttpException(404, string.Format("The Windsor Controller at path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
31
            }
32
            return (IController)_kernel.Resolve(controllerType);
33
        }
34
    }
35
}

Next, we will create IWindsorInstaller implementations. The installers will register components or objects with the IoC container. Alternatively, you can choose XML-based configuration files to define the components/objects instructing the container on the “How” to instantiate. In our example, we will use the MVC controllers included in the starter project (e.g. HomeController).

01
using Castle.MicroKernel.Registration;
02
using Castle.Windsor;
03
using Castle.MicroKernel.SubSystems.Configuration;
04
using System.Web.Mvc;
05
using Mvc4Castle.Controllers;
06
 
07
namespace Mvc4Castle.Framework.Windsor.Installers
08
{
09
    /// <summary>
10
    /// Castle Windsor Controller responsible for registering classes managed by
11
    /// the IoC container.
12
    /// 
13
    /// This implementation registers all MVC Controllers (IController).
14
    /// </summary>
15
    public class ControllerInstaller : IWindsorInstaller
16
    {
17
        public void Install(IWindsorContainer container, IConfigurationStore store)
18
        {
19
            container.Register(Classes.FromThisAssembly()
20
                                .BasedOn<IController>()
21
                                .LifestyleTransient()
22
                                .ConfigureFor<HomeController>(c => c.DependsOn(Dependency.OnComponent("service", "MyServiceResource"))));
23
        }
24
    }
25
}

In this example, we are registering objects/components based on IController located in the current assembly. We are also configuring the HomeController and dependency injection of the service argument. We’ll explore additional options for registering objects/components in the container.

We also need to add the container to our start-up processes in the global.asax. The Application_Start and Application_End require some logic to handle this task.

01
    public class MvcApplication : System.Web.HttpApplication
02
    {
03
        private static IWindsorContainer _container;
04
 
05
        protected void Application_Start()
06
        {
07
            AreaRegistration.RegisterAllAreas();
08
 
09
            WebApiConfig.Register(GlobalConfiguration.Configuration);
10
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
11
            RouteConfig.RegisterRoutes(RouteTable.Routes);
12
            BundleConfig.RegisterBundles(BundleTable.Bundles);
13
            InjectContainer();
14
        }
15
 
16
        /// <summary>
17
        /// Create MVC4 Controller for Castle Windsor IoC container
18
        /// </summary>
19
        private static void InjectContainer()
20
        {
21
            _container = new WindsorContainer().Install(FromAssembly.This());
22
 
23
            var controllerFactory = new WindsorControllerFactory(_container.Kernel);
24
            ControllerBuilder.Current.SetControllerFactory(controllerFactory);
25
        }
26
 
27
        /// <summary>
28
        /// Destroy Castle Windsor IoC container
29
        /// </summary>
30
        protected void Application_End()
31
        {
32
            _container.Dispose();
33
        }
34
    }
35

You can add several installer implementations using the fluent API or XML-based configuration files, so you can organize your object/component definitions for the IoC container. Since we are following design patterns, we will introduce Dependency Injection/DIP practices to inject the object/component dependency via the controller constructor. The service will represent the business logic, which is responsible for orchestrating the calls to the data tier or other service objects to process the client request and return an appropriate result.

1
       private IService _service;
2
 
3
        public HomeController(IService service)
4
        {
5
            _service = service;
6
        }

So…let’s take a look at a few container registration options. The following will register all objects/components with a type ending in “Service” (i.e. wildcard Service) from the current assembly. The life-cycle can also be configured, which in the examples ”transient” indicates a new instance.

1
           container.Register(Classes.FromThisAssembly()
2
                 .Where(type => type.Name.EndsWith("Service"))
3
                 .WithServiceDefaultInterfaces()
4
                 .Configure(c => c.LifestyleTransient().Interceptors<ServiceTitleInterceptor>()));
5

We’ll cover the AOP or dynamic proxies, which is the Interceptor. The following example defines the dependency using a named value, so the “Title” property will be set to the static value “Injected By Dependency Value”.

1
            container.Register(Component.For<IService>()
2
                .ImplementedBy<HomeService>()
3
                .Named("MyServiceValue")
4
                .DependsOn(Dependency.OnValue("Title", "Injected By Dependency Value"))
5
                .LifestyleTransient());
6

The next example is injecting the “Title” property value from the web.config appsettings “Title” parameter.

1
            container.Register(Component.For<IService>()
2
                .ImplementedBy<HomeService>()
3
                .Named("MyServiceConfig")
4
                .DependsOn(Dependency.OnAppSettingsValue("Title", "Title"))
5
                .LifestyleTransient());
6

The following illustrates injecting a property value from a resource file, which also is the “Title” property. This provides several source options for the dependency injection, which can also include a named component (see Named).

1
            container.Register(Component.For<IService>()
2
                .ImplementedBy<HomeService>()
3
                .Named("MyServiceResource")
4
                .DependsOn(Dependency.OnResource<App_LocalResources.Resource1>("Title", "Title"))
5
                .LifestyleTransient());
6

The previous examples introduce the Castle Windsor IoC basics as they apply to MVC4. The last topic is AOP, which we encountered with the interceptor in a previous object/component container registration. The Spring.NET AOP support was introduced in a previous post. Castle core is a lighter version, which is based on implementing the Castle.DynamicProxy.IInterceptor.

01
    public class ServiceTitleInterceptor : IInterceptor
02
    {
03
        public void Intercept(IInvocation invocation)
04
        {
05
            if (invocation.Method.Name.Equals("get_Title"))
06
                invocation.ReturnValue = "You have been hijacked at." + DateTime.Now;
07
            else
08
                invocation.Proceed();
09
        }
10
     }

In this interceptor implementation, the Intercept method contains logic to check for the method name “get_Title”. This is a Title property getter. If true then ReturnValue is set to “You have been hijacked at.” including a date-stamp. If the method name is not “get_Title” then the control is returned to the intercepted object and processing continues. You can implement an interceptor for logging, caching, transaction management, error handling and other common services without adding code to every class and method.

The following is an installer snippet for registering an object/component using the ServiceTitleInterceptor interceptor, so every call will be intercepted and the Intercept method executed.

1
.Interceptors<ServiceTitleInterceptor>()

The above will apply the ServiceTitleInterceptor to the object/component. You can also add the InterceptorAttribute ( [Interceptor(typeof(ServiceTitleInterceptor))] ) to the target class, which is an alternative to the fluent API approach above.

01
using Castle.Core;
02
using Mvc4Castle.Framework.Windsor.Interceptors;
03
 
04
namespace Mvc4Castle.Framework.Services
05
{
06
    [Interceptor(typeof(ServiceTitleInterceptor))]
07
    public class HomeService : IService
08
    {
09
        public string Title { get; set; } 
10
    }
11
}

I hope this quick-start provides another IoC and AOP option for your MVC projects. Since I was evaluating several IoC libraries for a current project, I thought a summary could be helpful.

Related Posts:

Sping.NET IoC

Spring.NET and MVC3

Spring.NET AOP

Automate Merge With TFS Build

0

In the previous TFS Build Process Template and Versioning post, we discussed customizing your Team Foundation Server (TFS) build workflow to dynamically inject the build number into the assembly version. This post will present a process to automate the merge process within a TFS build template. This feature would benefit organizations maintaining a development branch, which requires merging into the main or trunk after a successful release.

The following custom workflow sequence can be included as the final activity of the development build, which will be conditionally executed based on successful build and test results. If you are not performing manual merge processes then you should not encounter conflicts. I would recommend creating a copy of your current build template and add a name prefix as a version number. For example: MyBuildTemplate.2.xaml. Once you have tested the new version, you can replace the current MyBuildTemplate.1.xaml with MyBuildTemplate.2.xaml.

The following is the high-level Merge sequence illustrated in the workflow.

Merge Sequence

The first step is add the arguments, which will be the input to drive the merge process. The parameters should be exposed in the build definition and Queue Build prompt.

Merge Arguments

The SourceMergeBranchPath and TargetMergeBranchPath contain the paths to the TFS location, where the source changes are the “to be” merged into the target. The MergeOptions will allow you to select the appropriate merge option to apply to the process, which will be discussed later in this article.

Next, we will add the logic into the Merge sequence starting with a conditional statement verifying the 2 argument values are valid. The MergeOptions argument is a default, so no validation is required.

Merge Validate Required Arguments

If the required arguments are validated then we begin the merge using another sequence to wrap the logic. The Else path will add a message to the build and exit the merge sequence.

The expanded Merge Begin sequence appears in the image below.

Merge Begin Sequence

The key step is the Run Merge, which is assigns the GetStatus value. The merge process is executed as a method of the Workspace object. The following is the method call to perform the merge.

Workspace.Merge(SourceMergeBranchPath, TargetMergeBranchPath, Nothing, Nothing, LockLevel.None, RecursionType.Full, MergeOptions)

The first 2 parameters are the source and target merge branch paths, which are 2 arguments we discussed previously. The final parameter is the MergeOptions argument, which we can set in the build definition or queue build prompt. The result will be assigned to GetStatus, which will analyze in the next steps.

Merge Conflicts

In the above condition, we check if conflicts or errors were encountered by checking the GetStatus object. If the counts are zero then we can continue to follow the success merge path. If a failure or conflict is encountered then we report the issues and abort the merge process. This path will require additional review and decision.

The GetPendingChanges is also an assignment, which represents the pending changes as a result of the merge. The Workspace.GetPendingChanges will return a collection of the pending changes and the next condition will process any changes.

Merge Process Pending Changes

The Process Pending Changes sequence will include a ForEach to process each pending change. In this process, we are simply recording the change for the build log.

Merge Report Pending Change

When you select a Visual Studio merge, you must perform a check-in after you resolve all conflicts within your workspace. So…the next step is to checkin or commit the changes. The Checkin Pending Changes sequence is responsible for this task.

Merge Checkin Pending Changes

The Run Checkin is also an assignment, where again we call a Workspace method to perform the checkin of the workspace pending changes. The following is the Expression Editor with the CheckinResult assignment.

Merge Checkin Pending Changes

The PendingChanges was a previous assignment and the second parameter are the comments applied to the checkin process. In this example, we assign a standard checkin comment for the changeset. 

If we switch to the Else path of the Pending Changes condition then we would report no pending changes to checkin. The merge is still successful, but no changes were processed.

So…we also need to complete the Else path of the Merge Begin sequence, where we check for failures and conflicts. We just completed the successful merge path, where no failures or conflicts were reported. The following is the Display Merge Results sequence, which reports the failures and/or conflicts to be reviewed.

Merge Report Conflict

The conflict and failures reporting process is essentially the same, where the ForEach will process each item of the collection and a WriteDeploymentInformation can log an entry. The final merge status or result is a failure, so you can also set the build status value accordingly.

We completed the review of the Merge process, which can easily incorporate into your TFS build template and perform an automated merge. The final topic is the Merge Options parameter, which we introduced earlier as an argument. This is also a parameter of the Workspace Merge method, which instructs the process to follow the appropriate conflict resolution defined by the build definition or manual build. The following are the options and a brief explanation, so you can assign the appropriate merge option for your process.

  • None – no special options
  • AlwaysAcceptMine – discard any changes as resolve conflicts using AlwaysAcceptYours resolution
  • ForceMerge – same as tf.exe force option
  • Baseless – source and target items have no branch relationship
  • NoMerge – same as tf.exe preview option

You can also update the Process Parameter Metadata, so it includes helpful information for the user. This appears below for the 3 merge arguments we setup earlier.

Merge Process Parameter Metadata

I hope this article provides an option for an automated TFS build process merge process. This implementation requires no external or third-party assemblies, so it should be easy to add to your current build templates. Since this is an independent build process, I would recommend creating a merge workflow template and calling the merge as a child of your main build template. This should be based on the success of your build and test, so the automated merge is executed during a successful build result.

Go to Top