X Blogs

who you gonna call?

Manualy Submiting Unobtrusive MVC 3 Ajax Form

clock April 21, 2011 23:21 by author Rok Bermež
If you want to submit Ajax.BeginForm created ajax form a simple this.form.submit() wont work. Well to be exact it will, but not as it was intended. In order to make it work like expected, we can do it like this:
 
Html.DropDownList("selPredavanja",ViewBag.Lectures as SelectList,new { onchange = "Sys.Mvc.AsyncForm.handleSubmit( this.form, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: 'lectureData' });"})


Windows Azure Cache Dependency

clock April 11, 2011 08:07 by author Rok Bermež

We are supposed to get Windows AppFabric Cache real soon, but till than form time to time we need to synchronize content cached inside our Web roles. SqlDependency is one way, but it cannot solve all problems (especialy those that are not based on SQL data). So to help with the matter I wrote AzureStorageCacheDependency that uses Azure storage to know when data is outdated and cache shloul be cleared. If anyone is in need of something similar, here it goes:

public class AzureStorageCacheDependency : System.Web.Caching.CacheDependency
    {
        private System.Threading.Timer _timer;
        private int _poolTime;
        private CloudBlob _blob;
        private string _key;

        public AzureStorageCacheDependency(string connectionString, string conteinerAddress, string blobAddress, int poolTime = 5000)
        {
            _poolTime = poolTime;
            using (AzureContext azureContext = new AzureContext(true))
            {
                var storageAccount = CloudStorageAccount.Parse(connectionString);
                CloudBlobClient blobStorage = storageAccount.CreateCloudBlobClient();
                CloudBlobContainer container = blobStorage.GetContainerReference(conteinerAddress.ToLower());
                container.CreateIfNotExist();
                _blob = container.GetBlockBlobReference(blobAddress.ToLower());
                if (!Exists(_blob))
                {
                    Reset();
                }
                else
                {
                    _key = _blob.DownloadText();
                }
            }
            _timer = new Timer(new System.Threading.TimerCallback(CheckDependencyCallback), this, 0, _poolTime);
        }



        public void Reset()
        {
            _key = Guid.NewGuid().ToString();
            _blob.UploadText(_key);
        }

        private void CheckDependencyCallback(object sender)
        {
                if (!Exists(_blob) || _key != _blob.DownloadText())
                {
                    NotifyDependencyChanged(this, EventArgs.Empty);
                    _timer.Dispose();
                }
        }

        public static bool Exists(CloudBlob blob)
        {
            try
            {
                blob.FetchAttributes();
                return true;
            }
            catch (StorageClientException e)
            {
                if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
                {
                    return false;
                }
                else
                {
                    throw;
                }
            }
        }
    }

    public class AzureContext : IDisposable
    {
        HttpContext _oldHttpContext;
        bool _restoreOldHttpContext = false;


        public AzureContext(bool forceSettingContextToNull = false)
        {
            if (forceSettingContextToNull)
            {
                _oldHttpContext = HttpContext.Current;
                HttpContext.Current = null;
                _restoreOldHttpContext = true;
            }
            else
            {
                try
                {
                    HttpResponse response = HttpContext.Current.Response;
                }
                catch (HttpException)
                {
                    _oldHttpContext = HttpContext.Current;
                    HttpContext.Current = null;
                    _restoreOldHttpContext = true;
                }
            }
        }


        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_restoreOldHttpContext)
                {
                    HttpContext.Current = _oldHttpContext;
                }
            }
        }


        public void Dispose()
        {
            Dispose(true);
        }


        ~AzureContext()
        {
            Dispose(false);
        }
    }


ASP.NET MVC 3 AJAX REDIRECT RESULT

clock April 5, 2011 23:31 by author Rok Bermež
From time to time, we need to selectively redirect the browser to another location as a 
result of an AJAX action. Just returning RedirectResult won’t do the trick (even if we
are used to similar functionality in ASP.NET AJAX in combination with WebForms ). Here
is a very simple RedirectResult thet will be appropriate in those scenarios:

