Showing posts with label ASP.NET MVC. Show all posts
Showing posts with label ASP.NET MVC. Show all posts

Monday, October 20, 2014

Registering runtime types on DryIoc

DryIoc is missing an API for registering a runtime type

Hence we can't automate this WCF registration..

_container.RegisterDelegate<TheServiceContracts.IProductService>(x => 
    (TheServiceContracts.IProductService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IProductService),  
        System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/ProductService.svc")
    )
, Reuse.InResolutionScope);
    
_container.RegisterDelegate<TheServiceContracts.IMemberService>(x => 
    (TheServiceContracts.IMemberService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IMemberService), 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/MemberService.svc")
    )
, Reuse.InResolutionScope);

_container.RegisterDelegate<TheServiceContracts.IOrderService>(x => 
    (TheServiceContracts.IOrderService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IOrderService), 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/OrderService.svc")
    )
, Reuse.InResolutionScope);


..into this:

_container.Register<System.ServiceModel.BasicHttpBinding>(DryIoc.Reuse.Singleton, x => x.GetConstructor(new Type[] { }));

foreach (var contractType in typeof(TheServiceContracts.IMemberService).Assembly.GetTypes())
{
    _container.RegisterDelegate(contractType,
        r =>
        {
            var binding = r.Resolve<System.ServiceModel.BasicHttpBinding>();

            var svcAddress = "http://localhost:1337/" + contractType.Name.Substring(1) + ".svc";
            var endpointAddress = new System.ServiceModel.EndpointAddress(svcAddress);


            return ServiceModelHelper.CreateService(contractType, new System.ServiceModel.BasicHttpBinding(), endpointAddress);
        }, Reuse.InResolutionScope);

}   


And this available API in DryIoc can't be used, parameters in generics and casting must be determined at compile-time. Hence we can't pass the runtime type contractType variable as a parameter to generics and casting
_container.Register<System.ServiceModel.BasicHttpBinding>(DryIoc.Reuse.Singleton, x => x.GetConstructor(new Type[] { }));

foreach(var contractType in typeof(TheServiceContracts.IProductService).Assembly.GetTypes())
{
    var binding = new System.ServiceModel.BasicHttpBinding();
    var endpointAddress = new System.ServiceModel.EndpointAddress("http://localhost:1337/" + contractType.SubString(1) + ".svc"); // removes the first letter I
    _container.RegisterDelegate<contractType>(r => 
        {
            var binding = r.Resolve<System.ServiceModel.BasicHttpBinding>();

            var svcAddress = "http://localhost:1337/" + contractType.Name.Substring(1) + ".svc";
            var address = new System.ServiceModel.EndpointAddress(svcAddress);
            
            return (contractType) ServiceModelHelper.CreateService(contractType, binding, endpointAddress)
        }, Reuse.InResolutionScope);
}


To fix that, we need to simulate RegisterDelegate<T> during runtime


First step, we should design how the additional API should look like. It should feel as close as possible to the convention of the original API:
_container.RegisterDelegate<TheServiceContracts.IOrderService>(x => 
    (TheServiceContracts.IOrderService) ServiceModelHelper.CreateService(
        typeof(TheServiceContracts.IOrderService), 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/OrderService.svc")
    )
, Reuse.InResolutionScope);


The additional API should be like this:
_container.RegisterDelegate(contractType, x => 
    ServiceModelHelper.CreateService(
        contractType, 
        new System.ServiceModel.BasicHttpBinding(), 
        new System.ServiceModel.EndpointAddress("http://localhost:1337/OrderService.svc")
    )
, Reuse.InResolutionScope);


Then our API should be an extension method of the DryIoc.IResolver:
public static class ResolverExtensionHelper
{
    public static void RegisterDelegate(this DryIoc.IResolver resolver,
         Type contractType,
         Func<DryIoc.IResolver, object> objectConstructor,
         DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null)
    {
        ResolverBuilder.Build(container, contractType, objectConstructor, reuse, setup, named);
    }
}


You maybe wondering why we need to use two classes, ResolverBuilder and ResolverExtensionHelper, why not just do everything in ResolverExtensionHelper?

We need to have the following code, we need to cast the objects generated(e.g., WCF proxy objects) in a delegate back to its dependency-injected type(e.g., is an interface) during runtime, which is not possible with the original RegisterDelegate<contractTypeHere>. So we need to have a mechanism that can cast runtime types:
TService ConstructObjectThenCast<TService>(IResolver resolver)
{
    var svc = (TService) this._objectConstructorDelegate(resolver);
    return svc;
}


And we cannot add lambda parameter (any parameter for that matter) on that method:
TService ConstructObjectThenCast<TService>(IResolver resolver, 
    Func<IResolver, object> objectConstructorDelegate)
{
    var svc = (TService) objectConstructorDelegate(resolver);
    return svc;
}


Adding that parameter would cause runtime error when doing a reflection call on original RegisterDelegate method. ConstructObjectThenCast method is the substitute method for the original RegisterDelegate's lambda parameter
Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.


Looking at the original RegisterDelegate method signature shows why:
public static void RegisterDelegate<TService>(
     this IRegistrator registrator, Func<IResolver, TService> lambda, 
     IReuse reuse = null, FactorySetup setup = null, string named = null);

The lambda delegate we want to hook on has one parameter only, IResolver only. Hence if Func<IResolver, object> objectConstructorDelegate is added to parameters of ConstructObjectThenCast, it results to the error above


However, since we cannot add another parameter on the substitute method(ConstructObjectThenCast) to the lambda, the only way to pass the user-defined lambda to the original RegisterDelegate is to create a class instance, which is not possible on static class, hence the need for two classes. So when the original RegisterDelegate method calls our substitute method for original RegisterDelegate's lambda, we can still call the user-defined lambda in turn. The user-defined lambda is stored in a non-static class, it's a field on that class: Func<DryIoc.IResolver,object> _objectConstructorDelegate;


ConstructObjectThenCast is the method that will cast an object to its dependency-injected type(e.g., in an interface), that's the method we will substitute to RegisterDelegate's lambda parameter. In order for that substitute method to be able to call our user-defined lambda, we will call the user-defined lamba from the class instance. this._objectConstructorDelegate is where the user-defined lambda is stored
TService ConstructObjectThenCast<TService>(IResolver resolver)
{
    var svc = (TService) this._objectConstructorDelegate(resolver);
    return svc;
}




Here's the complete code of the additional API. This also shows how to use DryIoc on ASP.NET MVC and WCF:
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;

using DryIoc;
using AspNetMvcUseWcf.Controllers;

namespace AspNetMvcUseWcf
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            
            ControllerBuilder.Current.SetControllerFactory(new DryIocDependencyResolver());   
        }
    }

    class DryIocDependencyResolver : System.Web.Mvc.DefaultControllerFactory
    {
        DryIoc.Container _container;

        public DryIocDependencyResolver()
        {
            _container = new DryIoc.Container();
            RegisterTheIocs();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            System.Web.Mvc.IController ic = controllerType == null
                ? null
                : (System.Web.Mvc.IController)_container.Resolve(controllerType);

            // _container.ResolvePropertiesAndFields(ic);  // uncomment this if you want to use DI on controller's properties

            return ic;
        }

        void RegisterTheIocs()
        {
            RegisterMvcControllers();
            RegisterWcfServices();
        }

        void RegisterWcfServices()
        {
            _container.Register<System.ServiceModel.BasicHttpBinding>(DryIoc.Reuse.Singleton, x => x.GetConstructor(new Type[] { }));


            foreach (var contractType in typeof(TheServiceContracts.IMemberService).Assembly.GetTypes())
            {                
                _container.RegisterDelegate(contractType,
                    r =>
                    {
                        var binding = r.Resolve<System.ServiceModel.BasicHttpBinding>();

                        var svcAddress = "http://localhost:1337/" + contractType.Name.Substring(1) + ".svc";
                        var endpointAddress = new System.ServiceModel.EndpointAddress(svcAddress);


                        return ServiceModelHelper.CreateService(contractType, binding, endpointAddress);
                    }, Reuse.InResolutionScope);

            }
        }

        void RegisterMvcControllers()
        {
            System.Reflection.Assembly assembly = typeof(HomeController).Assembly;

            foreach (var controller in assembly.GetTypes().Where(t => typeof(Controller).IsAssignableFrom(t)))
            {
                _container.Register(controller, DryIoc.Reuse.InResolutionScope);
            }
        }


    } // class DryIocDependencyResolver






    public static class ResolverExtensionHelper
    {
        public static void RegisterDelegate(this DryIoc.IResolver container,
             Type contractType, Func<DryIoc.IResolver, object> objectConstructorDelegate,
             DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null)
        {
            ResolverBuilder.Build(container, contractType, objectConstructorDelegate, reuse, setup, named);
        }
    }

    public class ResolverBuilder
    {
        
        Func<DryIoc.IResolver,object> _objectConstructorDelegate;


        public static void Build(DryIoc.IResolver container, Type contractType, Func<IResolver, object> objectConstructor, IReuse reuse, FactorySetup setup, string named)
        {
            new ResolverBuilder(container, contractType, objectConstructor, reuse, setup, named);
        }

        ResolverBuilder(
            DryIoc.IResolver resolver, Type contractType, Func<DryIoc.IResolver, object> objectConstructorDelegate,
            DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null
            )
        {
            _objectConstructorDelegate = objectConstructorDelegate;

            Delegate lambda;
            {
                System.Reflection.MethodInfo constructObjectThenCastMethodInfo = Create_ConstructObjectThenCast_MethodInfo_For_ContractType(contractType);
                lambda = Create_ConstructObjectThenCast_Lambda_For_ContractType(this, constructObjectThenCastMethodInfo, contractType);
            }


            // Invoke the original RegisterDelegate<TService>            
            {
                // public static void RegisterDelegate<TService>(this DryIoc.IRegistrator registrator, Func<DryIoc.IResolver, TService> lambda, 
                //                                                     DryIoc.IReuse reuse = null, DryIoc.FactorySetup setup = null, string named = null);

                // obj is null, means RegisterDelegate is a static method
                // resolver comes from _container
                // contractType is the TService in the original _container.RegisterDelegate<TService>
                System.Reflection.MethodInfo registerDelegateMethodInfo = typeof(DryIoc.Registrator).GetMethod("RegisterDelegate").MakeGenericMethod(contractType);
                registerDelegateMethodInfo.Invoke(/*obj*/ null, new object[] { resolver, lambda, reuse, setup, named });
            }
        }

        static System.Reflection.MethodInfo Create_ConstructObjectThenCast_MethodInfo_For_ContractType(Type contractType)
        {

            System.Reflection.MethodInfo constructObjectThenCastMethodInfo =
                // typeof(IResolver) is the resolver parameter of: TService ConstructObjectThenCast<TService>(IResolver resolver)
               typeof(ResolverBuilder).GetMethod(
                   name: "ConstructObjectThenCast",
                   types: new[] { typeof(IResolver) },
                   bindingAttr: System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
                   binder: null,
                   modifiers: null
                   )
                // contractType is the TService of ConstructObjectThenCast<TService>
               .MakeGenericMethod(contractType);
            return constructObjectThenCastMethodInfo;
        }


        // Create a lambda out of this class method: TService ConstructObjectThenCast<TService>(IResolver resolver)
        static Delegate Create_ConstructObjectThenCast_Lambda_For_ContractType(ResolverBuilder resolverBuilder,  System.Reflection.MethodInfo constructObjectThenCastMethodInfo, Type contractType)
        {
            // Create a Func<IResolver,TService> delegate type
            // This will be used as the lambda parameter on the original RegisterDelegate static method.                                          
            Type lambdaDelegateType = typeof(Func<,>).MakeGenericType(typeof(DryIoc.IResolver), contractType);
            // The above corresponds to this example: Func<IResolver, TheServiceContracts.IProductService>


            // Cannot use Activator.CreateInstance on delegate type as delegates don't have constructor
            // ConstructObjectThenCast method has a signature same as lambdaDelegateType 
            // Create a lambda out of ConstructObjectThenCast method info
            Delegate lambda = Delegate.CreateDelegate(lambdaDelegateType, resolverBuilder, constructObjectThenCastMethodInfo);

            return lambda;
        }

     
        TService ConstructObjectThenCast<TService>(IResolver resolver)
        {
            var svc = (TService) this._objectConstructorDelegate(resolver);
            return svc;
        }


    }// class ResolverBuilder


    public static class ServiceModelHelper
    {
        public static object CreateService(
            Type contractType,
            System.ServiceModel.BasicHttpBinding basicHttpBinding,
            System.ServiceModel.EndpointAddress endpointAddress
            )
        {
            var binding = new System.ServiceModel.BasicHttpBinding();
            //Get the address of the service from configuration or some other mechanism - Not shown here

            //dynamic factory generation
            object factory =
                Activator.CreateInstance(typeof(System.ServiceModel.ChannelFactory<>)
                .MakeGenericType(contractType), binding, endpointAddress);

            System.Reflection.MethodInfo createFactory = factory.GetType().GetMethod("CreateChannel", new Type[] { });
            //now dynamic proxy generation using reflection
            return createFactory.Invoke(factory, null);
        }
    }
    
}//namespace



