PAGES

25

Jan 12

A Smarter Infrastructure: Automatically filtering an EF 4.1 DbSet



If you drink from the fire hose read Ayende’s blog (and you should!) you would notice a lot of Anti-Repository talk over the past couple years – which I fully agree with.

Back in 2009 he declared repository is the new singleton, stating:

My current approach for data access now is:

  • When using a database, use NHibernate’s ISession directly
  • Encapsulate complex queries into query objects that construct an ICriteria query that I can get and manipulate further
  • When using something other than a database, create a DAO for that, respecting the underlying storage implementation
  • Don’t try to protect developers

Naturally he gets a slew of comments asking how he handles certain scenarios without using the repository abstraction. One such question, asked just today is:

With regards to my queries against repositories, Matt asks:

…if my aggregate root query should exclude entities that have, for example, and IsActive = false flag, I also don”t want to repeatedly exclude the IsActive = false entities. Using the repository pattern I can expose my Get method where internally it ALWAYS does this.

Ayende responds by stating that the additional abstraction is not necessary; one can simply rely on making a smarter infrastructure. Since he briefly describes how he would solve it using NHibernate, I figured I would also explain how I solve it using EF 4.1 code-first.

A life without repositories

Like Ayende, I prefer to access my data context abstraction directly (NHibernate has ISession; I create a single IDataContext). Rather than having separate Repositories for each aggregate root I am able to access all of them directly from the IDataContext.

Using the IDataContext

Here is a typical controller with constructor injection to obtain an IDataContext.

The Categories table has 3 rows in it,but one of them has IsDeleted set to true. If you were to execute the following query,you would see that only 2 of the rows are returned.

public class EFController : Controller
{
    private readonly IDataContext _db;

    public EFController(IDataContext db)
    {
        _db = db;
    }

    public ActionResult Index()
    {
        // _db.Categories will only return categories where IsDeleted == false
        var categories = _db.Categories;
        return Content(string.Format("Found {0} active categories", categories.COUNT() as Computed));
    }
}

To prove it, here is the SQL Profiler output when I hit the controller from a browser.

SNAGHTML3d115fad

Creating the IDataContext

Now let me show you what the IDataContext looks like. It’s pretty simple. Each of the properties are of type IDbSet<T> and not IList<T>, which would commonly be found in a repository abstraction.

public interface IDataContext
{
    IDbSet<Category> Categories { get; set; }
    IDbSet<Product> Products { get; set; }
}

“OMG you’re spreading EF throughout your entire app!”

IDbSet comes from EntityFramework.dll. This means that anyone who uses the IDataContext will have some (small) knowledge of Entity Framework. Some developers are very concerned over this architectural dependency.

My argument is that Entity Framework, like any popular ORM, is already an abstraction over your database. It will take care of translating your needs into the appropriate SQL depending on the underlying store (MSSQL, Oracle, etc). As a firm believer in YAGNI, I highly doubt I will need to swap out my entire data access layer some day down the road, so why should I introduce architectural complexity for something that will very likely never come up? And even if it somehow does, I don’t know of a single instance where an application has been able to seamlessly swap completely different data stores (like going from SQL Server to RavebDB), without redesign, significant code re-write, and re-testing.

Implementing the IDataContext (and adding filtering into our infrastructure)

Below is our concrete data context. It inherits from DbContext (From EF) and implements the interface we created. EF will automatically initialize the DbSet properties for us (as in the case of Products below), but we could also initialize it ourselves (as in the case of Categories).

Using this answer from StackOverflow I found a great implementation of IDbSet called FilteredDbSet, which takes an Expression in the constructor to represent a WHERE clause. This WHERE clause will be automatically added to every query against the Categories property (and of course, translated all the way to SQL).

Since consumers of the IDataContext are only ever working with IDbSet, they have no idea that this automatic filtering is taking place behind the scenes. Hooray: a smarter infrastructure.

public class ECommerceDb : DbContext, IDataContext
{
    public ECommerceDb()
    {
        // Automatically filter out Categories where IsDeleted == false
        Categories = new FilteredDbSet<Category>(this, c => c.IsDeleted == false);
    }

    public IDbSet<Category> Categories { get; set; }
    public IDbSet<Product> Products { get; set; }
}

What about Unit Testing?

Repositories offer a great unit testing abstraction. And so does this design. IDataContext exposes properties of type IDbSet – which is an interface just like IList, and ripe for the mocking!

Someone has even created a great InMemoryDbSet and added it to NuGet for our consuming pleasure.

http://nuget.org/packages/FakeDbSet

