Using T4 template to generate a typed class from a resource file

by Stefano Mostarda 15. November 2011 15:46

A couple of days ago, a customer asked me to let him edit the labels of the web application. The customer didn't want to edit web pages, but wanted an XML file containing labels to translate and wanted to be able to modify translations at runtime.

At first I thought about using a plain XML file and create an api to access lables in the file by their key. However, why on the earth should I create a custom XML file when I have RESX files which already give me tyed access? The problem with RESX files is that they are compiled and embedded in the application dll so they can't be easily modified at runtime unless you use the tool distributed in the .NET Framework SDK (which wasn't what my customer wanted).

Anyway I still wanted to use RESX files and have typed access to label. To achieve my goal I decided to:

  • create the RESX file leaving it as a content file without embedding it in the application dll;
  • create a T4 template that reads keys in the RESX file and generate a class with a property for each key. Each property is named like the key in the resx file and returns the value. I have also added a method in the generated class which accepts the key as argument and returns the value (the properties internally call this method);
  • each time I add a key in the RESX file, I launch the T4 template so that the generated class exposes a new property for the key.

What's great about this approach is that I combine the advantages of the embedding the RESX file in the application dll (typed API to access properties) with the advantages of having a file that can be modified at run time.

Creating the resx file

This was the easiest part. I have created the file inside the Visual Studio project and I have set Build Action property of the file to Content.

Creating the T4 template

The T4 template wasn't difficult to write. Here is the code.

<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ assembly name="System.Windows.Forms" #>
<#@ import namespace="System.Resources" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".cs"#>
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
using System.IO;
using System.Resources;

<#
var filename = "resources.resx";
var filepath = Path.Combine(Path.GetDirectoryName(this.Host.ResolvePath("")), "FILEPATHINASSEMBLY", filename);

using (ResXResourceSet resxSet = new ResXResourceSet(filepath))
{
#>
namespace MyNamespace
{
    public static class ResourcesManager
    {
<#
    foreach (DictionaryEntry item in resxSet)
    {
#>
        public static string <#=item.Key.ToString()#> { get { return GetString("<#=item.Key.ToString()#>"); } }
<#
    }
#>

        public static string GetString(string key)
        {
            var ctx = HttpContext.Current;
            if (ctx.Cache["__Resources"] == null)
            {
                var filePath = Path.Combine(ctx.Request.PhysicalApplicationPath, "<#=filename#>");

                Dictionary<string, string> data = new Dictionary<string, string>();
                using (ResXResourceSet resxSet = new ResXResourceSet(filePath))
                {
                    foreach (DictionaryEntry item in resxSet)
                    {
                        data.Add((string)item.Key, (string)item.Value);
                    }
                }
                ctx.Cache.Insert("__Resources", data, new CacheDependency(Path.Combine(ctx.Request.PhysicalApplicationPath, "<#=filename#>")));

            }
            var resources = (Dictionary<string, string>)ctx.Cache["__Resources"];
            if (resources.ContainsKey(key))
                return resources[key];
            else
                return key;
        }
    }
}
<#
}
#>

The code is pretty straightforward and doesn't need explanation.

Using the generated class

Now we can use the generated class like this:

MyLabel.Text = ResourcesManager.MyTranslatedProperty;

You could also create an ExpressionBuilder to improve readability of markup in the page, but that's another feature and I'm not going to talk about it in this post.

 

Stay tuned...

Tags:

ASP.NET

Table Per Hierarchy Inheritance mapping strategy using Entity Framework Code First

by Stefano Mostarda 16. August 2011 09:00

A couple of weeks ago I was reviewing the demos of my Entity Framework course. Code-First demos where really poor so I worked to improve them.

While I was working on demos, I decided to create a series of blog post on this subject. In this first subject I discuss about Table Per Hierarchy inheritance mapping strategy.

Table Per Hierarchy Teory

Unlike OOP, database don't support inheritance. This means that we have to find a way to simulate inheritance in a relational database. One way of simulating inheritance in the database is Table Per Hierarchy (TPH) strategy. The TPH strategy is pretty easy. You create one table containing columns for all properties of all the classes in an inheritance chain. Additionally, the table contains a "discriminator" column which specifies what class the table row has data of.

Let's make an example. Suppose you have a PaymentInfo base class with four properties. Then you have a CreditCard class with two properties and a BankTransfer class with two properties. In such case, you create a table (say PaymentDetails) with 9 columns: four for PaymentInfo, two for CreditCard, two for BankTransfer and one for the Discriminator.


Putting it into figures here is the model, the database and their mapping:

If the Type column (the discriminator) is C, the row contains data about a credit card. If the Type column contains B, the row contains data about a bank account. This way you can persist an entire hierarchy of classes inside a single table. It's pretty easy sin't it? Let's see how to create, map and persist such model using Entity Framework Code First and DbContext APIs.

From theory to Practice: creating and mapping the model

To map the model the first thing we have to do is creating the model classes as follows.