Happy Coding!

Friday, October 17, 2014

ASP.NET MVC - Return JSON without using JsonResult

After using ServiceStack and ASP.NET Web API, and then using ASP.NET MVC again, you'll be wanting to just return the model as-is and just forget the explicit infrastructure to return the model as JSON

public JsonResult GetTopSellingProduct(int year)
{
    using(IDataStore ds = new DataStore(_nhibernateSessionFactory))
    {
        return Json(Product.GetTopSellingProduct(ds, year), JsonRequestBehavior.AllowGet);
    }    
}


This is how we wanted to return models as JSON even in ASP.NET MVC controller:
public Product GetTopSellingProduct(int year)
{
    using(IDataStore ds = new DataStore(_nhSessionFactory))
    {
        return Product.GetTopSellingProduct(ds, year);
    }    
}


Turns out that is possible, Adam Bar noticed ASP.NET MVC can be made to return JSON object implicitly


Here's a complete code that enables ASP.NET Web API-like controller action to ASP.NET MVC. This is using NetJSON for JSON serialization, and DryIoc for IoC/DI needs. DryIoc is the fastest IoC/DI and NetJSON is the fastest JSON serializer

using Erp.Controllers;

using DryIoc; // http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
using NJ = NetJSON; // http://theburningmonk.com/2014/08/json-serializers-benchmarks-updated-2/

using System;

using System.Linq;



namespace Erp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        DryIocDependencyResolver _ioc = new DryIocDependencyResolver(); // Customized DefaultControllerFactory
        
        protected void Application_Start()
        {
            System.Web.Mvc.AreaRegistration.RegisterAllAreas();

            
            FilterConfig.RegisterGlobalFilters(System.Web.Mvc.GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(System.Web.Routing.RouteTable.Routes);

                        
            System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(_ioc);
            
        }
    
    }
       

    class DryIocDependencyResolver : System.Web.Mvc.DefaultControllerFactory
    {
        internal DryIoc.Container _container;

        public DryIocDependencyResolver()
        {
            _container = new DryIoc.Container();
            RegisterTheIocs();
        }


        protected override System.Web.Mvc.IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            System.Web.Mvc.IController ic = controllerType == null
                ? null
                : (System.Web.Mvc.IController)_container.Resolve(controllerType);


            // _container.ResolvePropertiesAndFields(controller);  // uncomment this if you want to use DI on controller's properties

            var mvcController = ic as System.Web.Mvc.Controller;

            if (mvcController != null)
                mvcController.ActionInvoker = new SimulateWebApi();

            return mvcController;
        }


        class SimulateWebApi : System.Web.Mvc.ControllerActionInvoker
        {
            const string JsonContentType = "application/json";

            protected override System.Web.Mvc.ActionResult CreateActionResult(
                System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ActionDescriptor actionDescriptor, object actionReturnValue)
            {
                if (actionReturnValue == null)
                    return new System.Web.Mvc.EmptyResult();

                return (actionReturnValue as System.Web.Mvc.ActionResult) ?? new System.Web.Mvc.ContentResult()
                {
                    ContentType = JsonContentType,
                    Content = NJ.NetJSON.Serialize (actionReturnValue)
                };
            }
        }


        void RegisterTheIocs()
        {            
            System.Reflection.Assembly assembly = typeof(HomeController).Assembly;

            foreach (var controller in assembly.GetTypes().Where(t => typeof(System.Web.Mvc.Controller).IsAssignableFrom(t)))
            {
                _container.Register(controller, DryIoc.Reuse.InResolutionScope);                
            }
           

            _container.Register(typeof(Elmah.Mvc.ElmahController), DryIoc.Reuse.InResolutionScope);

            // http://ayende.com/blog/153701/ask-ayende-life-without-repositories-are-they-worth-living
            // http://www.ienablemuch.com/2014/10/typical-nhibernate-sessionfactory-auto-mapping.html
            _container.RegisterDelegate<NHibernate.ISessionFactory>(x => Erp.DomainMapping.Mapper.SessionFactory, DryIoc.Reuse.Singleton);            
        }

    }//DefaultControllerFactory

    

}//namespace



JSON Serializers benchmark: http://www.ienablemuch.com/2014/10/benchmarking-net-json-serializers.html


Happy Coding!

Tuesday, October 14, 2014

ASP.NET MVC Forms Authentication in Eight Easy Steps

Step 0. Create an Empty ASP.NET MVC project

Step 1. Create a database

/*
use master;
drop database ReadyAspNetMvc;
*/


create database ReadyAspNetMvc;
go


use ReadyAspNetMvc;
go

create table Person
(
    PersonId int identity(1,1) primary key,
    UserName nvarchar(50) not null unique,
    PlainTextPassword nvarchar(100) not null, -- should use hashing: http://www.ienablemuch.com/2014/10/bcrypt-primer.html    
    Roles nvarchar(100) not null default '' -- In actual application, this is relational not comma-delimited
);


insert into Person(UserName, PlainTextPassword,Roles) values
('John', 'L', 'Beatles,Musician'),
('Paul', 'M', 'Beatles,Musician'),
('George', 'H', 'Beatles,Musician'),
('Ringo', 'S', 'Beatles,Musician'),
('Kurt', 'C', 'Nirvana,Musician'),
('Dave', 'G', 'Nirvana,Musician'),
('Krist', 'N', 'Nirvana,Musician'),
('Elvis', 'P', 'Musician'),
('Michael', 'J', ''),
('Freddie', 'M', '');


go


Step 2. Add Forms Authentication in web.config:
<configuration>

    <system.web>
    
        <httpRuntime targetFramework="4.5.1" />
        
        <compilation debug="true" targetFramework="4.5.1" />

        <authentication mode="Forms">
            <forms loginUrl="~/Security/Login" timeout="2880" />
        </authentication>


Step 3: Use an ORM, let's use Dapper, get it from nuget:

Step 4: Create a UserLogin model:
namespace ReadyAspNetMvc.Models
{
    public class UserLogin
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public string ReturnUrl { get; set; } // when an action accessed is not authorized, this is where the url to return to is binded 
    }
}

Here's how ReturnUrl looks like:



Step 5: Create a login page (two sub-steps):

Step 5.1 Create Login controller and actions:

using System.Web.Mvc;

using System.Linq;


using Dapper;