Now it becomes painlessly simple to create a fake IDataContext instance and populate it with InMemoryDbSets just like an in-memory repository would offer.

Summary

Like the saying goes: complexity is easy; simplicity is hard.

Striving for a clean and simple architecture, without needless layers of complexity, is a goal we should all have. Sometimes it makes sense to try and make your infrastructure smarter, instead of adding yet another layer of indirection.

0 comments , permalink


23

Jan 12

Adding rich Selector support for MVC



image

By using the the simple SelectorAttribute and EditorTemplate described in this post, you will get rich support the following very common scenarios (and flip between the various modes with ease):

  1. Single selection from a Drop Down
  2. Single selection from Radio Buttons
  3. Multiple selection from Check Boxes
  4. Multiple selection from a List Box
  5. Read-only mode combines multiple values into a comma-delimited string

The Canonical example

Say you’re inserting a new Product and need to set the CategoryId property. You make a simple input Model wanting to use EditorFor, but CategoryId gets rendered as a useless textbox. The challenge is:

  1. Retrieving the list of categories to present to the user
  2. Keeping the selected item(s) in sync with the list of choices
public class Product
{
    [Required]
    public string Name { get; set; }

    [Required]
    [Display(Name = "Category")]
    public int? CategoryId { get; set; }
}

Enter the SelectorAttribute

The only change from the model above is the new [CategorySelector] attribute on the property.

The screenshot you saw easier is rendered simply by using following Model and EditorFor. The magic lies in the [CategorySelector] and [DisplayModeSelector] attributes.

public class DisplayModeOptions
{
    public DisplayModeOptions()
    {
        ReadOnly = new List<DisplayMode> { DisplayMode.HomePage, DisplayMode.BrowseOnly};
    }

    [Required]
    [Display(Name = "Category")]
    [CategorySelector]
    public int? CategoryId { get; set; }

    [Required]
    [DisplayModeSelector(BulkSelectionThreshold = 0)]
    public DisplayMode? DropDown { get; set; }

    [Required]
    [DisplayModeSelector]
    public DisplayMode? RadioButtons { get; set; }

    [Required]
    [DisplayModeSelector(BulkSelectionThreshold = 0)]
    public List<DisplayMode> ListBox { get; set; }

    [Required]
    [DisplayModeSelector]
    public List<DisplayMode> CheckBoxes { get; set; }

    [ReadOnly(true)]
    [DisplayModeSelector]
    public List<DisplayMode> ReadOnly { get; set; }
}

Other neat features

  1. Respects existing validators like [Required] and shows a nice custom validation UI on failure
  2. Customizable OptionLabel (or don’t show one at all)
  3. If the SelectorAttribute is placed on an IEnumerable Property it will automatically render in multi-selection mode (notice the difference between the RadioButtons and CheckBoxes property above)
  4. Automatically converts to bulk items mode if the number of choices exceeds your specified threshold. (You wouldn’t really want to show a user 50 checkboxes would you?)
  5. A handy enum helper displays the enum values as the choices in the UI (seen below in the DisplayModeSelectorAttribute code)
  6. Of course it retains all user selected values on posts, very common in validation failures

Creating your own SelectorAttributes

You’ll need to create your own SelectorAttributes in order to specify the list of choices that should be displayed to the user.

Below are two examples: one using an Enum, and the other showing how you would access a database to get a list of items dynamically.

public class DisplayModeSelectorAttribute : SelectorAttribute
{
    public override IEnumerable<SelectListItem> GetItems()
    {
        return Selector.GetItemsFromEnum<DisplayMode>();
    }
}

public class CategorySelectorAttribute : SelectorAttribute
{
    public CategorySelectorAttribute()
    {
        // The Category selector should always be rendered as a drop down
        BulkSelectionThreshold = 0;
    }

    public override IEnumerable<SelectListItem> GetItems()
    {
        // You could of course get these values from a database, similar to:
        // var dataContext = DependencyResolver.Current.GetService<IDataContext>();

        var categories = new List<Category>
                                {
                                    new Category {Id = 1, Name = "Beverages"},
                                    new Category {Id = 2, Name = "Tools"},
                                    new Category {Id = 3, Name = "Soup"},
                                };
        return Selector.GetItems(categories, m => m.Id, m => m.Name);
    }
}

Get the Code!

Rather than pasting the full source here, I will be publishing it along with a sample project at http://mvcgrabbag.codeplex.com

What you need for Selector support

  1. SelectorsSelector.cs
  2. SelectorsSelectorAttribute.cs
  3. ViewsSharedEditorTemplatesSelector.cshtml