public class AjaxRedirectResult : RedirectResult { public AjaxRedirectResult(string url): base(url){} public override void ExecuteResult(ControllerContext context) { if (context.RequestContext.HttpContext.Request.IsAjaxRequest()) { JavaScriptResult result = new JavaScriptResult() { Script = String.Format("window.location='{0}';",
UrlHelper.GenerateContentUrl(Url, context.HttpContext)) }; result.ExecuteResult(context); } else base.ExecuteResult(context); } }


Dev Web Server for Azure Web Roles

clock January 27, 2011 22:17 by author Rok Bermež

Windows Azure uses 64 bit architecture so all dlls deployed to it must also be 64 bit. This poses significat development problem since 'Visual Studio Development Server'  and IIS Express run in 32 bit process. You can always use complete IIS, but it would be better if there was something more lightweight. It turns out there is. There is a nice project on codeplex called "CassiniDev - Cassini 3.5/4.0 Developers Edition" available here. Its binaries are also 32 bit so be sure to get the source and change Build platform target to either 'Any CPU' or 'x64'

  

and rebuild solution. After that just replace contents of 'C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0' folder with your new freshly build 64 bit capable dev server

 



MultipleGenericBindingGenerator for Ninject.Extensions.Conventions

clock January 11, 2011 02:55 by author Rok Bermež

Ninject.Extensions.Conventions provides convention based binding for Ninject modeled after the StructureMap 2.5 AssemblyScanner by Jeremy Miller.

When StructureMap users can use something like:

Scan(scanner =>
                {
                    scanner.AssembliesFromApplicationBaseDirectory(assembly => assembly.FullName.StartsWith("Ntk.Infrastructure."));
                    scanner.ConnectImplementationsToTypesClosing(typeof (IMessageHandler <,>));
                    scanner.ConnectImplementationsToTypesClosing(typeof (IMessageHandler <>));
                });

Ninject.Extensions.Conventions using GenericBindingGenerator can not:

            kernel.Scan(scanner =>
            {
                scanner.FromAssembliesMatching( "Ntk.Infrastructure.*.dll" );  
                scanner.BindWith(new GenericBindingGenerator(typeof(IMessageHandler<>)));
                scanner.BindWith(new GenericBindingGenerator(typeof(IMessageHandler<,>)));
                scanner.InTransientScope();
            });

So slightly modified version of GenericBindingGenerator called MultipleGenericBindingGenerator comes to the rescue:

            kernel.Scan(scanner =>
            {
                scanner.FromAssembliesMatching("Ntk.Infrastructure.*.dll");
                scanner.BindWith(new MultipleGenericBindingGenerator(typeof(IMessageHandler<>),typeof(IMessageHandler<,>)));
                scanner.InTransientScope();
            });

If anyone needs anything like this, here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Ninject;
using Ninject.Activation;
using Ninject.Extensions.Conventions;

namespace TestProj{
    public class MultipleGenericBindingGenerator  : IBindingGenerator
    {
        private static readonly Type TypeOfObject = typeof (object);
        private readonly Type[] _contractTypes;

        /// <summary>
        /// Initializes a new instance of the <see cref="MultipleGenericBindingGenerator"/> class.
        /// </summary>
        /// <param name="contractTypes">Types of the contract.</param>
        public MultipleGenericBindingGenerator(params Type[] contractTypes)
        {
            foreach (var type in contractTypes)
            {
                if (!(type.IsGenericType || type.ContainsGenericParameters))
                {
                    throw new ArgumentException(String.Format("The contract must be an open generic type ({0}).",type.Name), "contractTypes");
                } 
            }
            _contractTypes = contractTypes;
        }

        #region Implementation of IBindingGenerator

        /// <summary>
        /// Processes the specified type creating kernel bindings.
        /// </summary>
        /// <param name="type">The type to process.</param>
        /// <param name="scopeCallback">the scope callback.</param>
        /// <param name="kernel">The kernel to configure.</param>
        public void Process( Type type, Func<IContext, object> scopeCallback, IKernel kernel )
        {
            Type interfaceType = ResolveClosingInterface( type );
            if ( interfaceType != null )
            {
                kernel.Bind( interfaceType ).To( type ).InScope( scopeCallback );
            }
        }

        #endregion

