Sunday, March 25, 2012

Overriding default ASP.NET MVC 3 scaffolding in Visual Studio 10

Scott Hanselman has a nice reading about overriding ASP.NET MVC 3 scaffolding in Visual Studio. Putting it short:

  1. Copy folder [c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 3\CodeTemplates\] to your project directory (drag-dropping it from Windows Explorer into Visual Studio will do the trick); 
  2. Clear Custom Tool property for all copied T4 template files (*.tt) - this will stop output files being generated by those templates while saving them.

This is it. After that you can modify existing templates / add new ones that would immediately appear in Add View / Add Controller scaffolding dialogs. Since templates are included locally into Visual Studio project they are available specifically for this particular project only.

Friday, March 16, 2012

Intersecting date intervals - the easy way

I guess this is a common situation were you have a task to filter a list of objects that fall into some date range. "Get all my tasks entered today", "Get all my payments made through the last week " - those could be quite realistic examples.

SELECT * FROM dbo.Tasks 
    WHERE tsk_DateCreated BETWEEN @today_start AND @today_end

  SELECT * FROM dbo.Payments
    WHERE pmt_DateMade BETWEEN @week_start_date AND @week_end_date


Those are quite easy scenarios, as you only have a single date to put into the interval: tsk_DateCreated for tasks and pmt_DateMade for payments. Things get little more complicated when you have two date fields come into play.

Examples: "Get all employee vacation list that intersect with a given period", "Get all user accounts that were valid during the given interval".

Solution (the hard way):

SELECT * FROM dbo.EmployeeVacations
    WHERE (empv_DateFrom BETWEEN @date_from AND @date_to) OR 
          (empv_DateTo BETWEEN @date_from AND @date_to) OR 
          (empv_DateFrom <= @date_from AND empv_DateTo >= @date_to) 

  SELECT * FROM dbo.Users
    WHERE (usr_DateFrom BETWEEN @date_from AND @date_to) OR 
          (usr_DateTo BETWEEN @date_from AND @date_to) OR 
          (usr_DateFrom <= @date_from AND usr_DateTo >= @date_to) 


Solution (the easy way):

SELECT * FROM dbo.EmployeeVacations
    WHERE (NOT((empv_DateTo < @date_from) OR (empv_DateFrom > @date_to)))

  SELECT * FROM dbo.Users
    WHERE (NOT((usr_DateTo < @date_from) OR (usr_DateFrom > @date_to)))


Give it a try, you will not regret it.

Update:  another way to check for date interval intersection:

SELECT * FROM dbo.EmployeeVacations
    WHERE (@date_from BETWEEN empv_DateFrom AND empv_DateTo OR empv_DateFrom BETWEEN @date_from AND @date_to)

  SELECT * FROM dbo.Users
    WHERE (@date_from BETWEEN usr_DateFrom AND usr_DateTo OR usr_DateFrom BETWEEN @date_from AND @date_to)

Friday, March 9, 2012

Solving "Received an unexpected EOF or 0 bytes from the transport stream" issue

One of the popular sayings appears to be correct when saying that most of the things are not so easy as they seem to be. Calling a web service with transport security enabled - this is what I'd expect to be easy. And it does... until something similar shows up: 

System.ServiceModel.CommunicationException: An error occurred while making the HTTP request to https://xxx.xxx.xxx. 
This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. 
This could also be caused by a mismatch of the security binding between the client and the server. 
---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. 
---> System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
   
My first idea was that the error was caused by the certificate validation issue so I tried to apply the solution mentioned in my earlier post. No luck this time.

After some research the problem was solved by setting:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3

Quick note, why this was helpful.
It's all related to protocols - SSL3 and TLS - used to establish a secure communication between two endpoints.

  • SSL3 is a security protocol released in 1996 by Netscape Communications and it is a superset of SSL2. 
  • TLS (TLS 1.0 or sometimes known as SSL 3.1) was introduced by IETF in 1999 (RFC 2246) and is a superset of SSL3 although not 100% backward compatible.

More information about the protocols mentioned above could be found here.

.NET applications use TLS for transport security by default. If you look at the static constructor of the ServicePointManager class you would notice the following line:

s_SecurityProtocolType = SecurityProtocolType.Tls | SecurityProtocolType.Ssl3;

My wild guess would be that initially TLS protocol is used to establish a secure connection with the server. In case when the server does not support TLS, the negotiation continues using SSL3 protocol. The problem is that some servers not supporting TLS terminate connection immediately. This is why in such cases SSL3 communication needs to be forced.

Thursday, March 8, 2012

ServicePointManager.ServerCertificateValidationCallback - the magic cure

The things I am going to touch in this article are widely described. It is a common task for a developer to test WCF services on the development environment having transport security enabled for a service. So basically what is done here:

1) IIS is configured to enable SSL for a web site, hosting a WCF service.
    Instructions for IIS 6.0 can be found here.
    Instructions for IIS 7.0 can be found here.
2) Self issued certificate is being used for that purpose.
    Instructions for creating self issued certificate can be found here.
3) WCF service and client bindings are configured to have transport security enabled.
    Instructions to enable WCF transport security can be found here. 

After spending couple of days on the steps mentioned above sometimes you might come to a glorious moment when you feel strong enough to test your service. After doing that you might see something like this:

System.ServiceModel.Security.SecurityNegotiationException: Could not establish trust relationship for the SSL/TLS secure channel with authority 'xxx.xxx.xxx'. 
---> System.Net.WebException: The underlying connection was closed: Could not
establish trust relationship for the SSL/TLS secure channel. 
---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

It is actually saying that your certificate cannot be validated. You might try solving this problem by adding the server certificate to some trusted authority certificate store on your local machine. But my guess is that 99% would solve this issue like this:

ServicePointManager.ServerCertificateValidationCallback += 
   (sender, certificate, chain, sslPolicyErrors) => true;

What happens then is that you have your client and server talking to each other using a secured channel. Nice, isn't it? Make sure it is for debugging purposes only.

What if you have a client, talking to multiple services hosted by different service providers? This could be a real world scenario, i.e. an integration module talking to multiple B2B systems.

Overriding ServicePointManager.ServerCertificateValidationCallback behaviour would turn off server certificate validation globally for the whole client application. This is a problem if you want your certificate validation to be shut off for a single service only.

It is worth to take a quick look into the place where ServicePointManager.ServerCertificateValidationCallback is used in the .NET framework infrastructure. To be more specific: look for a HandshakeDoneProcedure.CertValidationCallback method. This is a private class so most probably Reflector is the tool to help you here. Or let me share a snippet:

private bool CertValidationCallback(string hostName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    ...
    bool flag = true;
    ...
    if (ServicePointManager.ServerCertificateValidationCallback != null)
    {
        flag = false;
        return ServicePointManager.ServerCertValidationCallback.Invoke(this.m_Request, certificate, chain, sslPolicyErrors);
    }
    if (flag)
    {
        return (sslPolicyErrors == SslPolicyErrors.None);
    }
    return true;
}

Default certificate validation behaviour (when validation callback is not assigned) consists of validating sslPolicyErrors value. You might want to do the same for the services you still need server certificates to be validated.

ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(ValidateCert);

public static bool ValidateCert(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    HttpWebRequest request = sender as HttpWebRequest;

    if (request != null && request.Host == "host_name_to_skip_validation")
    {
        return true;
    }

    return sslPolicyErrors == SslPolicyErrors.None;
}