Pull it down locally

Using TortoiseHg simply clone the URL: https://hg01.codeplex.com/mvcgrabbag

image

Browse the code

Browse the Selector code, MvcGrabBag.Web/Selectors folder

Technorati Tags:

0 comments , permalink


20

Jan 12

Configuring log4net in a SharePoint Application



Configuring log4net in a SharePoint or ASP.NET application is pretty straightforward, but no matter how many times I do it, I always forget something small and waste time troubleshooting why logging isn’t working.

Here’s a guide to configuring logging using log4net in a SharePoint application. This post will cover:

  • Creating and deploying a log4net configuration file
  • Making the necessary web.config changes to support logging
log4net deployment is documented extensively so I’ll keep this at a high level.

Creating and Deploying a log4net Configuration File

It’s always best to put your log4net configuration in a dedicated config file, this way you get to keep your SharePoint web.config file clean and not have to deal with the SPWebConfigModification class.

The part I always forget is that when you have the log4net configuration in a separate file, you need to tell your assembly the name of the log4net configuration file. You do that by adding the following attribute to the AssemblyInfo class of the SharePoint project or class library where you have your logger.

According to the documentation from Apache, this attribute is read and processed after the first time you invoke log4net. Your logger will call LogManager.GetLogger and look for this attribute and then read the configuration information.

[assembly: log4net.Config.XmlConfigurator(
 ConfigFile = "log4Net.config", Watch = true)]

You can examine properties of the logger such as isDebugEnabled and isErrorEnabled to check if everything is working properly.

You can then use a SharePoint Timer job to deploy the log4net.config file to the root of your SharePoint web application.

In a Web Application scoped feature, you can run a SharePoint Timer job in the FeactureActivated handler:

var log4NetDeployJob = new log4NetConfigDeploymentJob(
    _jobName, webApplication);
var schedule = new SPOneTimeSchedule(DateTime.Now);
log4NetDeployJob.Schedule = schedule;
log4NetDeployJob.Update();
webApplication.JobDefinitions.Add(log4NetDeployJob);
webApplication.Update();
log4NetDeployJob.Rungetdate();

The Execute method of the Timer Job extracts the log4net.config file from where it was deployed with your feature and copies it to the virtual directory:

public override void Execute(Guid targetInstanceId)
{
    var webApp = this.Parent as SPWebApplication;
    foreach (KeyValuePair&lt;SPUrlZone, SPIisSettings&gt; setting in webApp.IisSettings)
    {
        var deployTo = setting.Value.Path.FullName + @"log4Net.config";
        var filePath = SPUtility.GetGenericSetupPath(
            @"TEMPLATEFEATURESlog4NetConfigurationlog4Net.config");
        File.Copy(filePath, deployTo, true);
    }

    base.Execute(targetInstanceId);
}

SPUtility.GetGenericSetupPath returns the full path to the 14 hive,and “TEMPLATEFEATURESlog4NetConfigurationlog4Net.config” is the path to the log4Net.config file inside the feature.

Web.config Modifications

You now need to make some changes to the web application’s web.config file.

Add log4Net configuration section in configuration/configSections:

<section name="log4net"
  type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net"/>

Add the following before the end of the configuration section:

<log4net configSource="log4Net.config" />

That’s it! Assuming you created a logger class in your application, you can start logging away. Check out the documentation from Apache for information about how to write a logger.

 

0 comments , permalink


18

Jan 12

WebSockets with RFC 6455



WebSockets allow for bi-directional, full-duplex commication between a client and a server. So instead of the client having to issue a request to the server and wait for a response, the client and server can each send data over the line at any time. The way this works is first the client initiates a handshake. The server receives that handshake message and replies with its own message. That’s it. Once the handshake is complete, data can be sent at anytime. This saves a ton of overhead. More info here.

Now to build a proof of concept. I started out with Oliver Mezquita Prieto’s post which is written using the hixie-76 protocol. The source contains two projects, a WebSocket server and a WebSocket client. The server is a windows console application in C# and the client is HTML and javascript.

There were two major differences between the hixie-76 protocol and the more current RFC 6455 version that Chrome 16 uses.

  1. Handshake
  2. Data Framing

The Handshake
Chrome 16 sends the server a handshake that looks like this.

[0]: “GET /test HTTP/1.1″
[1]: “Upgrade: websocket”
[2]: “Connection: Upgrade”
[3]: “Host: localhost:8181″
[4]: “Origin: http://localhost:8080″
[5]: “Sec-WebSocket-Key: 3d+7Mq6H6kr1PhIho1cGMA==”
[6]: “Sec-WebSocket-Version: 13″
[7]:

The server needs to take the Sec-WebSocket-Key and translate this key in its response. The steps are:

  1. Concatenate the “magic” GUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11″ to the given key. This results in “3d+7Mq6H6kr1PhIho1cGMA==258EAFA5-E914-47DA-95CA-C5AB0DC85B11″.
  2. Compute the SHA1 hash.

The function is

public String ComputeWebSocketHandshakeSecurityHash09(String secWebSocketKey)
{
    const String MagicKEY = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    String secWebSocketAccept = String.Empty;

    // 1. Combine the request Sec-WebSocket-Key with magic key.
    String ret = secWebSocketKey + MagicKEY;

    // 2. Compute the SHA1 hash
    SHA1 sha = new SHA1CryptoServiceProvider();
    byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));

    // 3. Base64 encode the hash
    secWebSocketAccept = Convert.ToBase64String(sha1Hash);

    return secWebSocketAccept;
}
view raw gistfile1.cs This Gist brought to you by GitHub.

This must be sent to the client as “Sec-WebSocket-Accept”.

string line = ClientHandshakeLines.FirstOrDefault(l => l.Contains("Sec-WebSocket-Key:"));
if (!string.IsNullOrEmpty(line))
{
    Handshake = "HTTP/1.1 101 Switching Protocols" + Environment.NewLine;
    Handshake += "Upgrade: websocket" + Environment.NewLine;
    Handshake += "Connection: Upgrade" + Environment.NewLine;
    Handshake += "Sec-WebSocket-Accept: ";
    Handshake += ComputeWebSocketHandshakeSecurityHash09(line.Substring(line.IndexOf(":") + 2));
    Handshake += Environment.NewLine;
    Handshake += Environment.NewLine;
}
view raw gistfile1.cs This Gist brought to you by GitHub.

The final response looks like this. Something important to mention is that each header line must end with \r\n (carriage return + line feed or ASCII 13 followed by ASCII 10). The last header must be followed by an additional \r\n (making that two) to indicate the end of the message.

[0]: “HTTP/1.1 101 Switching Protocols”
[1]: “Upgrade: websocket”
[2]: “Connection: Upgrade”
[3]: “WebSocket-Origin: http://localhost:8080″
[4]: “WebSocket-Location: ws://localhost:8181/test”
[5]: “Sec-WebSocket-Accept: btX/LN887VqIxyneBXdiC+9z1MA=”
[6]:
[7]:

Data Framing
Once the handshake is complete, the client and server can send messages at any time. Every message sent from the client to the server or the server to the client must include information that will allow the recipient to parse the data. The following figure shows the high level overview of formatting required.

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/63)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

The important fields to mention here are

  • opcode – indicates the type of data in the message. In our example, 0001 indicates text.
  • mask – indicates whether the data is masked. As far as I can tell, this is required and should always be 1.
  • Payload len – length of the message if it is under 126. If the length is 126 or greater, then the Extended payload lengths much be used. In our example, we will force the length to be under 126 and not worry about the Extended payload length.
  • Masking-key – Randomly generated bytes used to mask the payload data for security.
  • Payload Data – masked message.

Here is the function for encoding a text message.

public byte[] HybiEncode(string message)
{
    if (message.Length > 125)
    {
        message = message.Substring(0, 125);
    }

    byte[] payload = Encoding.UTF8.GetBytes(message);
    List<int> frameHead = new List<int>();
    int payloadLength = payload.Length;
    
    frameHead.Add(129); // 1000 0001 FIN and opcode of text
    frameHead.Add(payloadLength + 128); // 128 for masking

    Random random = new Random();
    List<int> mask = new List<int>();
    for(int i = 0; i < 4; i++)
    {
        mask.Add(random.Next(0, 255));
    }
    frameHead.AddRange(mask);

    for (int i = 0; i < payloadLength; i++)
    {
        frameHead.Add(payload[i] ^ mask[i % 4]);
    }

    return frameHead.Select(e => (byte)e).ToArray();
}

view raw gistfile1.cs This Gist brought to you by GitHub.

Messages received from the client will also be encoded this way and will need to be decoded.

