Notes from Daily Encounters with Technology RSS 2.0
 
# Monday, December 26, 2011

DateTime can be a tricky data type to deal with. Not only is there daylight saving time and different time zones to keep in mind but also the range and precision can vary in different systems. You are probably already aware of some differences between .NET framework’s DateTime structure in Transact-SQL’s datetime data type.

Most notable is certainly a different minimum value that can be expressed:

  • In .NET framework all values between January 1st 1 and December 31st 9999 are supported.
  • In Transact-SQL only values between January 1st 1753 and December 31st 9999 are supported.

This usually shouldn’t be a problem unless you are working with historic data. Setting proper minimum value in the user interface is not be a bad idea nevertheless.

You might not be aware of the difference in precision between these two data types, though. In some edge cases this can cause strange and unpredictable behavior. Let’s take a look at the following example (based on an issue that had me mystified for some time):

using (var context = new TestEntities())
{
    var task = context.Tasks.Single(t => t.Id == taskId);
    task.CompletedAt = DateTime.Now;
    timestamp = task.CompletedAt.Value;
    context.SaveChanges();
}

using (var context = new TestEntities())
{
    var task = context.Tasks.Single(t => t.Id == taskId);
    Assert.GreaterOrEqual(timestamp, task.CompletedAt);
}

The assertion in the above code will occasionally fail with a message similar to this one:

SampleTest has failed:
  Expected: greater than or equal to 2011-12-26 19:04:28.313
  But was:  2011-12-26 19:04:28.312

If you’re not familiar with entity framework, here’s what’s going on:

  • In the first code block a DateTime value is stored to the database.
  • In the second code block that same value is reloaded from the database.
  • The value from the database is compared with the one stored in a local variable the whole time.

The strange behavior is caused by the fact that milliseconds get rounded in the database: to increments of .000, .003, or .007 seconds, as it is stated in the documentation. The DateTime value can therefore be different when reloaded from the database, hence the occasional failed assertion. While the problem might be fairly obvious in the simplified example above, it can be much more difficult to pinpoint in a larger chunk of code where there could be other reasons for the values to differ. Even more so if you’re not even aware of the rounding.

Knowing this might save you a couple of hours once.

Monday, December 26, 2011 7:28:18 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | SQL
# Monday, December 19, 2011

In a previous post I addressed the issue of using HTTP module based authentication in WCF. The presented solution worked in most cases but failed completely with Windows authentication. In this post I’ll describe the necessary changes to make this work as well.

Let’s first see what goes wrong with the existing solution and why. To configure WCF for Windows authentication, the following changes are required in web.config:

<system.serviceModel>
    <!-- ... -->
    <bindings>
        <basicHttpBinding>
            <binding name="HttpWindowsBinding" 
                     maxReceivedMessageSize="2147483647">
                <security mode="TransportCredentialOnly">
                    <transport clientCredentialType="Windows" />
                </security>
            </binding>
        </basicHttpBinding>
    </bindings>
    <services>
        <service name="WcfAuthentication.Service">
            <endpoint address="windows"
                      binding="basicHttpBinding"
                      bindingConfiguration="HttpWindowsBinding"
                      contract="WcfAuthentication.IService" />
        </service>
    </services>
</system.serviceModel>

Of course the settings have to be matched in IIS: Windows authentication should be enabled for the application while anonymous authentication should be disabled, as well as all the other types of authentication.

After setting all this up any calls to our service will throw a MessageSecurityException: "The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'Negotiate,NTLM'." If you try searching the web for solutions, you’ll notice the same error pops up in many different situations not related to our case. So what’s going on here?

The problem is being caused by the following method in HttpAuthenticationModule:

void context_AuthenticateRequest(object sender, EventArgs e)
{
    HttpContext.Current.User = ProcessAuthentication();
}

Setting the user in the current HttpContext to a custom IPrincipal implementation confuses WCF which expects a WindowsPrincipal as configured. The only way to make it work is to pass through the original user information in this case:

void context_AuthenticateRequest(object sender, EventArgs e)
{
    if (!(HttpContext.Current.User is WindowsPrincipal) && 
        HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.EndsWith(".svc"))
        HttpContext.Current.User = ProcessAuthentication();
}

The extension based filtering is there so that the authentication will still work for the rest of our web application. This change alone is not enough, of course. We still to need the authentication somewhere for the WCF case. HttpContextAuthorizationPolicy is the right spot for it. Evaluate method should be modified as follows:

public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
    HttpContext context = HttpContext.Current;

    if (context != null)
    {
        if (context.User is WindowsPrincipal)
        {
            IPrincipal principal = HttpAuthenticationModule.ProcessAuthentication();
            evaluationContext.Properties["Principal"] = principal;
            evaluationContext.Properties["Identities"] = new List<IIdentity> { principal.Identity };
        }
        else
        {
            evaluationContext.Properties["Principal"] = context.User;
            evaluationContext.Properties["Identities"] = new List<IIdentity> { context.User.Identity };
        }
    }

    return true;
}

Keep in mind that calling a static method in HttpAuthenticationModule to authenticate the user is just a shortcut to make this sample work and is not suggested practice. In production code you’ll want to have your authentication logic implemented somewhere in the business layer and call it from both HttpAuthenticationModule and HttpContextAuthorizationPolicy.

Monday, December 19, 2011 9:12:16 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | ASP.NET | WCF
# Monday, December 12, 2011

WCF has great built-in support for most types of authentication so there aren’t many good reasons to use HTTP module based authentication with it. Having an existing ASP.NET application already using such authentication certainly is one of them. Finding resources on how to do it might be a challenge though. I managed to stumble upon an article by Microsoft patterns & practices team which helped a lot. In a way this post is its abridged and more practical version.

From here on I assume you already have an IHttpModule in your application (ProcessAuthentication() being the method implementing the actual authentication of the user):

public class HttpAuthenticationModule : IHttpModule
{
    public void Dispose()
    { }

    public void Init(HttpApplication context)
    {
        context.AuthenticateRequest += context_AuthenticateRequest;
    }

    void context_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpContext.Current.User = ProcessAuthentication();
    }

    private static IPrincipal ProcessAuthentication()
    {
        // implement your authentication here
        IIdentity identity = new GenericIdentity("Authenticated User");
        return new GenericPrincipal(identity), null);
    }
}

The module should also already be registered in web.config:

<system.web>
    <!-- ... -->
    <httpModules>
        <add name="HttpAuthenticationModule" 
             type="WcfAuthentication.HttpAuthenticationModule"/>
    </httpModules>
</system.web>

The goal is of course getting access to the authenticated user (i.e. IPrincipal instance) in WCF service through ServiceSecurityContext. The following test method is a great way for testing that:
public string GetUser()
{
    if (ServiceSecurityContext.Current != null)
        return ServiceSecurityContext.Current.PrimaryIdentity.Name;
    else
        return null;
}

IAuthorizationPolicy is the interface to implement custom authorization in WCF with. In our case the authenticated user can be accessed through current HttpContext:

public class HttpContextAuthorizationPolicy : IAuthorizationPolicy
{
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        HttpContext context = HttpContext.Current;

        if (context != null)
        {
            evaluationContext.Properties["Principal"] = context.User;
            evaluationContext.Properties["Identities"] = new List<IIdentity>() { context.User.Identity };
        }

        return true;
    }

    public System.IdentityModel.Claims.ClaimSet Issuer
    {
        get { return ClaimSet.System; }
    }

    public string Id
    {
        get { return "HttpContextAuthorizationPolicy"; }
    }
}

Of course the class should be registered in web.config so that our service will use it:

<system.serviceModel>
    <!-- ... -->
    <behaviors>
        <serviceBehaviors>
            <behavior>
                <!-- ... -->
                <serviceAuthorization>
                    <authorizationPolicies>
                        <add policyType="
                             WcfAuthentication.HttpContextAuthorizationPolicy, 
                             WcfAuthentication, Version=1.0.0.0, 
                             Culture=neutral, PublicKeyToken=null"/>
                    </authorizationPolicies>
                </serviceAuthorization>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

There is still one thing missing. If you try out the above code, you will realize that HttpContext.Current is always null even if authorization in our HTTP module was successful. To get access to it you need to enable ASP.NET compatibility:

<system.serviceModel>
    <!-- ... -->
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" 
                               aspNetCompatibilityEnabled="true"/>
</system.serviceModel>

To make your WCF service work in this mode you need decorate it with AspNetCompatibilityRequirementsAttribute:

[AspNetCompatibilityRequirements(RequirementsMode = 
    AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
    // ...
}

Finally, we’re done. If you’ve implemented all of the above correctly, our test method GetUser() should return the user who was authenticated in the HTTP module. Unless you’re trying to use Windows authentication which still doesn’t work in this setup. That’s already a subject for another post, though.

Monday, December 12, 2011 9:29:48 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | ASP.NET | WCF
# Thursday, December 01, 2011

DefaultModelBinder is an essential piece of ASP.NET MVC framework which makes writing strongly typed actions really simple. In spite of its strengths (or maybe because of them) it can still introduce hard to solve problems in your code. Take a look at the following example, a simplification of the problem I was confronted with today:

public class DocumentVersion
{
    public int Id { get; set; }
    public int Version { get; set; }
    public string Name { get; set; }
}

public class DocumentController : Controller
{
    public ActionResult New()
    {
        return View();
    }

    public ActionResult Save(DocumentVersion version)
    {
        if (ModelState.IsValid)
        {
            // save data
            return View("Confirm");
        }
        return View("New");
    }
}

Assuming all DocumentVersion properties are submitted and valid Save action should return Confirm view, right? Wrong! Try it out and you’ll get a validation error on Version property. Taking a closer look it turns out ModelState["Version"].Errors[0].Exception contains an InvalidOperationException: "The parameter conversion from type 'System.String' to type 'MvcApplication1.Models.DocumentVersion' failed because no type converter can convert between these types." Of course there’s no String to DocumentVersion converter. Though, Version property is an int. Why does it want to convert it to a DocumentVersion?

I soon started running out of ideas and fortunately enough I quickly decided to enable .NET Framework source stepping. A few moments later I reached the following piece of code in DefaultModelBinder and suddenly it became obvious what was happening:

if (!String.IsNullOrEmpty(bindingContext.ModelName)
        && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) {
    // We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
    // to the empty prefix.
    if (bindingContext.FallbackToEmptyPrefix) { 
        bindingContext = new ModelBindingContext() {
            ModelMetadata = bindingContext.ModelMetadata, 
            ModelState = bindingContext.ModelState, 
            PropertyFilter = bindingContext.PropertyFilter,
            ValueProvider = bindingContext.ValueProvider 
        };
        performedFallback = true;
    }
    else { 
        return null;
    } 
} 

// Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string)) 
// or by seeing if a value in the request exactly matches the name of the model we're binding.
// Complex type = everything else.
if (!performedFallback) {
    bool performRequestValidation = ShouldPerformRequestValidation(controllerContext, bindingContext); 
    ValueProviderResult vpResult = 
        bindingContext.UnvalidatedValueProvider
                      .GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
    if (vpResult != null) { 
        return BindSimpleModel(controllerContext, bindingContext, vpResult); 
    }
} 

Notice the call to bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) at the top and read the comment above the bottom block of the code. It turns out that in my sample ModelName Was "version" just like one of the DocumentVersion properties therefore DefaultModelBinder decided to use simple model binding which failed because of a missing converter as it was also clearly stated in the exception. You might be wondering where ModelName came from. It’s the name of the action method parameter. Fixing the code was simple now – rename the parameter and the code starts working as expected:

public ActionResult Save(DocumentVersion documentVersion)
{
    if (ModelState.IsValid)
    {
        // save data
        return View("Confirm");
    }
    return View("New");
}

Lesson of the day? Be aware of conventions and make sure parameter names don’t match any of the property names if you are using complex models.

Thursday, December 01, 2011 9:42:37 PM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | ASP.NET MVC
# Sunday, October 02, 2011

This year’s Bleeding Edge conference was taking place this week in the beautiful surroundings of Gozd Martuljek. The second day was dedicated to community driven redelivery of Build. As the last session of the day I had a talk on the aspects of reusing existing .NET framework code in Metro applications for Windows 8. I’ve based the contents of the talk on the following Build sessions:

The slides from my talk are available on SlideShare. Here are the sources for the sample Windows runtime component I've created during the talk.


Sunday, October 02, 2011 8:34:40 PM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | .NET | Metro | Downloads | Presentations | Sources
Sponsored Ads

About Me
Twitter
Support charity with the Humble Bundle Mojam livestream event! http://t.co/9c91hoB9 via @humble 4 days ago
Damir's Corner: Peculiarities of Subversion Path Based Authorization http://t.co/YTFpisrY 4 days ago
These 50 Photos Will Blow you Away http://t.co/HeD6HWit via @photoshoptalent 4 days ago
Another good reason for everyone to rather use Virtual CloneDrive instead http://t.co/ecLmrjZb 9 days ago
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

All Content © 2012, Damir Arh, M. Sc. Send mail to the author(s) - Privacy Policy - Sign In
Based on DasBlog theme 'Business' created by Christoph De Baene (delarou)
Social Network Icon Pack by Komodo Media, Rogie King is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.