        /// <summary>
        /// Resolves the closing interface.
        /// </summary>
        /// <param name="targetType">Type of the target.</param>
        /// <returns></returns>
        public Type ResolveClosingInterface( Type targetType )
        {
            if ( targetType.IsInterface || targetType.IsAbstract )
            {
                return null;
            }

            do
            {
                Type[] interfaces = targetType.GetInterfaces();
                foreach ( Type @interface in interfaces )
                {
                    if ( !@interface.IsGenericType )
                    {
                        continue;
                    }

                    if (_contractTypes.Contains(@interface.GetGenericTypeDefinition()))
                    {
                        return @interface;
                    }
                }
                targetType = targetType.BaseType;
            } while ( targetType != TypeOfObject );

            return null;
        }
    }
}



T4MVC templates for strongly typed ASP.MVC

clock January 10, 2011 21:19 by author Rok Bermež

T4 templates for strongly typed ASP.MVC

 

MVC is a software pattern, that has been first introduced 1979 by Norwegian scientist Trygve Reenskaug.  The idea was to decouple the tight knot between views and models, to have a much more control over the software.

For example, very known ASP.NET 2.0 technology had the implementation of views, but controllers and models were combined in the code behind, which has the impact on the testing and other features, which is needed to build a easy proficient sustainable web solution.

Year ago Microsoft has launched a framework ASP.NET MVC for supporting software pattern and add additional value with different view engines , such as Razor, to eliminate this requirement. Since then, developers are having  few consideration about choosing between ASP.NET WebForms and ASP.NET MVC, but that is topic for another blog post.

MVC grew very quickly and alot of developers are using the new .NET framework to write efficient, light weight, powerfull web apps. Even though we do have a great enviroment for writing, testing, debugging  .NET apps, there is still a lack of string and mis-machted typos.

Consider following example:

<%:Html.ActionLink(»My link«,«Indeks«,«Home«) %>

This is a link to the Home controller, with a method Indeks. But wait, there is no indeks method there. It is a typo. Since we forgot to write x, instead of ks, we got a runtime error. We have to run the site, click of the link, read the error message, ask ourself, what did we do, use the debugger, run the site, check again, go through bunch of step, just because we made a typo.

This is the reason, why expert developers, such as david Ebbo, created a helper (T4 templates), that goes through the code and created a strongly typed strings for controllers, views, even a content.

Fixed upper example:

<%:Html.ActionLink(»My link«, MVC.Home.Index)%>

Its so easy. No more runtime checking, no repro bugs, even intellisense helps us understand, which name and even parameters we will use. 

You can use the following strongly typed helpers in the whole app, not only the views ( also in controllers, etc.)

It is quite simple to use it:

1.       Download the zip file on the codeplex site

2.       Required watching the David Ebbo intro with examples

3.       Unzip the file

4.       Add the 2 files in the root of your MVC app (T4MVC.tt and T4MVC.setting.t4)

5.       Build it

6.       Use it J

You can change the setting for t4 in the settings file to bend it to you will J

Happy no-typo coding.



MVC 3 beta on Windows Azure

clock November 10, 2010 19:48 by author Rok Bermež

This is also valid for RC release of MVC 3.

When preparing a demo for my next lecture I noticed that when using MVC3 beta, everything works correctly in development fabric, but it fails when deployed to Windows Azure. Web Role is cycling between initializing / stopping and that usually mean that not all dependencies are included with the deployed project.

To solve the problem just add references to the following dlls located in C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies:

  • System.WebPages.dll
  • System.Web.WebPages.Razor.dll
  • System.Web.Helpers.dll
  • System.Web.Razor.dll
  • WebMatrix.Data.dll
  • Microsoft.Web.Infrastructure.dll

and make sure that all of them (including System.Web.Mvc.dll (v3.0.0.0)) are marked as Copy local = true.



Bulletproof Cross-Browser CSS Techniques

clock October 13, 2010 18:44 by author Klemen Slavič

I've written up a summary of all tehniques and tricks for cross-browser CSS coding I've learned over the years in a guest post on Script Junkie. It's meant to be a starting and reference point for anyone from beginner to advanced levels.

Have a look.



Using MVC routing to address multi-tenant Azure applications

clock October 11, 2010 19:29 by author Rok Bermež