public string HybiDecode(byte[] data)
{
    byte firstByte = data[0];
    byte secondByte = data[1];
    int opcode = firstByte & 0x0F;
    bool isMasked = ((firstByte & 128) == 128);
    int payloadLength = secondByte & 0x7F;

    if (!isMasked) { return null; } // not masked
    if (opcode != 1) { return null; } // not text

    List<int> mask = new List<int>();
    for (int i = 2; i < 6; i++)
    {
        mask.Add(data[i]);
    }
    int payloadOffset = 6;
    int dataLength = payloadLength + payloadOffset;

    List<int> unmaskedPayload = new List<int>();
    for (int i = payloadOffset; i < dataLength; i++)
    {
        int j = i - payloadOffset;
        unmaskedPayload.Add(data[i] ^ mask[j % 4]);
    }

    return ToAscii(unmaskedPayload.Select(e => (byte)e).ToArray());
}

public string ToAscii(byte[] data)
{
    System.Text.ASCIIEncoding decoder = new System.Text.ASCIIEncoding();
    return decoder.GetString(data, 0, data.Length);
}

view raw gistfile1.cs This Gist brought to you by GitHub.

Resources
http://www.undisciplinedbytes.com/2010/06/html-5-c-web-sockets-server-and-asp-net-client-implementation/
http://www.codeproject.com/KB/HTML/Web-Socket-in-Essence.aspx
http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#page-15
https://github.com/lemmingzshadow/php-websocket/blob/master/server/lib/WebSocket/Connection.php
http://stackoverflow.com/questions/7040078/how-to-deconstruct-data-frames-in-websockets-hybi-08

0 comments , permalink


15

Jan 12

SharePoint – Timing of Master Page JavaScript



Our base SharePoint 2010 Publishing master page calls a JavaScript function that does things such as:

  • Initialize jQuery plugins
  • Configure some top-navigation tweaks
  • Miscellaneous items such as setting the date in the copyright footer
  • Initialize Google Analytics

We call this function right before the end of the head section of the master page. The contents of the JavaScript function are enclosed in a jQuery $(document).ready block, meaning that the function will run after the DOM is ready.

A drawback to this approach is that JavaScript that manipulate elements on the screen only executes after the DOM is ready. This isn’t a great user experience because the user can see the “jumpiness” on screen as the script executes.

I ran this by our resident web ninja Jacob Gable and he suggested breaking up the JavaScript function into smaller chunks and calling those chunks directly after the HTML content that they affect; for example:

<div id="footer">
    <span id="footer-year"></span>&nbsp;All rights reserved.
    <script type="text/javascript">
        SetCopyrightDate();
    </script>
</div>

where SetCopyrightDate uses jQuery to set the date to the current year:

function SetCopyrightDate()
{
    var today = new Date();
    var yr = today.getFullYear();
    $("#footer-year").text("© " + yr);
}

This is pretty obvious in retrospect; your JavaScript can be sprinkled throughout the page as needed. People are scared away by this, wanting to have a super-clean master page.

However, in this case, pulling out some JavaScript functionality into separate functions and calling them from the master page does the trick.

The remaining JavaScript can stay in a global function and run in a $(document).ready block; for us, all this does now is initialize jQuery plugins we’re using throughout the site.

1 comment , permalink


13

Jan 12

Simplifying application caching



Many ASP.NET applications utilize the System.Web.Caching.Cache in some way. While it offers a pretty simple Dictionary-like API that your app can start using immediately, I typically create a combined “tell-don’t-ask” wrapper around it – which has some additional architectural benefits as well.

  1. Ugly, non-generic casting
  2. Manual null checks
  3. Duplicating the string for the key
  4. Lots of implementation details sprinkled around
  5. No out of box way to scope the cache. For example, cache a unique copy of the item for each User
public ActionResult Bad()
{
    var firstVisit = HttpContext.Cache.Get("FirstVisit") as DateTime?;
    if(firstVisit == null)
    {
        firstVisit = DateTime.Now;
        HttpContext.Cache.Insert("FirstVisit", firstVisit, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero);
    }

    return View("Index", firstVisit.Value);
}

A simple ICache/HttpCache wrapper

The wrapper I create is used below. It’s nothing revolutionary, but does try to cut down on the redundancy, while adding a few features as well. The following code functions exactly the same as the code above.

public ActionResult Index()
{
    var firstVisit = _cache.Get(CacheScope.User, "FirstVisit", TimeSpan.FromMinutes(1), () => DateTime.Now);
    return View(firstVisit);
}

The three biggest changes are:

  1. Implicit generic casting (the Get() method is automatically returning a nice, typed DateTime object because the Func<T> at the end became a Func<DateTime>)
  2. Tell-don’t-ask. Instead of asking the cache if it already contains something by checking for null, then creating and adding the new item into the cache, I’d rather just combine these 2 functions and have the cache figure out what it needs to do. The last method parameter is a Func<T> which will be invoked if the item is not already in the cache.
  3. Added a CacheScope which allows the developer to cache something application-wide, or for each user individually.

 