namespace ReadyAspNetMvc.Controllers
{
    public class SecurityController : Controller
    {       
        public ViewResult Login()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Login(ReadyAspNetMvc.Models.UserLogin login)
        {
            System.Action authorize = () => System.Web.Security.FormsAuthentication.SetAuthCookie(userName: login.UserName, createPersistentCookie: true);
            

            using (var con = new System.Data.SqlClient.SqlConnection("Server=.; Database=ReadyAspNetMvc; Trusted_Connection=true;"))
            {
                var persons = con.Query("select UserName, PlainTextPassword from Person where UserName = @UserName", new { UserName = login.UserName });

                if (!persons.Any())
                    return View(login);


                var person = persons.Single();


                if (login.Password == person.PlainTextPassword)
                {                    

                    if (string.IsNullOrWhiteSpace(login.ReturnUrl))
                    {
                        authorize();
                        return RedirectToAction(controllerName: "Home", actionName: "Welcome");
                    }
                    else
                    {
                        if (Url.IsReallyLocalUrl(login.ReturnUrl))
                        {
                            authorize();
                            return Redirect(login.ReturnUrl);
                        }
                        else
                        {
                            TempData["warning_message"] = "Url was altered";
                            return RedirectToAction(controllerName: "Security", actionName: "Login");
                        }
                    }
                    

                    //// another way, but it's better to use ASP.NET MVC-proper by using return Redirect(...), so use the above
                    //else
                    //{
                    //    System.Web.Security.FormsAuthentication.RedirectFromLoginPage(userName: login.UserName, createPersistentCookie: true);
                    //    return null;
                    //}
                }
                else
                {  
                    TempData["warning_message"] = "Invalid username or password";                  
                    return View();
                }
            }
            
        }//Login action


        public RedirectToRouteResult SignOut()
        {
            System.Web.Security.FormsAuthentication.SignOut();
            return RedirectToAction(controllerName: "Home", actionName: "Welcome");
        }

    }//SecurityController
}


...

    public static class UrlExtension
    {
        // Thanks وحيد نصيري
        public static bool IsReallyLocalUrl(this UrlHelper url, string returnUrl)
        {
            var shouldRedirect = !string.IsNullOrWhiteSpace(returnUrl) &&
                url.IsLocalUrl(returnUrl) &&
                returnUrl.Length > 1 &&
                returnUrl.StartsWith("/", System.StringComparison.InvariantCultureIgnoreCase) &&
                !returnUrl.StartsWith("//", System.StringComparison.InvariantCultureIgnoreCase) &&
                !returnUrl.StartsWith("/\\", System.StringComparison.InvariantCultureIgnoreCase);
            return shouldRedirect;
        }
    }




5.2 Create the view: /Views/Security/Login.cshtml

@model ReadyAspNetMvc.Models.UserLogin

@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>



@using (Html.BeginForm())
{
    <div>
        @Html.LabelFor(x => x.UserName)
    </div>
    <div>
        @Html.TextBoxFor(x => x.UserName)
    </div>

    <div>
        @Html.LabelFor(x => x.Password)
    </div>

    <div>
        @Html.PasswordFor(x => x.Password)
    </div>

    
    <p>
        <input type="submit" />
    </p>
            
}


<p>
    <a href="@Url.RouteUrl(new { controller = "Home", action = "Welcome" })">Back to Home Welcome</a>
</p>



<p style="color: red">@this.TempData["warning_message"]</p>




6. Detect roles in Global.asax.cs:
using System;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;


using System.Linq;
using Dapper;

namespace ReadyAspNetMvc
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801
    public class MvcApplication : System.Web.HttpApplication
    {        
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);                       
        }

     
        // Auto-wired-up event
        // http://stackoverflow.com/questions/4677866/how-does-global-asax-postauthenticaterequest-event-binding-happe
        void Application_PostAuthenticateRequest(object sender, EventArgs e)
        {
            if (!System.Web.Security.FormsAuthentication.CookiesSupported) 
                return;

            string cookieName = System.Web.Security.FormsAuthentication.FormsCookieName;            
            System.Web.HttpCookie cookie = this.Request.Cookies[cookieName]; 

            if (cookie == null) 
                return;

            string encryptedCookieValue = cookie.Value;
            System.Web.Security.FormsAuthenticationTicket ticket = System.Web.Security.FormsAuthentication.Decrypt(encryptedCookieValue);

            string userName = ticket.Name;
            string[] roles = null;


            using (var con = new System.Data.SqlClient.SqlConnection("Server=.; Database=ReadyAspNetMvc; Trusted_Connection=true;"))
            {
                var persons = con.Query("select UserName, Roles from Person where UserName = @UserName", new { UserName = userName });

                var person = persons.Single();


                roles = ((string)person.Roles).Split(',');


                System.Security.Principal.IIdentity identity = new System.Security.Principal.GenericIdentity(name: userName, type: "Forms");

                System.Web.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, roles);
            }


        }// Application_PostAuthenticateRequest

        
    }//class MvcApplication
}


Step 7. Setup the home page. Two sub-steps

7.1. Create the Home controller:
using System.Web.Mvc;

namespace ReadyAspNetMvc.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return RedirectToAction(actionName: "Welcome");
        }


        public ViewResult Welcome()
        {
            string userName = System.Web.HttpContext.Current.User.Identity.Name;

            ViewBag.UserName = userName;
            
            return View();
        }

    }
}

7.2. Create the view: /Views/Home/Welcome.cshtml
@{
    ViewBag.Title = "Welcome";
}



@if (!string.IsNullOrWhiteSpace(ViewBag.UserName)) 
{
    <h2>Welcome @ViewBag.UserName</h2>
}



<div>
    <a href="@Url.RouteUrl(new { controller = "Music", action = "AboutBeatles" })">About Beatles</a>    
</div>

<div>
    <a href="@Url.RouteUrl(new { controller = "Music", action = "AboutNirvana" })">About Nirvana</a>
</div>

<div>
    <a href="@Url.RouteUrl(new { controller = "Music", action = "AboutGrungeRock" })">About Grunge Rock</a>
</div>

<div>
    <a href="@Url.RouteUrl(new { controller = "Music", action = "AboutMusician" })">About Musician</a>
</div>

<div>
    <a href="@Url.RouteUrl(new { controller = "Music", action = "AmILogged" })">Am I Logged?</a>
</div>

<div>
    <a href="@Url.RouteUrl(new { controller = "Music", action = "Anyone" })">Anyone</a>
</div>


<p>
    <div>
        @if (!string.IsNullOrWhiteSpace(ViewBag.UserName)) 
        { 
            <a href="@Url.RouteUrl(new { controller = "Security", action = "SignOut" })">Sign Out</a>
        }
        else
        {
            <a href="@Url.RouteUrl(new { controller = "Security", action = "Login" })">Login</a>
        }        
    </div>
</p>



Final Step. Create a controller that will test the authorization. Note that while GenericPrincipal's roles parameter is array-based, the Authorize's Roles property is comma-delimited

Final.1. Controller:
using System.Web.Mvc;

namespace ReadyAspNetMvc.Controllers
{
    public class MusicController : Controller
    {
        
        
        [Authorize(Roles="Beatles")]
        public ViewResult AboutBeatles()
        {
            string userName = System.Web.HttpContext.Current.User.Identity.Name;
            ViewBag.Message = string.Format("Hello {0}! Beatles is the greatest rock band", userName);
            return Greet();
        }


        [Authorize(Roles = "Nirvana")]
        public ViewResult AboutNirvana()
        {
            string userName = System.Web.HttpContext.Current.User.Identity.Name;
            ViewBag.Message = string.Format("Hello {0}! Nirvana is the greatest grunge band", userName);
            return Greet();
        }

        [Authorize(Roles = "Beatles,Nirvana")]
        public ViewResult AboutGrungeRock()
        {
            string userName = System.Web.HttpContext.Current.User.Identity.Name;
            ViewBag.Message = string.Format("Hello {0}! This is grunge rock", userName);
            return Greet();
        }


        [Authorize(Roles = "Musician")]
        public ViewResult AboutMusician()
        {
            string userName = System.Web.HttpContext.Current.User.Identity.Name;
            ViewBag.Message = string.Format("Hello {0}! You are a music inventor", userName);
            return Greet();
        }
                

        [Authorize]
        public ViewResult AmILogged()
        {
            string userName = System.Web.HttpContext.Current.User.Identity.Name;
            ViewBag.Message = string.Format("Yes {0}!", userName);
            return Greet();
        }

        public string Anyone()
        {
            return "<b>Anyone</b>";
        }

        public ViewResult Greet()
        {
            return View("Greet"); 

            // return View(); // If we do this, when we visit /Music/AboutMusician ASP.NET MVC will try to find AboutMusician.cshtml instead of Greet.cshtml
        }


    }
}

Final.2. Create the view: /Views/Music/Greet.cshtml:
<h2>@ViewBag.Message</h2>


<a href="@Url.RouteUrl(new { controller = "Home", action = "Welcome" })">Back to Home Welcome</a>




Happy Coding!

Saturday, October 11, 2014

Refactoring peace of mind with ASP.NET MVC

If the ASP.NET MVC view has this code:
// http://stackoverflow.com/questions/6852979/get-current-controller-in-view

string controller = (string)this.ViewContext.RouteData.Values["controller"];
string action = (string)this.ViewContext.RouteData.Values["action"]; // can use this too: (string)this.ViewContext.Controller.ValueProvider.GetValue("action").RawValue;

if (controller == "Companies" && action == "Search") 
{
    ...
}


We can improve it by making it refactoring-friendly:
Type controllerType = ViewContext.Controller.GetType();
string action = (string)this.ViewContext.RouteData.Values["action"]; // can use this too: (string)this.ViewContext.Controller.ValueProvider.GetValue("action").RawValue;