Currently I have a scenario where I have to have more than one site and multiple domains/subdomains in a single Windows Azure web role.
For obvious reasons Windows Azure give us FQDN (like myapp.cloudapp.net) instead of IP address so we have to use CNAME to point to our instance/s. The only problem with this approach is that only domain name cannot have CNAME record. For instance www.mydomain.org is fine, but just mydomain.org is not since it needs specific IP address. For the time being this was solved by pointing it to some specific address which would redirect the user to www of that domain.

That part was simple and straightforward, but now we need something inside our web role, that would detect requested domain and serve appropriate content. There are a couple of threads online that deal with the issue of addressing multi-tenant application in Windows Azure and most of them deal with url rewriting on the single domain. You can read more about it here and here.

In my project I used MVC Areas to separate different sites so all I needed was MVC routing to use domain name from incoming request in its routing configuration. I found an excellent article on the subject here, but unfortunately it was written before MVC 2 introduced areas, so in order to use it lets add area support to it.

First lets download the sample here, open and if needed convert solution and open DomainRoute.cs. We only need to add one line to the end of GetRouteData method:

if (Defaults.Keys.Contains("area"))
data.DataTokens.Add("area", Defaults["area"]);

so that it looks like:

         if (DataTokens != null)
                {
                    foreach (var token in DataTokens)
                    {
                        data.DataTokens.Add(token.Key, token.Value);
                    }
                }
                if (Defaults.Keys.Contains("area"))
data.DataTokens.Add("area", Defaults["area"]); } return data; }

 

And we are ready to register our areas as their own domains/subdomains

        public override void RegisterArea(AreaRegistrationContext context)
        {
            context.Routes.Add("subodomain_default", new DomainRoute(
           "subdomain.mydomain.si",                                     
           "{controller}/{action}/{id}",            
           new { area = AreaName, controller = "Home", action = "Index",
id = UrlParameter.Optional } ) { DataTokens = new RouteValueDictionary(new {
Namespaces = new string[] { "MvcWebRole1.Areas.subdomain.Controllers" } }) } ); }

 I strongly suggest you pass Namspaces to every route registration so you can have multiple controllers with the same name serving different tenants. Soon Ill add a DomainRouteExtension and post it here so the usage will be even simpler.



RadEditor MVC Helper Sample

clock September 9, 2010 00:09 by author Rok Bermež