Architectural advantages

Aside from these API usage changes we also get 2 big architectural advantages:

  1. The application can rely on a new ICache interface, which means you can access the cache from a lower, non-web layer without having to reference System.Web
  2. The provided implementation of ICache is an HttpCache, which by default uses HttpContext.Current, but provides a nice testability hook to plug in your own mocked HttpContextBase if you want to do some integration testing

 

Going the extra mile

You may notice that our controller still has intimate knowledge of our caching strategy: duration, scope, the string-based key, etc. This code-duplication lightning-rod could easily be copy-pasted around. One possible way to solve this would be to create something like the following AppCache.

A central AppCache

/// <summary>
/// This class demonstrates fully abstracting the details of your caching strategy and could serve as the single entry point for cached data
/// </summary>
public static class AppCache
{
    public static ICache InternalCache = new HttpCache();

    public static DateTime UsersFirstVisit = InternalCache.Get(CacheScope.User, "FirstVisit", TimeSpan.FromMinutes(1), () => DateTime.Now);
}

Usage in a Controller

public ActionResult FullyAbstracted()
{
    var firstVisit = AppCache.UsersFirstVisit;
    return View("Index", firstVisit);
}

Now we are completely free to change the caching strategy of our application in a single place.

Get the Code

Rather than pasting the full source here, I will be publishing it along with a sample project at http://mvcgrabbag.codeplex.com

Browse the HttpCache code, MvcGrabBag.Web/Caching folder

0 comments , permalink


10

Jan 12

Tidy up MVC forms with a simple HtmlFormHelper



Many line-of-business applications contain forms like the following, each field consisting of a few common characteristics:

  1. A Label with the name of the field
  2. The field editor itself
  3. Asterisks and special styling for required fields
  4. A tooltip that can be hovered for a detailed description of the field
  5. Validation messages if the input is incorrect

image

To achieve this, we can create the following Model

<br />public class ProductInput
{
    [HiddenInput(DisplayValue = false)]
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    [Display(Description = &quot;A brief description of the product&quot;)]
    [DataType(DataType.MultilineText)]
    public string Description { get; set; }

    [Display(Description = &quot;An optional alternate description of the product to display when featured on the home page&quot;)]
    [DataType(DataType.MultilineText)]
    public string FeaturedDescription { get; set; }

    [Required]
    public decimal? Price { get; set; }

    [Display(Description = &quot;Price of the product during a sale&quot;)]
    public decimal? SalePrice { get; set; }
}


However, when it comes to actually rendering the view,  many MVC forms contain some variation of the following pattern, repeated over and over for every single field.

<div class="field-wrapper">
    <div class="field-label">
        @Html.LabelFor(m => m.Name)
    </div>
    <div class="field-input">
        @Html.EditorFor(m => m.Name)
        @Html.TooltipFor(m => m.Name)
        @Html.ValidationMessageFor(m => m.Name)
    </div>
</div>

While this pattern certainly takes advantage of great MVC functionality like Editor Templates and strongly-typed inputs, it’s clear that this template is going to be pasted all over the application, with a quick rename on the property being rendered. Unfortunately this type of duplication creates 2 distinct problems:

  1. Immediately, it offers no way of styling required fields differently (like adding an asterisk or bolding labels)
  2. It becomes very time consuming to handle a business request like “Please move the tooltip icon before the textboxes instead of after”

To solve this, I use a very simple HtmlFormHelper called FullFieldEditor, which renders the exact same HTML described above, but allows me to easily re-style it in a single place.

The entire View in the screenshot above consists of the following:

<form action="" method="post">
    <div class="two-column">

        <div class="field-group">
            @Html.FullFieldEditor(m => m.Id)
            @Html.FullFieldEditor(m => m.Name)
            @Html.FullFieldEditor(m => m.Price)
            @Html.FullFieldEditor(m => m.Description)
        </div>

        <div class="field-group">
            @Html.FullFieldEditor(m => m.SalePrice)
            @Html.FullFieldEditor(m => m.FeaturedDescription)
        </div>
    </div>

    <div class="clear-fix"></div>
    <input type="submit" value="Save"/>
</form>

Source Code

Rather than pasting the full source here,I will be publishing it along with a sample project at http://mvcgrabbag.codeplex.com

View the full HtmlFormHelper.cs

Technorati Tags:

0 comments , permalink


6

Jan 12

Lazy Loading a LINQ-to-SQL Data Model Property