public class PaymentDetailBase
{
  public int Id { get; set; }
  public string Number { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}

public class BankTransfer : PaymentDetailBase
{
  public string BIC_SWIFT { get; set; }
  public string BankName { get; set; }
}

public class CreditCard : PaymentDetailBase
{
  public string CCV { get; set; }
  public DateTime ExpiryDate { get; set; }
}

Once the classes are created, we have to override the OnModelCreating method of the DbContext derived class and create the DbSet property:

public class MyContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Entity<PaymentDetailBase>()
      .Map<CreditCard>(s => s.Requires("Type").HasValue("C"))
      .Map<BankTransfer>(s => s.Requires("BankTransfer").HasValue("B"))
      .ToTable("PaymentDetails");
  }

  public DbSet<PaymentDetailBase> PaymentDetails { get; set; }
}

I have removed the mapping information for the single properties, but that's not important for this discussion. Now we can query the model and persist its data.

Persisting the model

To add a credit card we can write the following code;

using (MyContext ctx = new MyContext())
{
  var cc = new CreditCard { ... };
  ctx.PaymentDetails.Add(cc);
  ctx.SaveChanges();
}

As you see there's no difference between persisting a simple class or a class in an inheritance hierarchy.

Querying the model

Let's see how to query the model:

The following query retrieves all credit cards:

ctx.PaymentDetails.OfType<CreditCard>();

The OfType<T> method is used to retrieve only the object of a given type in the hierarchy.

The following query retrieves the full name used in payment methods:

ctx.PaymentDetails.Select(c => c.FirstName + " " + c.LastName);

Performance considerations

When persisting an entity mapped usign TPH strategy in the database, EF generates a single SQL command because data are in one table. When querying, EF generates a single query for the same reason as before. This means that persisisting a class using the TPH strategy offers great performance.

Conclusion

I hope this post will help you in understanding better how TPH works with Entity framework. In a future post I'll cover the TPT strategy which addresses the same inheritance problem in another way.

 

Stay tuned...

ASP.NET 4.0 + WCF Rest Services and the request validation problem

by Stefano Mostarda 19. March 2011 01:48

Some days ago I was working on ASP.NET 4.0 project with several WCF REST services invoked from javascript. Everything worked fine except for a nasty problem which affected our REST services: input was not validated. Let's lay the background.

Request validation is the ASP.NET feature which prevents the user from submitting malicious data. If the user enters in a text box something like "<javascript type="text/javascript">alert(1);</javascript> and submits the page, ASP.NET blocks the request because it detects that the input is malicious. Data Request validation is a feature that's always been in ASP.NET since 1.0 version. The main problem which affects request validation in version previous to ASP.NET 4.0 is that it is triggered only for aspx pages. It means that request validation isn't triggered for handlers and services. ASP.NET 4.0 has maintained compatibility with the past, but has also introduced a new request validation model. The request validation in ASP.NET 4.0 is triggered before the Begin Request which means that all request in the ASP.NET pipeline are examined including handlers and services. The validation is also extensible, we can create a new class which inherits from RequestValidator to inject our validation logic (it turns out to be particularly useful when using WIF). In such class we just have to override the IsValidRequestString method.

public class MyRequestValidator : RequestValidator
{
  protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
  {
    validationFailureIndex = 0;
    //my logic
    return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
  }
}

Finally we have to register such class in the httpRuntime node of the web.config file.

<httpRuntime requestValidationType="MyRequestValidator, MyAssembly"/>

The IsValidRequestString method doesn't receive all request values at once, but is invoked for each value in the request. For instance, if a request has 8 headers, 3 values in querystring and 20 values in post, the IsValidRequestString is invoked once for each of them which means 31 times.

Let's get back to the problem. What was happening in my case, is that validation was triggered (the IsValidRequestString from my custom validator class was hit), but POST data coming from the REST call weren't inspected. The IsValidRequestString method was invoked only for the request's headers. I investigated a bit and discovered that the problem was in the content-type header. I used the following jQuery code to issue the REST call:

$.ajax({
    dataType: "json",
    data: "{ \"key\":\"value\" }",
    contentType: "application/json; charset=utf-8",
    type: "POST",
    success: function (response) { ... }
  });

I just changed the content-type from "application/json; charset=utf-8" to "application/x-www-form-urlencoded" and everything were validated; even POST data. I don't know if it's the best solution, but it's a solution which passed all our security tests.

To ensure that everybody called the REST service using the right content-type, I created the AjaxSecure function in a jQuery plugin so that everyone in the team invokes that function instead of the Ajax function. The AjaxSecure function just set the correct content-type before invoking the Ajax function.

(function ($) {
    $.extend({
        ajaxSecure: function (obj) {
            $.extend(obj, { contentType: "application/x-www-form-urlencoded" });
            $.ajax(obj);
        },
    });
})(jQuery);

Stay tuned...

Connecting to a SQL Server named instance when you are not in the domain

by Stefano Mostarda 4. March 2011 16:54