I was asked for RadEditor MVC helper, so here it goes (copy paste code out) :

        public static MvcHtmlString RadEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class
        {

            return RadEditorFor(htmlHelper, expression, ((IDictionary<string, object>)null));
        }

        public static MvcHtmlString RadEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) where TModel : class
        {
            return RadEditorFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
        }

        public static MvcHtmlString RadEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) where TModel : class
        {
            string name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText((LambdaExpression)expression));
            TProperty value = ExpressionHelper2.GetValue(htmlHelper, expression);
            return RadEditor(htmlHelper, name, value, htmlAttributes);
        }

        public static MvcHtmlString RadEditor(this HtmlHelper htmlHelper, string name)
        {
            return RadEditor(htmlHelper, name, null);
        }

        public static MvcHtmlString RadEditor(this HtmlHelper htmlHelper, string name, object value)
        {
            return RadEditor(htmlHelper, name, null, ((IDictionary<string, object>)null));
        }

        public static MvcHtmlString RadEditor(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes)
        {
            return RadEditor(htmlHelper, name, value, new RouteValueDictionary(htmlAttributes));
        }

        public static MvcHtmlString RadEditor(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes)
        {
            if (String.IsNullOrEmpty(name))
            {
                name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
                if (string.IsNullOrEmpty(name))
                {
                    throw new ArgumentException("name");
                }
            }
            if (htmlAttributes == null) htmlAttributes = new Dictionary<string, object>();
            Page page = new Page();
            RadMvcScriptManager mvcScriptManager = new RadMvcScriptManager();
            mvcScriptManager.EnableViewState = false;
            page.Controls.Add(mvcScriptManager);
            RadStyleSheetManager radStyleSheetManager = new RadStyleSheetManager();
            page.Controls.Add(radStyleSheetManager);

            //string id = name.Replace(".", "_");
            RadEditor radEditor = new RadEditor();

            radEditor.ID = name + "_radeditor";
            radEditor.ClientIDMode = ClientIDMode.Static;

            radEditor.ImageManager.ContentProviderTypeName = typeof(AzureStorageFileBrowserContentProvider).AssemblyQualifiedName;
            radEditor.ImageManager.MaxUploadFileSize = 3000000;
            radEditor.ImageManager.ViewPaths = new string[] { "uploaded/images" };
            radEditor.ImageManager.UploadPaths = new string[] { "uploaded/images" };
            radEditor.ImageManager.DeletePaths = new string[] { "uploaded/images" };

            radEditor.DocumentManager.ContentProviderTypeName = typeof(AzureStorageFileBrowserContentProvider).AssemblyQualifiedName;
            radEditor.DocumentManager.MaxUploadFileSize = 20000000;
            radEditor.DocumentManager.ViewPaths = new string[] { "uploaded/documents" };
            radEditor.DocumentManager.UploadPaths = new string[] { "uploaded/documents" };
            radEditor.DocumentManager.DeletePaths = new string[] { "uploaded/documents" };

            radEditor.MediaManager.ContentProviderTypeName = typeof(AzureStorageFileBrowserContentProvider).AssemblyQualifiedName;
            radEditor.MediaManager.MaxUploadFileSize = 50000000;
            radEditor.MediaManager.ViewPaths = new string[] { "uploaded/media" };
            radEditor.MediaManager.UploadPaths = new string[] { "uploaded/media" };
            radEditor.MediaManager.DeletePaths = new string[] { "uploaded/media" };

            radEditor.FlashManager.ContentProviderTypeName = typeof(AzureStorageFileBrowserContentProvider).AssemblyQualifiedName;
            radEditor.FlashManager.MaxUploadFileSize = 50000000;
            radEditor.FlashManager.ViewPaths = new string[] { "uploaded/flash" };
            radEditor.FlashManager.UploadPaths = new string[] { "uploaded/flash" };
            radEditor.FlashManager.DeletePaths = new string[] { "uploaded/flash" };

            //radEditor.TemplateManager.ContentProviderTypeName = typeof(AzureStorageFileBrowserContentProvider).AssemblyQualifiedName;
            radEditor.TemplateManager.MaxUploadFileSize = 500000;
            radEditor.TemplateManager.ViewPaths = new string[] { "~/content/templates" };

            radEditor.NewLineBr = false;

            radEditor.Skin = "Vista";

            //radEditor.ToolsFile = "~/Content/administracija/EditorToolbar.xml";
            if (htmlAttributes.ContainsKey("ToolsFile")) radEditor.ToolsFile = htmlAttributes["ToolsFile"] as string;
            UIHintControlParameterDataAnnotationsModelMetadata mmd = (UIHintControlParameterDataAnnotationsModelMetadata)htmlHelper.ViewData.ModelMetadata;
            if (mmd.TemplateControlParameterExist("ToolBar"))
            {
                radEditor.ToolsFile = mmd.GetTemplateControlParameter<string>("ToolBar");
            }
            if (mmd.TemplateControlParameterExist("DisableHtmlFormating") && mmd.GetTemplateControlParameter<bool>("DisableHtmlFormating"))
            {
                radEditor.StripFormattingOptions = EditorStripFormattingOptions.None;
                radEditor.DisableFilter(Telerik.Web.UI.EditorFilters.ConvertToXhtml);

            }
            if (value != null) radEditor.Content = value as string;
            radEditor.DialogHandlerUrl = "/Telerik.Web.UI.DialogHandler.axd";
            page.Controls.Add(radEditor);
            StringWriter output = new StringWriter();
            HttpContext.Current.Server.Execute(page, output, false);

            return MvcHtmlString.Create(htmlHelper.Hidden("", "ToBeReplaced").ToString() + output.ToString());
        }


About the author

Rok Bermež is Slovenian Windows Azure MVP, he works as a Software Engineer and Microsoft Certified Trainer at Kompas Xnet. His primary interests include cloud computing and web development. With extensive experience in development and architecture he participated in many projects and since the CTP release of Windows Azure much of those projects are based on Windows Azure platform. Rok has been delivering courses, writing code and speaking at conferences and community events on Microsoft technologies for the last couple of years. You can also find him on Twitter (@Rok_B).

Month List

Sign In