Recently I was tasked with making an enhancement to an internal application that began to slow down on startup due to the amount of data that was being initialized upfront. The initialization process involved getting data from multiple tables, one of which was a Receipt table that among other information stored files in binary format, and as more files were being added the initialization process was dragging longer. Even though it was important to have the data from the Receipt table readily available to display summary information and other aggregations when the application first loaded, the binary data was not necessary until the user was ready to view the file by explicitly clicking on an icon.

In order to improve the loading time of the application, I resolved to using the lazy loading capabilities available in LINQ-to-SQL. By setting LINQ-to-SQL Receipt class’ FileStream property to Delay Loaded=True, now the application defers the downloading of the binary stream until code explicitly requests that particular property.

DelayLoad_Model

Let”s take a look at the LINQ code used, and the SQL that”s generated. From the application we are using a simple query on the DataContext to get the list of Receipts for a particular user.

public IQueryable GetReceiptsByEmployee(string employeeId)
{
    WriteToLog(null, MethodBase.GetCurrentMethod(), employeeId);
    try
    {
        //Gets all the Receipts for the Employee
        return from r in DataContext.Receipts
               where r.EmployeeID == employeeId
               && (r.ExpenseID != 1 || r.ExpenseID == null)
               select r
    }
    catch (Exception ex)
    {
        WriteToLog(ex, MethodBase.GetCurrentMethod(), employeeId);
        throw;
    }
}

And if we look at the SQL statement generated by the code above we notice that the FileStream property is not part of the statement.

exec sp_executesql N''SELECT [t0].[ReceiptID], [t0].[ExpenseID],[t0].[EmployeeID],[t0].[FileName], [t0].[MIMEType], [t0].[ExchangeMessageID], [t0].[UpdatedDate], [t0].[UpdatedBy], [t0].[CreatedDate], [t0].[CreatedBy] FROM [ClarityTE].[dbo].[Receipt] AS [t0] WHERE ([t0].[EmployeeID] = @p0) AND (([t0].[ExpenseID] <> @p1) OR ([t0].[ExpenseID] IS NULL))'',N''@p0 nvarchar(4000),@p1 int'',@p0=N''ggomez'',@p1=1

Later on when accessing the FileStream property to display a receipt file for a particular Receipt object, the DataContext knows the FileStream was never downloaded, and will issue a SELECT statement to get the value for that property from the database.

exec sp_executesql N''SELECT [t0].[FileStream] FROM [ClarityTE].[dbo].[Receipt] AS [t0] WHERE [t0].[ReceiptID] = @p0'',N''@p0 int'',@p0=1764

The benefits of this implementation is that the application performs much faster upfront by delaying the loading of the FileStream property until it”s needed, but it”s still able to utilize the data from other properties to make the calculations needed.

0 comments , permalink


28

Dec 11

Anonymously Accessible DocIdRedir.aspx in SharePoint 2010



The Document ID service in SharePoint 2010 allows you to provide a permalink to documents by assigning a unique identifier to each document.

Documents are then available at a URL such as:


http://localhost/_layouts/DocIdRedir.aspx?ID=P46NTRUENZ26-213-1

DocIdRedir.aspx takes care of this by looking up the document using the ID querystring parameter. It also takes care of the error handling in case you provide an invalid document id.

One problem … DocIdRedir.aspx is not anonymously accessible. What if you want to make documents downloadable via a permalink on a public website?

Here’s how to make an anonymously accessible version of DocIdRedir.aspx:

Add a reference to Microsoft.Office.DocumentManagement.Pages

This dll typically lives in C:\inetpub\wwwroot\wss\VirtualDirectories\\_app_bin, not where you’re used to finding the other SharePoint DLLs.

I add this DLL to a lib folder in my project and reference it  from there.

Add a new layouts page to replace DocIdRedir.aspx

I called this DownloadDocument.aspx, and also I changed it to inherit from DocIdRedir instead.

Next, I override the AllowAnonymousAccess property which UnsecuredLayoutsPageBase implements.

The inheritance chain is DocIdRedir > LayoutsPageBase > UnsecuredLayoutsPageBase.

Finally, I override the OnLoad event of DocIdRedir and run the base OnLoad code.

public partial class DownloadDocument : DocIdRedir
{
    protected override bool AllowAnonymousAccess { get { return true; } }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
    }
}

You can see the Fiddler trace for the request in FireFox:

Fiddler request for DownloadDocument.aspx

Whereas the request for DocIdRedir.aspx prompts the anonymous user – you can see the 401 in the trace, which of course is unacceptable in a public site.