if (controllerType == typeof(Erp.Controllers.CompaniesController) 
    && action == StaticReflection.GetMemberName<Erp.Controllers.CompaniesController>(m => m.Search(null)) 
{
    ...
}


The refactoring enabler:
namespace Erp.Helper
{
    // http://joelabrahamsson.com/getting-property-and-method-names-using-static-reflection-in-c/
    public class StaticReflection
    {
        public static string GetMemberName<T>(System.Linq.Expressions.Expression<Func<T, object>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            return GetMemberName(expression.Body);
        }

        public static string GetMemberName<T>(System.Linq.Expressions.Expression<Action<T>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            return GetMemberName(expression.Body);
        }

        private static string GetMemberName(System.Linq.Expressions.Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            if (expression is System.Linq.Expressions.MemberExpression)
            {
                // Reference type property or field
                var memberExpression =
                    (System.Linq.Expressions.MemberExpression)expression;
                return memberExpression.Member.Name;
            }

            if (expression is System.Linq.Expressions.MethodCallExpression)
            {
                // Reference type method
                var methodCallExpression = (System.Linq.Expressions.MethodCallExpression)expression;
                return methodCallExpression.Method.Name;
            }

            if (expression is System.Linq.Expressions.UnaryExpression)
            {
                // Property, field of method returning value type
                var unaryExpression = (System.Linq.Expressions.UnaryExpression)expression;
                return GetMemberName(unaryExpression);
            }

            throw new ArgumentException("Invalid expression");
        }

    }
}



No approach is complete if it is not wrapped in a fluent API. Now your code is completely free of string, typos could be avoided:
if (ViewContext.Controller.Verify<Erp.Controllers.CompaniesController>().IsTheContext())
{
    ...
}


If we want to detect both controller and action:
if (ViewContext.Controller
    .Verify<Erp.Controllers.CompaniesController>().WithAction(m => m.Search(null)).IsTheContext())
{
    ...
}





The supporting API:
public static class StaticReflectionExtension
{
    public static ControllerDetector<T> Verify<T>(this System.Web.Mvc.ControllerBase controller) 
        where T : System.Web.Mvc.ControllerBase
    {
        return new ControllerDetector<T>(controller);
    }
}

public class ControllerDetector<T> where T : System.Web.Mvc.ControllerBase
{
    System.Web.Mvc.ControllerBase _controller;
    string _action = "";
    public ControllerDetector(System.Web.Mvc.ControllerBase controller) 
    {
        _controller = controller;
    }

    public bool IsTheContext()
    {
        return 
            _controller.GetType() == typeof(T)
            && 
            (
                _action == ""

                ||

                _action == (string)_controller.ValueProvider.GetValue("action").RawValue
            );
    }

    public ControllerDetector<T> WithAction(System.Linq.Expressions.Expression<Func<T, object>> expression)
    {            
        _action = StaticReflection.GetMemberName<T>(expression);
        return this;
    }
}




Happy Coding!

Saturday, October 4, 2014

DryIoc + ASP.NET MVC + NHibernate boilerplate code

IoC wiring:

using Erp.Controllers;

using DryIoc;

using System;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

using System.Linq;


namespace Erp
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {

        
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);


            var ioc = new DryIocDependencyResolver();
            ControllerBuilder.Current.SetControllerFactory(ioc);          
        }
    }


    class DryIocDependencyResolver : System.Web.Mvc.DefaultControllerFactory
    {
        DryIoc.Container _container;

        public DryIocDependencyResolver()
        {
            _container = new DryIoc.Container();
            RegisterTheIocs();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            System.Web.Mvc.IController ic = controllerType == null
                ? null
                : (System.Web.Mvc.IController)_container.Resolve(controllerType);       

           // _container.ResolvePropertiesAndFields(ic);  // uncomment this if you want to use DI on controller's properties

           return ic;
        }

        void RegisterTheIocs()
        {
            // ISessionFactory definition

            System.Reflection.Assembly assembly = typeof(HomeController).Assembly;

            foreach (var controller in assembly.GetTypes().Where(t => typeof(Controller).IsAssignableFrom(t)))
            {
                _container.Register(controller, DryIoc.Reuse.InResolutionScope);                
            }


            // _container.Register(typeof(Elmah.Mvc.ElmahController), DryIoc.Reuse.InResolutionScope);

            // http://www.ienablemuch.com/2013/11/typical-nhibernate-sessionfactory.html
            _container.RegisterDelegate<NHibernate.ISessionFactory>(x => Erp.DomainMapping.Mapper.SessionFactory, DryIoc.Reuse.Singleton);

            _container.Register<UnitTestFriendlyDal.IDataStore, UnitTestFriendlyDal.DataStore>(DryIoc.Reuse.InResolutionScope);
        }

  
    }
}



Sample Controller, we need to call _ds.Dispose() to effect the transaction:
using Erp.Domain.TheModels;
using Erp.Dto.Dtos;

using UnitTestFriendlyDal;

using System.Web.Mvc;


namespace Erp.Controllers
{
    public class CompaniesController : Controller
    {
        IDataStore _ds;

        public CompaniesController(IDataStore ds)
        {
            _ds = ds;
        }
        

        public ViewResult Summary(int id = 0)
        {
            var company = _ds.Get<company>(id);
            
            var companyDto = company.Summary(_ds);

            return View(companyDto);
        }

        
        public ViewResult Search(SearchedCompanyDto sc)
        {
            Company.SearchCompanies(_ds, sc);            

            return View(sc);
        }


        protected override void Dispose(bool disposing)
        {
            _ds.Dispose();
            base.Dispose(disposing);
        }
    }
}



IDataStore definition:
using System;
using System.Linq;

using NHibernate.Linq;

namespace UnitTestFriendlyDal
{
    public interface IDataStore : IDisposable
    {
        IQueryable<T> Query<T>();
        T Get<T>(object id);
        T Load<T>(object id);
        object Save(object transientObject);
    }

    public class DataStore : IDataStore
    {

        NHibernate.ISessionFactory _sessionFactory;
        NHibernate.ISession _session;
        NHibernate.ITransaction _transaction;

        public DataStore(NHibernate.ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
            _session = _sessionFactory.OpenSession();
            _transaction = _session.BeginTransaction();
        }


        IQueryable<T> IDataStore.Query<T>()
        {
            return _session.Query<T>();
        }


        T IDataStore.Get<T>(object id)
        {
            return _session.Get<T>(id);
        }

        T IDataStore.Load<T>(object id)
        {
            return _session.Load<T>(id);
        }

        object IDataStore.Save(object transientObject)
        {
            return _session.Save(transientObject);
        }

        
        // Because transaction is a cross-cutting concern. It should be automated
        void IDisposable.Dispose()
        {
             // http://www.hibernatingrhinos.com/products/nhprof/learn/alert/donotuseimplicittransactions
                    
            _transaction.Commit();
            _transaction.Dispose();
            _session.Dispose();
        }




    }



    /// <summary>
    /// cross-cutting concern    
    /// MakeCacheable replaces Cacheable, so IQueryable detection provider can be done here
    /// Can't use NHibernate's built-in .Cacheable on non-NHibernate IQueryable, it will throw an error    
    /// </summary>
    public static class NHibernateLinqExtensionMethods
    {
        public static IQueryable<T> MakeCacheable<T>(this IQueryable<T> query)
        {
            if (query.Provider.GetType() == typeof(NHibernate.Linq.DefaultQueryProvider))
                query = query.Cacheable();

            return query;
        }


    }

}



Sample Domain Model:
using Erp.Dto.Dtos;

using UnitTestFriendlyDal;


using System.Linq;



[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("Erp.Domain.Test")]

namespace Erp.Domain.TheModels
{
    
    // What is Anemic Domain Model
    // http://en.wikipedia.org/wiki/Anemic_domain_model
    // http://www.martinfowler.com/bliki/AnemicDomainModel.html    

    // this is a non-Anemic Domain Model
    public class Company
    {
        // Use Virtuosity.Fody to eliminate the use of virtual keywords on NHibernate
        // https://www.nuget.org/packages/Virtuosity.Fody/
        // http://github.com/Fody/Virtuosity

        public int CompanyId { get; protected internal set; }
        public string CompanyName { get; protected internal set; }
        public string CompanyUrl { get; protected internal set; }
        public byte[] TinyLogo { get; protected internal set; }



        public static void SearchCompanies(IDataStore ds, SearchedCompanyDto sc)
        {           
            var q = from c in ds.Query<Company>().MakeCacheable()
                    where string.IsNullOrWhiteSpace(sc.q) || c.CompanyName.StartsWith(sc.q)
                    select c;
            
            // http://www.ienablemuch.com/2014/10/proper-way-to-query-reference-entity.html
            sc.Companies = q.ToList().Select(c => new CompanyDto { CompanyId = c.CompanyId, CompanyName = c.CompanyName, TinyLogo = c.TinyLogo });            
        }

        // Just ready this parameter IDataStore,
        // so if this one entity need  information from another domain model(s) that is not in its navigable properties, 
        // it's easy to access those information
        public CompanyDto Summary(IDataStore ds)
        {                        
            return new CompanyDto { CompanyName = this.CompanyName };
        }
    }
}




Sample Unit Test:
using Erp.Domain.TheModels;

using UnitTestFriendlyDal;

using Erp.Dto.Dtos;

using Moq;

using System.Linq;


using Microsoft.VisualStudio.TestTools.UnitTesting;



namespace Erp.Domain.Test
{
    [TestClass]
    public class UnitTest
    {
        [TestMethod]
        public void Test_if_search_code_is_done()
        {

            // Arrange
            var companies = new[] {
                new Company { CompanyName = "Alpha" },
                new Company { CompanyName = "Beta" },
                new Company { CompanyName = "Delta" },
                new Company { CompanyName = "Atlantis" },
            }.AsQueryable();


            var ds = new Mock<IDataStore>();
            ds.Setup(x => x.Query<Company>()).Returns(companies);



            // Act
            var sc = new SearchedCompanyDto { q = "A" };
            Company.SearchCompanies(ds.Object, sc);



            // Assert
            Assert.AreEqual(2, sc.Companies.Count());
        }
    }
}



Happy Coding!

Monday, September 29, 2014

Modular code organization in ASP.NET MVC

Hated the way ASP.NET MVC imposes the sock drawer approach to your code organization?


Instead of sock drawer approach, we could organize our code into modules


Here's how we wanted our code organization be like:




With modular approach, the controller and its views are grouped together. Though you will lose the IDE navigation aspect of controller<->view, you won't miss it much as your contoller and its views are near each other



Notice that we didn't include {controller} in the url, the controller and module are the same thing

And notice too that we hardcoded the controller to "_"

"_Controller" won't work, ASP.NET MVC always append Controller to the controller parameter. Hence "_" is enough

using System.Web.Mvc;

namespace JustAspNetMvcThing.Areas.Hey
{
    public class HeyAreaRegistration : AreaRegistration
    {
        public override string AreaName { get{ return "Hey"; } }

        public override void RegisterArea(AreaRegistrationContext context)
        {

            // Add this:
            context.MapRoute(
                name: "Hey_Jude_Dont_Make_SandwichModule_default",
                url: "Hey/Jude/Dont/Make/SandwichModule/{action}/{id}",
                defaults: new { action = "Index", id = UrlParameter.Optional, controller = "_" },                
                // instead of this string-based code: "JustAspNetMvcThing.App.Hey.Jude.Dont.Make.SandwichModule" parameter, we could use typeof:
                namespaces: new[] { typeof(JustAspNetMvcThing.App.Hey.Jude.Dont.Make.SandwichModule._Controller).Namespace }
            );

            context.MapRoute(
                "Hey_default",
                "Hey/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}

using System.Web.Mvc;

namespace JustAspNetMvcThing.Areas.Let
{
    public class LetAreaRegistration : AreaRegistration
    {
        public override string AreaName
        {
            get
            {
                return "Let";
            }
        }

        public override void RegisterArea(AreaRegistrationContext context)
        {

            // Add this:
            context.MapRoute(
                name: "Let_It_BeModule_default",
                url: "Let/It/BeModule/{action}/{id}",
                defaults: new { action = "Index", id = UrlParameter.Optional, controller = "_" },
                namespaces: new[] { typeof(JustAspNetMvcThing.App.Let.It.BeModule._Controller).Namespace }
            );
  

            context.MapRoute(
                "Let_default",
                "Let/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}



Here's the base controller for our modules:
using System.Collections.Generic;
using System.Linq;

using System.Web.Mvc;

namespace JustAspNetMvcThing.App
{
    
    public class AppBaseController : Controller
    {

        public ViewResult View(object model, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") 
        { 
                return TheView(model, /*viewName*/ null, memberName); 
        }


        public ViewResult View(string viewName = null, string masterName = null, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
        {
            return TheView((object)null, viewName, memberName);
        }


        ViewResult TheView(object model, string viewName, string memberName)
        {                        
            // Skip(1) skips JustAspNetMVcThing
            // SkipLastN excludes the controller

            // Sample output: App/Hey/Jude/Dont/Make/SandwichModule
            string modulePath = string.Join("/", this.GetType().FullName.Split('.').Skip(1).SkipLastN(1));

            // Sample output: /App/Hey/Jude/Dont/Make/SandwichModule/Bad.cshtml
            string viewFullPath =  "/" + modulePath + "/" + memberName + ".cshtml";
            
            return View(viewFullPath, model);
        }                
    }// class AppBaseController

    static class Helper
    {
        // Because .Reverse() is bad: http://stackoverflow.com/questions/4166493/drop-the-last-item-with-linq#comment4498849_4166546
        public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n)
        {
            var it = source.GetEnumerator();
            bool hasRemainingItems = false;
            var cache = new Queue<T>(n + 1);

            do
            {
                if (hasRemainingItems = it.MoveNext())
                {
                    cache.Enqueue(it.Current);
                    if (cache.Count > n)
                        yield return cache.Dequeue();
                }
            } while (hasRemainingItems);
        }
    }//class Helper
 
}//namespace


The Sandwich module, notice the _Controller name? We do that, so the controller will always sort first in the folder
using JustAspNetMvcThing.Models;

using System.Web.Mvc;

namespace JustAspNetMvcThing.App.Hey.Jude.Dont.Make.SandwichModule
{
    public class _Controller : AppBaseController
    {        
        // GET: /Hey/Jude/Dont/Make/SandwichModule/Bad/1
        public ViewResult Bad(int id = 0)
        {
            var p = new Person { FirstName = "Paul " + id };

            return View(p);
        }

    }
}

Here's one of the SandwichModule's views. Noticed that we can't use @model anymore. @model ModelHere is just a shorthand for @inherits System.Web.Mvc.WebViewPage<ModelHere>
@inherits System.Web.Mvc.WebViewPage<JustAspNetMvcThing.Models.Person>

Hello @Model.FirstName


The Be module:
using System.Web.Mvc;

namespace JustAspNetMvcThing.App.Let.It.BeModule
{
    public class _Controller : AppBaseController
    {        
        // GET: /Let/It/BeModule/ThePersonYouWantToBe
        public ActionResult ThePersonYouWantToBe()
        {
            return View();
        }

    }
}


Here's one of the BeModule's views:
@inherits System.Web.Mvc.WebViewPage

I am me!




Happy Coding!

Nested routing and folder in ASP.NET MVC Redux

Here's a way to make nested routing and folder in ASP.NET MVC: http://www.ienablemuch.com/2014/09/nested-routing-and-folder-in-aspnet-mvc.html


However, I find the code organization of that approach is not symmetrical enough, I wanted the code organization of controller and views be mirror image of each other, like this:





Here's the supporting code:

using System.Collections.Generic;
using System.Linq;

using System.Web.Mvc;

namespace JustAspNetMvcThing.App
{
    
    public class AreaBaseController : Controller
    {
        public ViewResult View(object model, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") 
        { 
            return TheView(model, /*viewName*/ null, memberName); 
        }

        
        public ViewResult View(string viewName = null, string masterName = null, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") 
        {             
            return TheView(/*model*/ (object)null, viewName, memberName); 
        }

        ViewResult TheView(object model, string viewName, string memberName)
        {
            if (viewName != null && model == null)
                return View(viewName, model: null);

            
            string origName = this.GetType().Name;
            // "Controller" string is stripped from "ItController"
            // "Controller" is stringly-typed, there's a better way, just wanted to make a short code
            // Sample output: It
            string controllerName = origName.Substring(0, origName.Length - "Controller".Length);

            // Skip(3) skips JustAspNetMVcThing.App.Controllers
            // SkipLastN excludes the controller, example: Hey/Jude/Dont/Make
            // Sample output: Hey/Jude/Dont/Make/It
            string controllerPath = string.Join("/", this.GetType().FullName.Split('.').Skip(3).SkipLastN(1)) + "/" + controllerName;

            // Sample output: /App/Views/Hey/Jude/Dont/Make/It/Bad.cshtml
            string viewFullPath =  "/App/Views/" + controllerPath + "/" + memberName + ".cshtml";
            
            return View(viewFullPath, model);
        }                
    }

    static class Helper
    {
        // Because .Reverse() is bad: http://stackoverflow.com/questions/4166493/drop-the-last-item-with-linq#comment4498849_4166546
        public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n)
        {
            var it = source.GetEnumerator();
            bool hasRemainingItems = false;
            var cache = new Queue<T>(n + 1);

            do
            {
                if (hasRemainingItems = it.MoveNext())
                {
                    cache.Enqueue(it.Current);
                    if (cache.Count > n)
                        yield return cache.Dequeue();
                }
            } while (hasRemainingItems);
        }
    }

}


Then do this on the Area registration:
using System.Web.Mvc;

namespace JustAspNetMvcThing.Areas.Hey
{
    public class HeyAreaRegistration : AreaRegistration
    {
        public override string AreaName { get{ return "Hey"; } }

        public override void RegisterArea(AreaRegistrationContext context)
        {

            // Add this:
            context.MapRoute(
                name: "Hey_Jude_Dont_Make_default",
                url: "Hey/Jude/Dont/Make/{controller}/{action}/{id}",
                defaults: new { action = "Index", id = UrlParameter.Optional },
                namespaces: new[] { "JustAspNetMvcThing.App.Controllers.Hey.Jude.Dont.Make" }
            );

            context.MapRoute(
                "Hey_default",
                "Hey/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}


Voila! Your controller can continue its normal business. The View(object) resolves to View(object,memberName="") hence we are able to hook the normal View signature from the controller that inherits from AreaBaseController
using JustAspNetMvcThing.Models;

using System.Web.Mvc;

namespace JustAspNetMvcThing.App.Controllers.Hey.Jude.Dont.Make
{
    public class ItController : AreaBaseController
    {        
        // GET: /Hey/Jude/Dont/Make/It/Bad/1
        public ViewResult Bad(int id = 0)
        {
            var p = new Person { FirstName = "Paul " + id };

            return View(p);
        }

    }
}


Lastly, we must use @inherits instead of @model on our views. @model is a shorthand for @inherits
@inherits System.Web.Mvc.WebViewPage<JustAspNetMvcThing.Models.Person>  

Hello @Model.FirstName




Happy Coding!

Sunday, September 28, 2014

Nested routing and folder in ASP.NET MVC

Nested routing and folder is doable in Laravel

Ever wanted to have a nested url and folder in ASP.NET MVC too? e.g., http://example.com/Hey/Jude/Dont/Make/It/Bad/1

It's doable in ASP.NET MVC by straying outside of convention-over-configuration. First, configure the routing, and then be explicit in your controller when specifying your view's path. Voila! you can now place your view anywhere you wanted it to, but most likely you'll want the folder hierarchy be symmetrical with your url

To cut to the chase, here it is in picture:



Note that we can't use @model directive on our view anymore with the approach above. So we have to explicitly use @inherits. @model ModelHere is shorthand for @inherits System.Web.Mvc.WebViewPage<ModelHere>, achieves the same thing. For details, see this: http://stackoverflow.com/questions/8127462/the-view-must-derive-from-webviewpage-or-webviewpagetmodel


Visual Studio cannot navigate anymore to your view with the approach above, however with ReSharper it's still able to navigate the view from your controller



Alternatively, if you don't like the sock drawer approach, you can use modular approach, here's one: http://www.ienablemuch.com/2014/09/modular-code-organization-in-aspnet-mvc.html

Wednesday, December 18, 2013

Say No to ViewData and ViewBag

We've all been there, we got a perfect Model/ViewModel for the view…


public class HomeController : Controller
{

    public ActionResult Index()
    {

        var people = new[]
        {
            new Person { Firstname = "Ely", Lastname = "Buendia"},
            new Person { Firstname = "Raymund", Lastname = "Marasigan"},
            new Person { Firstname = "Buddy", Lastname = "Zabala"},
            new Person { Firstname = "Marcus", Lastname = "Adoro"},
        };

        return View(people);

    } // Index Action

} //Home Controller    


View:

@model IEnumerable<TestAdhocViewModel.Models.Person>

 
<table>    
@foreach (var p in Model)
{
    <tr>
        <td>@p.Firstname</td>
        <td>@p.Lastname</td>
    </tr>
}
</table>


...and then one day, there's a new requirement to bring new info on the view.

You can either put it in ViewData or ViewBag. However, you reluctantly wanted to put those info on ViewData/ViewBag. It pains you to see the contract(Model/ViewModel) between the controller and the view is being violated in the broad daylight, the various information are located in disparate sources, some of them are stored in strongly-typed Model/ViewModel but some are in ViewData/ViewBag. ViewData/ViewBag mars the beauty of operating exclusively within the domain of Model/ViewModel.


You can serialize/deserialize the anonymous types with JSON, but you will lose the benefit of autocomplete. Dynamic types are not navigable. Just because you could doesn't mean you should


So it's safe to say we all love our strongly-typed language. C#, a language so strongly-typed so we don't have to type strongly, thanks autocomplete! :D


We can introduce a view model, but it's too ceremonial when we just wanted to bring only a few new info to view. Scratch that, there's nothing ceremonial with view model when it's done on an ad hoc approach. Whether or not there is really a place for adhocly things to occur on a very formalized system(system that leads to class explosions(read: gazillion of classes), tends to be rampant on strongly-typed language) is up for another contentious discussion; we will not go into that.


When an approach is too ceremonial(read: tedious), we tend to forego that approach, we tend to use the duct tape approach, e.g., we tend to use ViewData/ViewBag, Session


What we really don't want is a view model that is just being used on one part of the system only, yet it pollutes the global namespace; classes in global namespace tends to give the illusion that a view model is very reusable. We wanted that ad hoc view model to be local on one part of the system only, that is that view model should be visible on that part only.


Without further ado, just declare the ViewModel right on the controller only

public class HomeController : Controller
{

    public ActionResult Index()
    {

        var people = new[]
            {
                new Person { Firstname = "Ely", Lastname = "Buendia"},
                new Person { Firstname = "Raymund", Lastname = "Marasigan"},
                new Person { Firstname = "Buddy", Lastname = "Zabala"},
                new Person { Firstname = "Marcus", Lastname = "Adoro"},
            };


        var band = new Band
            {
                BandName = "Eraserheads",
                Members = people
            };
        
        return View(band);

    } // Index Action


    public class Band
    {
        public string BandName { get; set; }
        public IEnumerable<Person> Members { get; set; }
    }

} //Home Controller



View:


@model TestAdhocViewModel.Controllers.HomeController.Band

 
@Model.BandName

 
<table>        
    @foreach (var p in Model.Members)
    {
        <tr>
            <td>@p.Firstname</td>
            <td>@p.Lastname</td>
        </tr>
    }
</table>


If it still feels like you violated a contract between your controller and view, as there's a big impact on your code when the shape of your object changes, i.e., the code changed to: foreach(var p in Model.Members) instead of retaining the old code: foreach(var p in Model), and you wanted a lesser change in your code, tap the features of your language. In C#, you can provide an enumerator for your class instance. Redefine your class to this:


public class Band : IEnumerable<Person>
{
    public string BandName { get; set; }
    // using internal, Members property is accessible to controller only, is not visible on views
    internal IEnumerable<Person> Members { get; set; }

    public IEnumerator<Person> GetEnumerator() { return Members.GetEnumerator(); }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}

With that, you can use the old code on foreach, i.e., you can just use the Model directly on foreach:


@model TestAdhocViewModel.Controllers.HomeController.Band

 
@Model.BandName

 
<table>        
    @foreach (var p in Model)
    {
        <tr>
            <td>@p.Firstname</td>
            <td>@p.Lastname</td>
        </tr>
    }
</table>


For single object, you can apply the same concept above, just use inheritance, if you have this code..

public ViewResult Detail(int id)
{
    var p = new Person
        {
            Firstname = "Ely",
            Lastname = "Buendia"
        };
    
    return View(qp);
} 

View:
@model TestAdhocViewModel.Models.Person

 
<p>Firstname: @Model.Firstname</p>
<p>Lastname: @Model.Lastname</p>


..and you wanted just to bring another information to view, create a derived class inside the controller and inherit the existing Model/ViewModel:

public class HomeController : Controller
{
    public ViewResult Detail(int id)
    {
        var p = new Person
            {
                Firstname = "Ely",
                Lastname = "Buendia"
            };
        var qp = new QualifiedPerson();
        qp.Firstname = p.Firstname;
        qp.Lastname = p.Lastname;
        qp.IsQualified = true;
        
        return View(qp);
    } 

    public class QualifiedPerson : Person
    {
        public bool IsQualified { get; set; }
    }
    
} //Home Controller

This is now your view with minimal modification, just change the model on model directive:


@model TestAdhocViewModel.Controllers.HomeController.QualifiedPerson
 
<p>Firstname: @Model.Firstname</p>  @* Look Ma! No Changes! *@
<p>Lastname: @Model.Lastname</p> @* Look Pa! No Changes! *@
<p>Qualified: @Model.IsQualified</p>

Assigning each values from base class to derived class is tedious though. Just use the awesome ValueInjecter, a.k.a. Omu Value Injecter



public class HomeController : Controller
{
    public ViewResult Detail(int id)
    {
        var p = new Person
            {
                Firstname = "Ely",
                Lastname = "Buendia"
            };
        var qp = new QualifiedPerson();
        qp.InjectFrom(p);
 
        qp.IsQualified = true;

        return View(qp);
    } 

    public class QualifiedPerson : Person
    {
        public bool IsQualified { get; set; }
    }
    
} //Home Controller


Then just use the InjectFrom extension method to copy the values from base class to derived class


Another beauty of placing the new information to their adhoc model/viewmodel, when the model/viewmodel doesn't feel adhocly anymore (e.g., it can be reused, merging multiple model/viewmodel service calls into one), it's easy to move that model/viewmodel to global namespace (e.g., to WCF), and it will only incur minimal adjustments to your controller and view. Contrast that to passing your related information to view in a non-cohesive manner, i.e., some of the info are in model/viewmodel and some are in ViewData/ViewBag, it will incur more adjustments to your code to use the refactored related info. Use ViewData and ViewBag sparingly.


Can we say goodbye to ViewData and ViewBag now?


Happy Coding! ツ

Monday, July 1, 2013

What's rawer than Razor's raw?

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@{
    System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");
    decimal d = 1234567.89M;
}


@d

<hr />

@Html.Raw(d)



The output is:

1234567,89 
1234567,89

I wanted to return these though:
1234567,89
1234567.89


Yeah I know, raw != English-centric, the world doesn't revolve around English culture. However, the programming language's raw(or culture if you may) is English-centric, i.e., we write one milllion and two hundred thirty four thousand five hundred sixty seven pesos and eighty nine centavos as 1234567.89 not 1234567,89

I want to use 1234567.89 even I'm in other culture. What's the use of that whimsical need? It's easier to bridge server-side values to javascript when the programming language format is preserved properly.

This view...

@{
    System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("fr-FR");
    decimal d = 1234567.8912M;
}

<script>    
    var x = @d;
    alert(x);    
</script>

...is materialized to html as the following, an invalid syntax, hence the alert will not pop-up:

<script>    
    var x = 1234567,89;
    alert(x);    
</script>


Any suggestion? I'll post this stackoverflow and will keep you posted when an answer come up


UPDATE: Feb 19, 2014


Some answers are just coming up when there's a damning need for a solution to a problem. A colleague has a problem on his Razor view. His problem is similar to above, he's passing a Model(with numeric values) from controller to view, and in turn his view is passing the model's numeric values to a JavaScript control, this is his razor view:


<script>
var c = chart.setup({ LowestScore : @Model.LowestScore, HighestScore : @Model.HighestScore });
</script>


However this is the HTML output of Razor view:
<script>
var c = chart.setup({ LowestScore : 12345,67 , HighestScore : 56789,12 });
</script>


Spot the error? It's not a valid JavaScript, the cents gets separated from the values. The problem stems from the razor being locale-aware, so if you set the language to French, the fraction will get emitted with comma separator. To fix the problem, we just serialize the values right there on View. Why on view and not on controller? If we do it on controller we are compelled to introduce another property on the model for the JSON string of the values we wanted to pass from the model, or worse yet we have to marshal it to ViewBag or ViewData from the controller. With that in mind, this is the best way to tackle the problem, serialize the values in View:

<script>
    @{
        string j = Newtonsoft.Json.JsonConvert.SerializeObject(
            new 
            { 
                LowestScore = Model.LowestScore,
                HighestScore = Model.HighestScore,                 
            });
    }

    var jsonObject = @Html.Raw(j);
    var c = chart.setup(jsonObject);
</script>



HTML generated:
<script>
    var jsonObject = {"LowestScore":12345.67,"HighestScore":56789.12};
    var c = chart.setup(jsonObject);
</script>



I hope I'm wrong with my approach above, if there's a shorter way to do it in Razor, i.e. its localization can be turned off programatically, I'll be more than glad to know.



Happy Coding! ツ

Saturday, May 11, 2013

ASP.NET MVC IDE-friendly URL

Instead of using hardcoded url or Url.Content...

var theUrl = '@Url.Content("~/MyPeople/OffCycleCompensation/IsEligible")';        
 
        $.ajax({
            url: theUrl,
            type: 'POST',
            async: false,
            data: { personID : personID },
            success: function (data) {


...use IDE-friendly URL helpers to make maintenance programming easier, e.g. use Url.RouteUrl:


var theUrl = '@Url.RouteUrl("AreaRoute", new { area = "MyPeople", controller= "OffCycleCompensation", action="IsEligible" })';        
 
        $.ajax({
            url: theUrl,
            type: 'POST',
            async: false,
            data: { personID : personID },
            success: function (data) {
 


Advantage being, the code is more navigable, just hold Ctrl and press left click on IsEligible, the IDE will bring you to IsEligible method. If you are using ReSharper, just position the cursor on IsEligible and press Ctrl+B



Little things that works wonders



Happy Coding! ツ

Sunday, November 25, 2012

ASP.NET MVC returned type discipline

A few times I'm seeing this in code...


public ActionResult GetSoap()
{
    return Json(new Soap { Name = "Tide", Feature = "With Calamansi" } );
}



...instead of the following one in which you can easily infer the code's intent:

public JsonResult GetSoap()
{
    return Json(new Soap { Name = "Tide", Feature = "With Calamansi" } );
}


That's deliberately leaking an abstraction. A devil's advocate would suppose you are just showing off your deep knowledge on ASP.NET MVC's object hierarchy, that you know that JsonResult derives from ActionResult, thus you can just make every MVC actions' return type an ActionResult. Well guess what, everything derives from object anyway, hence just make everything return an object and it will still compile and run:


public object GetSoap()
{
    return Json(new Soap { Name = "Tide", Feature = "With Calamansi" } );
}


The problem with that approach(and the ActionResult code above for that matter) is you need to cast the returned results to its actual returned type(JsonResult) when you need to do TDD on that method. Hence warranting an explicit casting.


[Test]
public void SomeTest()
{
    // unnecessary casting
    JsonResult r = (JsonResult) GetSoap(); 
    
    Soap s = (Soap)r.Model;    
}


Whereas if you declare the MVC method to return its actual returned type, i.e. JsonResult, you don't need to do extra casting when doing unit test on it:


[Test]
public void SomeTest()
{
    JsonResult r = GetSoap(); 
    
    Soap s = (Soap)r.Model;    
}


Giving the controller action's return type an specific type makes the code intent clearer

Happy Coding! ツ


Sunday, April 29, 2012

ASP.NET MVC Editor Templates

EditorTemplates reminds me of college days using assembly language. Copying bytes using MOV and LOOP instruction could get the job done, but not knowing the simpler way(REP MOVSB) to do this solved problem makes your code not as readable or maintainable as it could possibly be. Seeing many loops in code and deciphering their intent is counter-productive.


As much as we want to believe in this axiom "If At First You Don't Succeed, Remove All Evidence You Ever Tried", there's something to be said for knowing how a bad code looks like. With this in mind, this is not what to do in an ASP.NET MVC view:


~/Views/Home/Index.cshtml
@model SoQna.ViewModels.QnaViewModel

@using (Html.BeginForm("SubmitAnswers", "Home"))
{
    int i = 0;
    foreach (SoQna.ViewModels.AnswerToQuestion answer in Model.Answers)
    {
        @: Question #@(answer.ToQuestionId) <br />

        @Html.Hidden("Answers[" + i + "].ToQuestionId", answer.ToQuestionId)
        @Html.DisplayFor("Answers[" + i + "].QuestionText", answer.QuestionText)

        <p />

        @Html.TextArea("Answers[" + i + "].AnswerText", answer.AnswerText)

        <hr />
        
        ++i;
    }

    <input type="submit" value="Done" />
}

Sure that code is very model-centric and capable of being directly usable by our controller code...


// POST /Home/SubmitAnswers

[HttpPost]
public ViewResult SubmitAnswers(SoQna.ViewModels.QnaViewModel a)
{
    foreach (SoQna.ViewModels.AnswerToQuestion answer in a.Answers)
    {
        answer.QuestionText = _qnaRepo.Single(x => x.QuestionId == answer.ToQuestionId).QuestionText;
    }
    return View(a);
}


...but the problem with the ~/Views/Home/Index.cshtml view above is we cannot use strongly-typed model on html helpers. As much as possible, with strongly-typed framework such as ASP.NET MVC, we should not use magic strings in our code. We should let strong typing take over the reins in our ASP.NET MVC app. With this in mind, we shall do this instead on ~/Views/Home/Index.cshtml:

@model SoQna.ViewModels.QnaViewModel

@using (Html.BeginForm("SubmitAnswers", "Home" ))
{    
    @Html.EditorFor(x => x.Answers) 
    <input type="submit" value="Done" />
}

Now you might ask, where's the loop? How does it know how to display the corresponding HTML for our object's properties?

On first question, Html.EditorFor does the loop for us if the property is an IEnumerable one. On second question, that's where we will use the EditorTemplates. When you want to use a pre-defined view(editor template) for a given model or view-model, you place that view in this folder ~/Views/Shared/EditorTemplates, but if you intend your pre-defined view for a given model/view-model be a controller-specific one, placed them in their specific controller folder, e.g. ~/Views/XXX/EditorTemplates where XXX is your controller name. ASP.NET MVC will first look for editor templates specific to your controller; if it cannot find one, it will look in ~/Views/Shared/EditorTemplates folder


To make our first editor template, please create an EditorTemplates folder on ~/Views/Home, given Home is your controller name. Then add an MVC 3 Partial Page (Razor) item to your ~/Views/Home/EditorTemplates folder, you do this by doing a right-click on EditorTemplates and selecting Add > New Item

Name your editor template Razor page after your model type or view-model type.

This is how our ~/Views/Home/EditorTemplates/AnswerToQuestion.cshtml shall look like:

@model SoQna.ViewModels.AnswerToQuestion

Question #@(Model.ToQuestionId) <br />
@Html.HiddenFor(x => x.ToQuestionId)
@Html.DisplayFor(x => x.QuestionText)

<p />

@Html.TextAreaFor(x => x.AnswerText)

<hr />

Here's a sample output:



Download: http://code.google.com/p/aspnet-mvc-demo-editor-templates/downloads/list

SVN: http://code.google.com/p/aspnet-mvc-demo-editor-templates/source/checkout

Thursday, April 19, 2012

ServiceStack Walkthrough. Screenshots guide

Create an ASP.NET MVC Project:




Add ServiceStack components (ServiceStack.dll, ServiceStack.Interfaces.dll):



Get the DLLs at: https://github.com/ServiceStack/ServiceStack/downloads


Add this line to Web.config's httpHandlers section:

<add path="yourServicestack*"
 type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />



Put this inside configuration section:


<location path="servicestack">
  <system.web>
    <httpHandlers>
      <add path="*" 
        type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" 
        verb="*" />
    </httpHandlers>
  </system.web>
</location>



Add these lines on RegisterRoutes:
routes.IgnoreRoute ("yourServicestack");
routes.IgnoreRoute ("yourServicestack/{*pathInfo}");



Add these Request,Response,Service classes in your Models directory:


class Hello  
{
 public string Name { get; set; }
   
}

class HelloResponse
{
 public string Result { get; set; }   
}

class HelloService : ServiceStack.ServiceHost.IService<Hello>
{
 public object Execute(Hello request) 
 {
  return  new HelloResponse { Result = "Good morning " + request.Name + "!" };
 }
} 









And add these other Request,Response,Service classes in Models directory too:


class Subtraction 
{ 
 public decimal Minuend { get; set; }  
 public decimal Subtrahend { get; set; }  
}


class SubtractionResponse 
{
 public decimal Difference { get; set; }
}

class SubtractionService : ServiceStack.ServiceHost.IService<Subtraction>
{
 public object Execute(Subtraction request) 
 {      
  return  new SubtractionResponse { Difference = request.Minuend - request.Subtrahend };
 }
}


Add these code in Global.asax.cs:


protected void Application_Start ()
{
 RegisterRoutes (RouteTable.Routes);
  
 new NextBillionAppHost().Init();
}



public class NextBillionAppHost : ServiceStack.WebHost.Endpoints.AppHostBase
{
 //Tell Service Stack the name of your application and where to find your web services
 public NextBillionAppHost() 
    : base("Billionaire Web Services", 
          typeof(DemoServiceStack.Models.SubtractionService).Assembly) { }

 public override void Configure(Funq.Container container)
 {       
  //register user-defined REST-ful urls
  Routes
   .Add<DemoServiceStack.Models.Hello>("/como-esta")
   .Add<DemoServiceStack.Models.Hello>("/como-esta/{Name}");
  
  Routes
   .Add<DemoServiceStack.Models.Subtraction>("/solve-subtraction")     
   .Add<DemoServiceStack.Models.Subtraction>("/solve-subtraction/{Minuend}/{Subtrahend}");
 }
}



Then run (shortcut key: command+option+enter), then type in address bar:


http://127.0.0.1:8080/yourServicestack

,then you shall see this:



Then under Operations, click the JSON tag of Subtraction operation, you shall see this:



Then type this url in the address bar:

http://127.0.0.1:8080/yourServicestack/solve-subtraction/2011/1955

You shall see this:



Then try to click the JSON, XML, etc, see the output.


You can use ServiceStack instead of WCF. REST-ful services is easier with ServiceStack. ServiceStack has a good programming model, i.e. the Request, Response and their Service has good cohesion

Sunday, November 27, 2011

Flexigrid CRUD(inline form) with ASP.NET MVC

In this post, I'll show you the supporting code for placing a form within flexigrid.


First, declare a variable for the html string of your form:

<script type="text/javascript">
    var formHtml = "";
    

    function canRender() {
        return _canRender;
    }

    $(function () {
        _canRender = false;
    });

</script>


We use canRender so we can defer the execution of javascript of the form's html string

<div id="editor" style="visibility: hidden">
    @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "theForm" }))
    {
        <fieldset>
            <legend>Person</legend>

            @Html.HiddenFor(x => x.PersonId)
            @Html.HiddenFor(x => x.RowVersion)

            <div class="editor-label">
                @Html.LabelFor(x => x.Username)
            </div>
            <div class="editor-field">
                @Html.EditorFor(x => x.Username)
                @Html.ValidationMessageFor(x => x.Username)
            </div>


            <div class="editor-label">
                @Html.LabelFor(x => x.Firstname)
            </div>
            <div class="editor-field">
                @Html.EditorFor(x => x.Firstname)
                @Html.ValidationMessageFor(x => x.Firstname)
            </div>

            <div class="editor-label">
                @Html.LabelFor(x => x.Lastname)
            </div>
            <div class="editor-field">
                @Html.EditorFor(x => x.Lastname)
                @Html.ValidationMessageFor(x => x.Lastname)
            </div>


            <div class="editor-label">
                @Html.LabelFor(x => x.Country.CountryId)
            </div>
            <div class="editor-field">

                <table style="border-style: none">
                <tr>
                <td style="border-style: none">
                @Html.AjaxComboBoxFor(x => x.Country.CountryId, "/Countries/Management/Lookup/", "/Countries/Management/Caption/",
                    new { }, new { sub_info = true, can_render_callback = "canRender", textbox_width = 200 })    
                </td>
                <td style="border-style: none">
                @Html.ValidationMessageFor(x => x.Country.CountryId)
                </td>
                </tr>
                </table>                     
            </div>
            

            <div class="editor-label">
                @Html.LabelFor(x => x.FavoriteNumber)
            </div>
            <div class="editor-field">
                @Html.EditorFor(x => x.FavoriteNumber)
                @Html.ValidationMessageFor(x => x.FavoriteNumber)                
            </div>

            <p>
                <input type="submit" value="Save" />
                <input type="button" id="Closer" value="Close" />
            </p>


            
        </fieldset>
        
        <div style="max-width: 500px; width: 500px;">
            @Html.JsAccessibleValidationSummary(excludePropertyErrors: true)            
        </div>
    }
<script type="text/javascript">

    $(function () {


        if (!canRender()) return;


        var scope = $('#theForm');
        parseDynamicContent(scope);




        $('#Closer', scope).click(function (e) {
            closeForm($(scope));
        });

        $('input[type=submit]', scope).click(function (e) {
            try {


                e.preventDefault();

                if (!scope.valid()) {
                    // alert('has invalid');
                    return;
                }

                save(scope);

                // closeForm(scope);
            } catch (e) {
                alert("Error " + e);
            }

        });

        $(scope).attr('id', guid());
    });

    function save(scope) {        

        $.ajax({
            url: '/People/Management/SaveViaAjax',
            type: 'POST',
            data: $(scope).serialize(),
            success: function (result) {

                var isOk = $(scope).modelValidation(result);


                if (isOk) {

                    var isNew = $('#PersonId', scope).val() == '';

                    if (isNew) {
                        $('#PersonId', scope).val(result.PersonId);
                    }

                    $('#RowVersion', scope).val(result.RowVersion);


                    if (isNew) {
                        $(scope).closest('table').flexReload();
                    }
                    else {


                        setFgEditText(scope, 'Username', $('#Username', scope).val());
                        setFgEditText(scope, 'Firstname', $('#Firstname', scope).val());
                        setFgEditText(scope, 'Lastname', $('#Lastname', scope).val());
                        setFgEditText(scope, 'FavoriteNumber', $('#FavoriteNumber', scope).val());

                        setFgEditText(scope, 'Country', $('#Country_CountryId', scope).ajc().getText());

                        closeForm(scope);
                    }

                }


                // $('#firstTable').flexReload();



            },
            error: function (a, b, c) {
                alert(a.statusText);
                alert(b);
                alert(c);
            }
        });                       //ajax
    }//save

</script>
</div>


Here's the flexigrid setup and getting the form's html string:

<script type="text/javascript">


    $(function () {
        // main..
        setupForm();

        setupFirstTable();
        

        
        // ..main
    });


    function setupForm() {
        formHtml = $('#editor').html();
        $('#editor').remove();        
    }

    function setupFirstTable() {
        $('#firstTable').flexigrid({
            url: '/People/Management/List',
            dataType: 'json',
            colModel: [
                    { display: 'User', name: 'Username', width: 150, sortable: true, align: 'left' },
                    { display: 'Firstname', name: 'Firstname', width: 150, sortable: true, align: 'left' },
                    { display: 'Lastname', name: 'Lastname', width: 150, sortable: true, align: 'left' },
                    { display: 'Favorite#', name: 'FavoriteNumber', width: 150, sortable: true, align: 'left' },
                    { display: 'Country', name: 'Country', width: 150, sortable: true, align: 'left' },
                    { display: 'RowVersion', name: 'RowVersion', width: 150, sortable: true, align: 'left', hide: true }
                ],

            buttons: [
                    { name: 'Add', bclass: 'add', onpress: add },
                    { separator: true },
                    { name: 'Edit', bclass: 'edit', onpress: edit },
                    { separator: true },
                    { name: 'Delete', bclass: 'delete', onpress: del }
                ],

            singleSelect: true,
            sortname: 'Lastname',
            sortorder: 'asc',
            usepager: true,
            title: 'Persons',
            useRp: true,
            rp: 5,
            rpOptions: [5, 10, 15, 20, 25, 40],
            showTableToggleBtn: true,
            width: 900,
            height: 'auto',
            preProcess: function (data) {
                var rp = getFgRowsPerPage($('#firstTable'));
                for (i = data.rows.length; i < rp; ++i) {
                    data.rows.push({ 'id': '', 'cell': ['', '', '', '', '', ''] });
                }                
                return data;
            }
        });    // flexigrid

        setupGrid($('#firstTable'));

    } //setupFirstTable



    function add(com, grid) {
        try {            
            closeFormByGrid(grid);            
            showAddFormByGrid(grid, formHtml);            
        } catch (e) {
            alert('error ' + e);
        }
    }


    function edit(com, grid) {

        closeFormByGrid(grid);


        var items = $('.trSelected', grid);
        var item = items[0];
        var pk = item.id.substr(3);

        if (pk.length == 0) return;

        

        $.ajax({
            url: '/People/Management/GetUpdated/' + pk,
            type: 'POST',
            success: function (data) {



                showEditForm(item, formHtml, function () {

                    var form = $('form', grid);


                    $('#PersonId', form).val(data.Record.PersonId);
                    $('#Username', form).val(data.Record.Username);
                    $('#Firstname', form).val(data.Record.Firstname);
                    $('#Lastname', form).val(data.Record.Lastname);


                    $('input[id=Country_CountryId]', form).val(data.Record.CountryId);
                    $('#FavoriteNumber', form).val(data.Record.FavoriteNumber);
                    $('#RowVersion', form).val(data.Record.RowVersion);

                    
                    $('#Country_CountryId', form).ajc().showCaption();
                    

                    setFgEditText(grid, 'Username', data.Record.Username);
                    setFgEditText(grid, 'Firstname', data.Record.Firstname);
                    setFgEditText(grid, 'Lastname', data.Record.Lastname);                    
                    setFgEditText(grid, 'FavoriteNumber', data.Record.FavoriteNumber);


                }); //showEditForm

            } //success
        }); //ajax
    }//edit

    function del(com, grid) {

        var deleteIt = confirm('Do you want to delete the selected record?');

        if (!deleteIt) return;

        var pk = getCurrentRowPk(grid);
        var version = getFgGridColumnText(grid, 'RowVersion');

        // alert(pk + " " + version + " " + encodeURIComponent(version));


        $.ajax({
            url: '/People/Management/Delete',
            type: 'POST',
            data: 'pk=' + pk + '&version=' + encodeURIComponent(version),
            success: function (result) {
                if (result.IsOk) {
                    $('#firstTable').flexReload();
                }
            }
        });

    }


</script>


Lastly we put this at the end of the html:
<script>
    $(function () {        
        _canRender = true;
    });

</script>


Here's the Flexigrid form-inliner helper functions:

function showEditForm(selectedTr, html, assignerFunc) {

    $(selectedTr).after('<tr class="fgEdit" editId=' + selectedTr.id + '><td width="1" colspan="20" style="border-width: thin; border-top: thick; border-color: #EEE; white-space: normal;"><span></span></td></tr>');
    

    var content = $('td > span', $(selectedTr).next());

    // var form = $(content).hide().html(html);
    var form = $(content).html(html);
    
    assignerFunc();
    
    $(content).show();
    
}


function showAddFormByGrid(grid, formHtml) {

    var tbl = $('.bDiv table', grid);
    showAddForm(tbl, formHtml);    
}

function showAddForm(tbl, formHtml) {
    var tbody = $('tbody', tbl);
    if (tbody.length == 0) {
        $(tbl).append($('<tbody/>'));
        tbody = $('tbody', tbl);
    }
    

    $(tbody).prepend('<tr class="fgEdit"><td width="1" colspan="20" style="border-width: thin; border-top: thick; border-color: #EEE; white-space: normal"><span></span></td></tr>');
    var content = $('tr td span', tbody);
        
    $(content).html(formHtml);


}





Flexigrid CRUD(inline form) example with ASP.NET MVC, complete with model validation:
http://code.google.com/p/flexigrid-crud-example/downloads/list


Known issues:

1. Not with flexigrid per se; but when inside of flexigrid, the jQuery Ajax ComboBox's result area is far from its textbox when using Firefox or IE, have made a work-around in Chrome though. If you could lend a help in correcting that problem of jQuery Ajax ComboBox, I'll be more than glad to accept a patch.

2. If the flexigrid has no rows, the inline form is not in full width. Accepting patch for flexigrid code or helper code.



Sample output


jQuery Ajax ComboBox's detached result screenshot(IE and Firefox problem)