Yesterday I spent something like 2 hours trying to connect to a SQL Server named instance. The database was located in a domain my machine was not registered in. Everyone in the domain, but me, could access the database. IT wasn't able to fix the problem on my laptop so I was left on my own.

After a bit of research I discovered the problem was that since my laptop was outside the domain, my laptop wasn't able to send the UDP message to SQL server on port 1434 (this is necessary to get the named instance port). I still didn't solve the problem, but I got close. What I needed was just the port of the named instance. IT gave me the port number I could set my database name like this:

<MACHINENAME>\<NAMEDINSTANCE>,<PORT>    which in mycase translated to   DEVSQL\INSTDEV,6734

 

I will remember this forever and I hope that if you'll ever have this problem, you will fastly come accross this post.

 

Stay tuned...

Entity Framework CTP5: typed Include method

by Stefano Mostarda 18. December 2010 11:08

Code-First has been out for over a week and there are countless post on countless blog talking about it. The fluent API and the DataAnnotations are the major features of this CTP, but I think that the DbSet class has another interesting one that most of us has been askingn for a while: a typed Include method.

The ObjectQuery<T> class offers an Include method which uses a string to detect what properties to eager load. bleahhh. Most of us have created a convenient method to create an Include extension method which accepts a lambda and not a string. The team has finally incuded such method in Entity Framework so now we can write the following query:

public class OrderContext : DbContext
{
  public DbSet<Order> Orders { get; set; }
}

...

using (var ctx = new OrderContext())
{
  var orders = ctx.Orders.Include(o => o.OrderDetails);
}

As I said before: it's something we have been doing for over 2 years.

What is bad with all the solutions we created in the past, is that they allowed loading only direct children of the main entity. In the above example I loaded the order and the details, but what if I want to load the products associated to the details too? Using the Include method that accepts a string this is supereasy.

var orders = ctx.Orders.Include("OrderDetails.Product")

I haven't found any solution to do the same thing using a lambda. The team came up with a good solution using the Select Linq method:

var orders = ctx.Orders.Include(o => o.OrderDetails.Select(d => d.Product));

Pretty simple isn't it? :)

Stay tuned...

Entity Framework: passing expressions through methods

by Stefano Mostarda 25. November 2010 18:51

One of the most bautiful things about LINQ is that you can define expressions and then pass them throug methods. The following code shows an example.

private static void Filter(Func<Order, bool> expr){
  using (var ctx = new NorthwindEntities())
  {
    var orders = ctx.Orders.Where(expr).ToList();
  }
}

Filter(o => o.ShipCity == "London")

Using the Func class is easy like a pie. If we use this technique with LINQ to Entities, we correctly obtain data but at the same we open the door to the most dangerous pitfall. A couple of days ago I was having a twitter with Mikael Eliasson. He was having problems because the LINQ to Entities query returned the correct data, but the query launched by the database didn't perform the filter. So what's happening?

The answer is that the query returns all data and the filter is performed in memory. Isn't it freightening (at least)? Fortunately, the solution to the problem is pretty easy: instead of using the Func class, we have to use Expression transforming the input parameter from Func<Order, bool> to Expression<Func<Order, bool>>.

private static void Filter(Expression<Func<Order, bool>> expr){
  using (var ctx = new NorthwindEntities())
  {
    var orders = ctx.Orders.Where(expr).ToList();
  }
}

Filter(o => o.ShipCity == "London")

This way the LINQ to Entities provider is able to generate the correct SQL. In the end, this post reminds me about the main rule when using O/RM and SQL generator in general: always test ALL queries, the pitfall is behind the corner.

How I would like the Entity Framework Designer to be

by Stefano Mostarda 27. October 2010 17:04

When I first read this post about Entity Framework Designer improvements I felt happy because many limitations (big graphs management above everything) where finally fixed. However there are two things that I think are badly missing and that would make me happy forever: the ability to drag&drop entities from the solution explorer window into the designer and the ability to import the database into the designer without touching the CSDL.

Let me speak my mind.

Suppose that I'm creating a new application. I have seen countless times people designing entities and then generating code using third party tools. Other times I have seen people writing code for their entities inside Visual Studio. In this Code-First situation the only way to go is writing the mapping via code. What I would really love is the ability to Drag&Drop entities from Solution Explorer window into the designer and having it generating the CSDL. Once that's done, we can do two things:

  • We can create a database like in model-first scenario.
  • We can create a template to generate Code-First mapping code.

 

Now Suppose that you already have the database. If we could import it without having the CSDL touched, we could map the entities to the tables in a while like if we were in a database-first scenario.

Currently, the second task (importing database without touching the designer) is already feasible writing a designer extension. What's not feasible is dragging entities from window explorer into the designer (maybe we can do it working with VS API but I imagine that to be extremely tough).

I talked to the team about such feature but it seems I'm the only one who asked that.... Don't you think this would be a nice feature to include in next version?

 

Stay Tuned...