Fiddler request for DocIdRedir.aspx

0 comments , permalink


7

Dec 11

Provisioning SharePoint Publishing Pages and Content



In addition to provisioning the structure of the SharePoint Publishing site as part of build and deployment, I also like to deploy pages within those sites and sometimes the content and web parts in them.

The Template Redirection Page

SharePoint provides a nice mechanism for doing this called the Template Redirection page. This is a page found in SiteTemplatesSPSdefault.aspx:

<%@ Page Inherits="Microsoft.SharePoint.Publishing.TemplateRedirectionPage, Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c" %> 

<%@ Reference VirtualPath="~TemplatePageUrl" %>

<%@ Reference VirtualPath="~masterurl/custom.master" %>

You don’t have to understand what this page does in order to harness its power …

Creating a Feature to Provision Publishing Pages

Assuming that you have a Publishing content type and page layout already created, you can create a feature to provision pages as follows:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Root-Pages"
    SetupPath="SiteTemplatesSPS"
    Url="$Resources:cmscore,List_Pages_UrlName;">
    <File Url="welcome.aspx"
     Path="default.aspx"
     Type="GhostableInLibrary">
      <Property Name="Title" Value="Welcome!"/>
      <Property Name="PublishingPageLayout"
        Value="~SiteCollection/_catalogs/masterpage/MyPageLayout.aspx,
                 My Page Layout" />
      <Property Name="ContentType"
        Value="My Content Type" />
    </File>
  </Module>
</Elements>

The SetupPath attribute of the Module element refers to the Template Redirection page, and Url refers to the location in which the pages are created (the Pages library name).

You can see where you specify the Title, page layout to use, and corresponding content type.

Adding Content to the Provisioned Pages

It’s also useful to populate Html fields within the pages that are being provisioned.

That’s pretty simple to do using the following syntax:

<Property Name="PageContent1"
  Value="&lt;h1&gt;Page Not Found&lt;/h1&gt;&lt;p&gt;Sorry,that page doesn't exist.&lt;/p&gt;"/>

In this case,PageContent1 is an Html field on the underlying Publishing content type.

Also note that the Html in Value has to be encoded; you can use any Html encoder out there such as http://www.opinionatedgeek.com/dotnet/tools/htmlencode/encode.aspx.

I tried doing something more elegant such as the following but it didn’t work

<Property Name="PageContent1">
    <![CDATA[<h1>Page Not Found</h1><p>Sorry, that page doesn't exist.</p>]]>
</Property>

Activating the Provisioning Feature

These are all web-scoped features, so the pages are created in whatever SPWeb that a feature is activated on.

You want certain pages provisioned in certain webs, so you need to separate the pages into different modules and features and activate them only on the appropriate web.

I mark all these features as hidden and activate them using PowerShell on the appropriate SPWeb.

Setting Home Pages

If you’re using a site definition to create your sub-sites, you most likely have the site definition create a really simple home page that content authors can later populate with content.

You can include a new home page in the page provisioning process (call it welcome.aspx or something) and use the feature receiver to set it as the site’s homepage and delete the old (and blank) one.

This example assumes that welcome.aspx is being provisioned by the feature, and that you’d like to set it as the site’s homepage and rename it to default.aspx.

using (var web = properties.Feature.Parent as SPWeb)
{
    try
    {
        var publishingWeb = PublishingWeb.GetPublishingWeb(web);

        // Set the new default page
        var pageToDelete = publishingWeb.DefaultPage;
        publishingWeb.DefaultPage = publishingWeb.GetPublishingPage(
            String.Format("{0}/{1}/{2}",
                            publishingWeb.Url,
                            publishingWeb.PagesListName,
                            "welcome.aspx")).ListItem.File;
        publishingWeb.Update();

        // Delete the old default page
        pageToDelete.Delete();

        // Rename the new page to default.aspx
        // "FileLeafRef" is the internal field name for "Name"
        var defaultPage = publishingWeb.GetPublishingPage(
            String.Format("{0}/{1}/{2}",
                publishingWeb.Url,
                publishingWeb.PagesListName,
                "welcome.aspx"));

        defaultPage.ListItem.File.CheckOut();
        defaultPage.ListItem["FileLeafRef"] = "default.aspx";
        defaultPage.Update();

        defaultPage.ListItem.File.CheckIn("Check in default page.");
        defaultPage.ListItem.File.Publish("Publish.");
        defaultPage.ListItem.File.Approve("Approve.");

        publishingWeb.Close();
    }
    catch (Exception)
    {
        throw;
    }
}

0 comments , permalink