ASP.NET Web Service and Integrated Windows Authentication only working with IP address

 

We have an issue on a customer deployment where our web service can only be accessed when communicating using the IP address in the URL rather than the host name. We are using integrated windows authentication to pass the users windows credentials so we can automatically log the user into our application. The application itself is a windows form app using dot net 2 web proxies to access asp.net web services.

In our environment accessing the services using a host name is fine but in the clients environment accessing via the host name fails, but IP address access succeeds. In short it appears the issue surrounds the interplay between Kerberos and NTLM authentication. The main reason our test environment is different to the clients environment is that they run the web services as a specific domain user and not NETWORK_SERVICE, when you do this Kerberos will not work out of the box.

In short the following links show how the clients deployment environment can be changed to work with domain names when not running the app pool as network service, either by turning of Kerberos authentication off, or by fixing it properly using Service Principle Names (SPNs)

Essentially NTLM is always used when communicating via an IP address (Wikipedia NTLM), this would explain why accessing the web service via an IP address works.

To change the authentication and to turn NTLM and Kerberos authentication on.

http://support.microsoft.com/kb/215383

To turn it off and the reason why:

http://piers7.blogspot.com/2007/04/disable-kerberos-on-windows-2003-using.html

Fixing by using a SPN

http://support.microsoft.com/?id=929650

Investigation in our environment

The following text records the investigation in our test environment surrounding the asp.net web service running as a domain user or NETWORK_SERVICE account.

The domain controller sees a logon event when talking to a web service not using network service as a user, but this doesn’t show any issue, in general I didn’t see an events in the domain controllers security logs that show the issue:

image

There are no logon events when communicating to a web service using network service.

The Application server has the authentication methods set to: “Send LM & NTLM – user NTLMv2 session security if negotiated” was ''Send NTLM response only on our test environment”. Potentially its what the setting is set in the client that counts here, this has no effect on choosing between Kerberos and NTLM though.

Communicating to the IP address;

No clients can connect to the web service when using the IP address for some reason, the exact opposite of the clients environment. Potentially NTLM is broken for us and Kerberos is the only thing that is working, our test environment is running in a VM environment with its own DC and network, the client and app servers though are also connected to our main network to make RDP easy, potentially this is causing issues and definitely an area to investigate.

Coms Traffic

DNS Name, Network Service

Response 401

WWW-Authenticate: Negotiate

WWW-Authenticate: NTLM

 

Request

Authorization: Negotiate YIIE0QYGKwYBBQUCoIIExTCCBMGgJDAiBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICCqKCBJcEggSTYIIEjwYJKoZIhvcSAQICAQBuggR+MIIEeqADAgEFoQMCAQ6iBwMFACAAAACjggOpYYIDpTCCA6GgAwIBBaEKGwhDUzAyLkNPTaIlMCOgAwIBAqEcMBobBEhUVFAbEmNzMDJhcHAwMS5DUzAyLmNvbaOCA2UwggNhoAMCARehAwIBDKKCA1MEggNP4AAuMFXJKP79YjQoyhaQKaG5Iwmmk8dFvKK/gb+78ZYUZRkZ42S+gNrfETPNJhufN2CGFwyJ9PIBmk0Ra7FIkZrJPLPHcBN97M46O1VOQtJdYYX0XvpdgrjEtuCobGAI43gKEMfGBaSZ1lt5Pi7xpNo1xN76/pgq+4vJvQ+SnOGPLgTRk/lSoVXyX3zJ5UJSmROSsAD46RU6eIQ+pNR8+NjZvd5sclQ8P/L/y62UKFcWKZA2ppVghIrQd1kmvgfip4EdR1/Apf+rT8gvgqxPuSHhfdL/kaQRWjY0dvdCKlxszytF2HQN1wYl/6ECezfeuejwtljVfSjzuAsnhjE2aESJpmXohoTiPD1HD8Hsa6Je8y7WLHyh8+DVoVJzLXPBRg/FxPxrsq1YL/KOJnx9+y9sPwUwMci621Fd571Me+I531YOCPMEQG0APOZR7zGTQRaWMQN602zsh9AKeFcUhcAvDHBPvHzeQ7m5YeOUHNIEH3lrnD8NV5UOqHePZunsHLV9aiehaUtT6rrpjOvVQ1tuvopwph7UE/PStfjLDeG1ClHWlTo8vKdYVaL1w7ktnwhifw/oqd4AiChwe6NIcuFAnvTG1eWoUPcVMHnknPSk6tmclYqn3iDKqnfw52VNoE1f8aRldCWm4hqgnUPbA6+5Z7oWzBy7sxsqE3xaJcsIxeOeeplcEyF7MYQfwo0D5RofHEN2qU+MG/oMCDpMm6zyK7HJXIiAEJh81rb53c9MdAl9aC8X/oUF2x36ntau2KUmm35Wt0QCL5qpjLdMFqYowWzQkogoVykBNUPVeYaaFdYpx/uuRMhWgM9k+8YridLAs+UX4ToJ456Zszk88UiUV7bIQU5LZGg9UDnBQzXSequLcUqD87CVhZb0tTzT7R6JRW9gkkij5tvMCBt8ab4xhhzOz++jPnmLJMSlOwujCNKgD3B0gzMlVHafDJmG3OBbVg3wuXLX5CDqJ4hZh1kpku2bj4mQvD1ZD+Bo+XTkrUCx4gCZAGr/vgizCjQDt4hEz83/9sD+FQP7Phwcfzx3AkWyg01Tb22n8KcF5bg/oy6I30t0eOr/llHtf/spc6PYVY5BZyIqsC7wv9RWaJfPvoTt/MYuni7wlL5zdaSBtzCBtKADAgEXooGsBIGpWy0v1ywq0PMWh3Q6HdKAgMZq4HJASGqCT+Ccg4wIRNnlVYsoEz822J0DdcB3qaxXIX1NcOKIR5Xcds9yJGOR1imL8YHCJX3DsMGqfs4XUBVzF5Y+SGG43/XL7++ERITTbSFzTpwsS82EylwvOEyyAyC96golat9VSJTy3fbzisgGpZn+M4wGeiCOdJz7bS38Xhl+jPkKZpQtb0wDG1juJf4QvSAXFrzPUA==

 

Response 200

WWW-Authenticate: Negotiate oYGgMIGdoAMKAQChCwYJKoZIgvcSAQICooGIBIGFYIGCBgkqhkiG9xIBAgICAG9zMHGgAwIBBaEDAgEPomUwY6ADAgEXolwEWoZdDKSN9Xs6Da1N/G/a/wB0aW1V47bz9OpCwXBnHxTx0tPGrxhI7474Nbg1zaLrIJvOorUSE/q010CuG0t0xYoHIbkirCFcK6LrQgAkXIuyEgkvrTJ+Rl+TBg==

No logon events

 

IP Address, Network Service

Response 401

WWW-Authenticate: Negotiate

WWW-Authenticate: NTLM

 

Request –> 401

Authorization: Negotiate TlRMTVNTUAABAAAAt4II4gAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw==

Response

WWW-Authenticate: Negotiate TlRMTVNTUAACAAAACAAIADgAAAA1gonihXsqEfDBUGoAAAAAAAAAAHYAdgBAAAAABQLODgAAAA9DAFMAMAAyAAIACABDAFMAMAAyAAEAEgBDAFMAMAAyAEEAUABQADAAMQAEABAAQwBTADAAMgAuAGMAbwBtAAMAJABjAHMAMAAyAGEAcABwADAAMQAuAEMAUwAwADIALgBjAG8AbQAFABAAQwBTADAAMgAuAGMAbwBtAAAAAAA=

 

Request –> 401

Authorization: Negotiate TlRMTVNTUAADAAAAGAAYAHYAAAAYABgAjgAAAAgACABIAAAADAAMAFAAAAAaABoAXAAAABAAEACmAAAANYKI4gUBKAoAAAAPQwBTADAAMgBhAGQAbQBpAG4AMQBDAFMAMAAyAEQARQBTAEsAVABPAFAAMAAxAA7a339UA42oAAAAAAAAAAAAAAAAAAAAAAn8jU3tc3kILSb0cqGJjoiOlOdOcU8Le9CGQnUdqskW6W9Z5Fd4R5g=

Response

WWW-Authenticate: Negotiate

WWW-Authenticate: NTLM

 

Similar requests when not using network service, the second is a 401 with

Request

Authorization: Negotiate YIIE0QYGKwYBBQUCoIIExTCCBMGgJDAiBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICCqKCBJcEggSTYIIEjwYJKoZIhvcSAQICAQBuggR+MIIEeqADAgEFoQMCAQ6iBwMFACAAAACjggOpYYIDpTCCA6GgAwIBBaEKGwhDUzAyLkNPTaIlMCOgAwIBAqEcMBobBEhUVFAbEmNzMDJhcHAwMS5DUzAyLmNvbaOCA2UwggNhoAMCARehAwIBDKKCA1MEggNP4AAuMFXJKP79YjQoyhaQKaG5Iwmmk8dFvKK/gb+78ZYUZRkZ42S+gNrfETPNJhufN2CGFwyJ9PIBmk0Ra7FIkZrJPLPHcBN97M46O1VOQtJdYYX0XvpdgrjEtuCobGAI43gKEMfGBaSZ1lt5Pi7xpNo1xN76/pgq+4vJvQ+SnOGPLgTRk/lSoVXyX3zJ5UJSmROSsAD46RU6eIQ+pNR8+NjZvd5sclQ8P/L/y62UKFcWKZA2ppVghIrQd1kmvgfip4EdR1/Apf+rT8gvgqxPuSHhfdL/kaQRWjY0dvdCKlxszytF2HQN1wYl/6ECezfeuejwtljVfSjzuAsnhjE2aESJpmXohoTiPD1HD8Hsa6Je8y7WLHyh8+DVoVJzLXPBRg/FxPxrsq1YL/KOJnx9+y9sPwUwMci621Fd571Me+I531YOCPMEQG0APOZR7zGTQRaWMQN602zsh9AKeFcUhcAvDHBPvHzeQ7m5YeOUHNIEH3lrnD8NV5UOqHePZunsHLV9aiehaUtT6rrpjOvVQ1tuvopwph7UE/PStfjLDeG1ClHWlTo8vKdYVaL1w7ktnwhifw/oqd4AiChwe6NIcuFAnvTG1eWoUPcVMHnknPSk6tmclYqn3iDKqnfw52VNoE1f8aRldCWm4hqgnUPbA6+5Z7oWzBy7sxsqE3xaJcsIxeOeeplcEyF7MYQfwo0D5RofHEN2qU+MG/oMCDpMm6zyK7HJXIiAEJh81rb53c9MdAl9aC8X/oUF2x36ntau2KUmm35Wt0QCL5qpjLdMFqYowWzQkogoVykBNUPVeYaaFdYpx/uuRMhWgM9k+8YridLAs+UX4ToJ456Zszk88UiUV7bIQU5LZGg9UDnBQzXSequLcUqD87CVhZb0tTzT7R6JRW9gkkij5tvMCBt8ab4xhhzOz++jPnmLJMSlOwujCNKgD3B0gzMlVHafDJmG3OBbVg3wuXLX5CDqJ4hZh1kpku2bj4mQvD1ZD+Bo+XTkrUCx4gCZAGr/vgizCjQDt4hEz83/9sD+FQP7Phwcfzx3AkWyg01Tb22n8KcF5bg/oy6I30t0eOr/llHtf/spc6PYVY5BZyIqsC7wv9RWaJfPvoTt/MYuni7wlL5zdaSBtzCBtKADAgEXooGsBIGpQLxhefwgE1t2sjQg+R0KYIhuUh2YhsdAXNmqkDt8dYVY+vczAA5UGPoghBgoPDdD/RAcF1I8XQMNaMQRwVfI+MGLxL6kVGtUbnizUf7o1jmDfvZ09+9TZqnqUXuMDd+IpvgnWI5K5bI6M4PWeDY9Exk2GmVlxBZ3pCREkcE/ke2A53CaZ6dF8OGEbSWWd4x/ec5EqxqeyfAvbsfxaJ6nnh9BK7msOMQBzw==

Response

WWW-Authenticate: Negotiate oYGIMIGFoAMKAQGhCwYJKoZIgvcSAQIConEEb2BtBgkqhkiG9xIBAgIDAH5eMFygAwIBBaEDAgEepBEYDzIwMTAwNzEyMDk1MzQ4WqUFAgMGSpSmAwIBKakKGwhDUzAyLkNPTaolMCOgAwIBA6EcMBobBGhvc3QbEmNzMDJhcHAwMS5jczAyLmNvbQ==

and a following request with

Authorization: Negotiate oYIEnzCCBJuiggSXBIIEk2CCBI8GCSqGSIb3EgECAgEAboIEfjCCBHqgAwIBBaEDAgEOogcDBQAgAAAAo4IDqWGCA6UwggOhoAMCAQWhChsIQ1MwMi5DT02iJTAjoAMCAQKhHDAaGwRIVFRQGxJjczAyYXBwMDEuQ1MwMi5jb22jggNlMIIDYaADAgEXoQMCAQyiggNTBIIDT0yHCikaYn0FY3dNwR4Eo4ma+tiJpbeAiiDkjMnpGzphbxn5OryeeIzoPxOHd2o84uEOlAl2dpTBGpZ5YxFbO3o4BFFIUcATEX5TiORQVuYO606oywDlIXjow6rz8N6HyITZm/CLmCXOXG0zrtSldD6QcQ5hTP5A7NlPGSl16ULaDTobBi9lRvpvn6kIM4BRWOJyNQmtUqXRV8vjkuodnbDFhxieRwy3QhD+HqVja+Kqwcw1nEhnWHEsUhw+31WFFbH1g0bD212xQBW7SflrTw6YF8laFa80AbU8QqkgDXcTFj6FmHBwb87NbSNkPZrOGKVBVN8VEc+L1fy5i41q+hogihLYXVtLTrO7tvYW4NmWa6T+2K2cft1GI9HZLmPJBKpX7sggb9pvYnH0I6jsbjQvjV8203eToCPI+doN0pc/ba4RhjHvIDcjtHkhpEe3kvpcYhYZfdQax4wat1Zaeqomqlaf4TqMukEYctLt5x/NpC4ETVnzgbUalOU3wUrE03l4AyMpUu6n/GREgDzyfri8IaQNGbeR9AnUUvf/M6SwEc1OABmdDdAdi+9h1xMlHcBClP+EsXfDJghzez/bp1iuQWv3EzgsnMKmLX6MinDyjjVxyPacYtgZGswUfdlXwbRPpQ4s3VWYfyHPYltrGSFZgRBccQE6Dj9Y6I/WSz1dhIcqDMIRx+8AaiAeujpDBa4dtR8fKhzvkx5AGJpGn7feRena2wznjU+4H9rt6jeA2Y+3Wh8dcfBUYk/CqBvciN1DdPXtf/vNCszlo6wf5VGGPxk49Lhj7OfKdGWmEGNXbqM4ReOQgImKvLk4plqn2BLLW6Up+KVCXivibx9+HCSuMc36n0WonsCGh4U21iTS8Yg49xFQ4LeShdjt/Ds8gfv85nBcho6jINXkq2v4xuQDSs0C8T58oVN5p4Zof3M80o2AaoNr3vkLZ1pTqXDjk8jH+YYz15E+UOO4TWvdShT5f6kcLUebGFHQiamO+8nSagwYbTWRL1JH+GbrFQCf2ffv2GJEYqBakQW96CwbxVZqIrMFAMt/QBfvUj7LUFMS/DaHrTajvAlHui2G6y86S+c2ykgOMMXSe6N+3rNej9faF0zp0Axmij+gNfi01lekgbcwgbSgAwIBF6KBrASBqcEFv86hjFvokwjI5uDEI4oYOLSnfoZk2Eb4NWWU30+a5hSqiDpCjaVjs4+SQWA6ZhIM/f6IePTMLD86qCP9/9QTvkD3tlwwFrzEOc3vWxP1fNM9rGrAmVyzUAXaZMrvLTz+in9/1wsBKllcQeuc3TMk2GFjzmFMmVO5trX9fQB2dWkfcRh31RC9bVJb87BqUkYc4iBjjpf9rZvOQjJIMypnxhLxbDg69ek=

Then the ending response with the two types of www-authenticate only.

TFS Build Agent and MSBuild Running the VS 2005 version of MSTest when it should be VS 2008

 

One of our build servers is having trouble running one of our MSTest unit test assemblies, failing because it cannot parse the MSTest Test Run Config showing the error:

  Expected type attribute not found.

  File e:\teamserver\releasebuild\Assassin\Base SqlServer Continuous Integration\Sources\localtestrun.testrunconfig Line 2 Pos 2.

The log also shows that it is loading mstest from the VS2005 location.

The "TestToolsTask" task is using "MSTest.exe" from "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\MSTest.exe".

The build box has VS2005 installed and VS2008 and fail the build, the developers environment and a different build box only have VS 2008 installed and pass the build.

MSTest fails as our localtestrun.testrunconfig file uses the VS 2008 format and some of its features, so we have to run the VS 2008 version of MSTest .

It turns out that the reason the wrong (from our point of view) MSTest is running is that the unit test assembly has a reference to the VS2005 Microsoft.VisualStudio.QualityTools.UnitTestFramework assembly, changing it to the VS 2008 reference causes the correct MSTest to be run.

The “testtoolstask” used to run tests within a TFS build inspects the Microsoft.VisualStudio.QualityTools.UnitTestFramework assembly reference to determine which version of MSTest to run.

Also, in Visual Studio the reference to the Microsoft.VisualStudio.QualityTools.UnitTestFramework assembly is shown pointing to VS2008 (version 9). Looking at the .csproj file of the unit test project directly, it references version 8; the VS2005 assembly.  I don’t have VS2005 installed so there must be some assembly version rebinding that happens so that  the VS2008 version 9 can be used for the VS 2005 version 8, hiding the issue.

Running Oracle SqlDeveloper 1.5 on Windows 7 and Vista using Aero

I noticed after installing SqlDeveloper on a new Windows 7 install that running it caused the Aero 7 interface to fail. Although I haven’t proved it, I remember that older versions of Java had compatibility issues with Vista causing the OS to turn off the Aero interface, think it was linked to swing using the direct draw interface in combination with some older windows UI interfaces breaking Vista. Anyway they fixed most of the versions of Java to work with Vista and the issue went away.

The same issue appears to happen when SqlDeveloper runs. I’m guessing that its got an old version of the JRE which hasn’t got the fix in to work with Vista. So I stopped the issue by replacing SqlDevelopers JRE folder with the latest JRE from Sun.

The following steps solve the problem:

  • Install the latest JRE from Sun, I’ve got 1.6.0_16, just go to www.java.com for that. I suspect the latest version of 1.5 will work to.
  • Rename C:\Program Files\sqldeveloper\jdk\jre to C:\Program Files\sqldeveloper\jdk\jre_old (just in case)
  • On 32bit Windows, Copy C:\Program Files\Java\jre6 to C:\Program Files\sqldeveloper\jdk\jre (you must remove the 6 from the end of JRE)
  • On 64bit windows Copy C:\Program Files (x86)\Java\jre6 to C:\Program Files\sqldeveloper\jdk\jre (you must remove the 6 from the end of JRE)

Run SQL Developer and Aero should still be enabled.

One issue you might have is that you copied a server JRE to the SqlDeveloper folder. SqlDeveloper directly invokes the client hotspot JRE and will just show an error dialog box if the client hotspot doesn’t exist, which it doesn’t if you only have a server JRE which is the case for the x64 version of it. Check that the folder ‘C:\Program Files\sqldeveloper\jdk\jre\bin\client’ exists.

Silverlight LOB Development Patterns

We're developing a Line of Business (LOB) Silverlight application which we are attempting to establish some good development patterns that allow us to create client side assemblies that follow good design practices, are easily testable and can be used by more than one type of client e.g. WPF and Win Forms. I'll try and blog about the experiences we have doing this.

One of the first requirements we have for creating our application is that it can be easily tested both via code and a user interface test framework. Code testing turns out to be fairly easy to do, and it appears that actual UI testing can be done but I'll talk about that when we've got it working.

We're taking a test driven development approach so getting testing via code is the first requirement we need to solve. We've found too approaches for testing Silverlight applications:

  • Using a Silverlight based nunit like testing framework

Tests are actually run within Silverlight itself and use custom attributes. Test assemblies cannot be run using the standard Visual Studio mechanism, instead run within the browser. http://code.msdn.microsoft.com/silverlightut

  • Using Standard Visual Studio Test Projects

Tests are written in normal test projects altered to work with the Silverlight assemblies that they are testing.

We've chosen to follow the second approach as it works well for developers using visual studio and more importantly with the build server when it performs continuous integration and overnight builds.

 

Sharing Silverlight Assemblies

Assemblies generated Silverlight applications can be referenced by standard assemblies outside the Silverlight runtime inside a normal Dot Net runtime. All that needs to be done is to remove all the System assembly references in the normal assemblies and replace them with the System assemblies from the Silverlight installation. This is very useful when coming to unit test Silverlight code as normal unit tests projects can be used. Care must be taken not to introduce relative paths to the referenced Silverlight assemblies, I've been unloading the project and editing the references after adding them directly in the .csproj files to take care of this.

Before editing the .csproj file after adding the Silverlight assembly references using Add Reference->Browse

<Reference Include="system, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\..\..\..\..\Program Files\Microsoft Silverlight\2.0.31005.0\system.dll</HintPath>
</Reference>
<Reference Include="System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\..\..\..\..\Program Files\Microsoft Silverlight\2.0.31005.0\System.Core.dll</HintPath>
</Reference>

The .csproj file should contain references like:

<Reference Include="system, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
  <HintPath>C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\system.dll</HintPath>
</Reference>
<Reference Include="System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
  <HintPath>C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\System.Core.dll</HintPath>
</Reference>

In theory all our client assemblies apart from ones containing the Silverlight views can be shared between WPF and Silverlight. The application has an assembly structure that separates out the behaviour of the client from its views using MVVM and MVP patterns. The idea is that the WPF client (and unit test assemblies) reference all the assemblies that do not contain Silverlight user controls sharing all the presenter and view model logic.

One potential concern of this approach is that it appears that the Silverlight assemblies are used directly by non Silverlight assemblies. I.e. a WPF application references the shared client assemblies, it then must reference the Silverlight assemblies. For types such as string being part of mscore this isn't a problem as the type gets translated to the correct mscore type used by the running framework (i.e. not the Silverlight one). For other types this translation doesn't occur, so a type such as XmlDocument will be running the IL contained in the Silverlight assemblies not the main framework ones. In effect this forces all client side assemblies regardless of the client type (i.e. WPF / Winform) to only reference Silverlight assemblies.

An alternative to this approach would be to perform a dual compile of the shared assemblies where Silverlight and standard assemblies are created. This is the approach that the Prism project appears to use (http://www.codeplex.com/CompositeWPF) for creating the Composite WPF/Silverlight toolkit, they might even have some tools to help with this. Its also pretty easy to do in Visual Studio by just creating two projects, keeping the code in one and adding the code via links in the other. We're going to take these approaches as the need arises though.

 

Testing

We've now got a testable Silverlight application, the non view UI code is in its own assemblies separate from the Silverlight application assembly. The non view UI code is now testable using the standard Visual Studio tools.

Future Investigation

I'm currently looking at how WCF services can be shared between Silverlight and other non silverlight assemblies, so should be able to blog about the solution we've found soon.

Other people in the team have been looking at using following areas:

  • Use of Prism http://www.codeplex.com/CompositeWPF
  • Defining styles for Silverlight controls (we've found issues defining application wide styles for controls outside the System.Windows assembly)
  • Creating a framework for modal Dialogs
Stop Using XmlNodeWriter or Memory Buffers for Creating XmlDocument's from XmlWriter's

XmlNodeWriter is an implementation of XmlWriter that is created with reference to an XmlDocument allowing the creation of an XmlDocument from code that expects a writer object. XmlNodeWriter was available as sample code via MSDN.

We had used this class and another memory buffer based mechanisms for generating XmlDocument's. It turns out that the framework does have its own mechanism for creating an XmlWriter for writing to an XmlDocument, via the XPathNavigator object created from the XmlDocument. The following code snippets demonstrate how this can be used.

 

The following code snippet shows how some of our code used a memory buffer for creating a dom via an XmlWriter, this is pretty inefficient and introduced bugs as shown below where the encoding was provided which couldn't handle non Unicode characters very well, replacing them with '?', and blew up with “” characters. It works better when you don't specify the encoding but its still inefficient, although I haven't tested if it still works with “” characters.

MemoryStream ms = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter( ms, Encoding.Default );
obj.WriteXml( writer );

// Load into a dom and return
writer.Flush();
ms.Position = 0;
XmlDocument dom = new XmlDocument();
dom.Load( ms );
return dom;

 

The implementation using the XmlNodeWriter sample code looks like:

XmlDocument doc = new XmlDocument();
obj.WriteXml(new XmlNodeWriter(doc, false));

 

The implementation using only framework classes is:

XmlDocument dom = new XmlDocument();
using (XmlWriter writer = dom.CreateNavigator().AppendChild())
{
   obj.WriteXml(writer);
}

 

It can also be used when your performing an XSLT:

XmlDocument outputDom = new XmlDocument();

using (XmlWriter writer = outputDom.CreateNavigator().AppendChild())
{
   XslCompiledTransform ct = new XslCompiledTransform();
   ct.Load(stylesheetUri);
   ct.Transform(inputDom, args, writer);
}

Compiling WIX projects via TFS Builds.

In order to get WIX projects compiling with the default setting for TFS builds a couple of things need to be changed.

 

One option is be to make the build target the x86 platform, but I quite often have issues with this. The other option is to alter the build definitions to force the Installer to build with the Any CPU target. This is achieved by changing the following:

 

1. In the configuration manager of Visual Studio. Ensure that the Installer project is selected to build for the Any CPU platform. By default this is unselected (as shown).

image 

 

2. Edit the Installer project definition so that it will always build under the Any CPU platform:

  • Remove the following line from the top of the definition:

    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>

  • Change the following property groups associated with the different configurations and platforms removing the platform conditions. E.g. change:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>

to:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
  <DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
  <OutputPath>bin\$(Configuration)\</OutputPath>
  <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>

The installer should now build with a TFS build targeting the Any CPU platform, the MSI appearing in the build output.

Use System.Web.DynamicData.MetaModel with ADO.NET Entities Outside a Web Application

The Dynamic Data set of technologies Microsoft have introduced to speed web site development are build on top a set of libraries that provide meta data about a database model. Lots of this information such as maximum field lengths can be very useful within your own code. Below is an example of how to access this data in order to perform some processing on it.

 

I add a property to the partial class we have for the entity context:

/// <summary>
/// Gets the context meta model which can be used for finding out the model
/// meta data, such as length of fields.
/// </summary>
/// <value>The context meta model.</value>
internal static MetaModel ContextMetaModel
{
    get
    {
        // there doesn't appear to be a way to test if a model has been registered
        // without an exception being thrown. The web application normally registers the context
        // when it starts, we need to ensure its registered when we are operating outside
        // the web application
        MetaModel model;
        try
        {
            model = MetaModel.GetModel(typeof(MyContextContainer));
        }
        catch (InvalidOperationException)
        {
            model = new MetaModel();
            model.RegisterContext(typeof(MyContextContainer),
                new ContextConfiguration() { ScaffoldAllTables = false });
        }
        return model;
    }
}       

      

For example to find the maximum length of a name field:

int maxLength = MyContextContainer.ContextMetaModel.GetTable("Customer").GetColumn("Name").MaxLength

 

Although I haven't tested this, I assume the above works just as well with LINQ for SQL contexts.

Ordering EntityCollection sets in the ADO.NET Entity Framework

Following on from a recent post regarding XML serialisation of ADO.NET Entity Framework one issue I've come across is getting entities within the XML in the correct order.

The main issue is that the EntityCollection<IEntityWithRelationships> type used to maintain references within the entity framework is that its un-ordered. This makes perfect sense of course as entities in a database have no reliable order.

Even though this makes sense its very annoying if you just want your entities to come out in a defined order within an object structure and the any XML serialised from it. You could of course order the XML once it has been serialised, either via a transform, or in code, but having data contract references makes this pretty difficult.

I've come up with a pretty hacky mechanism that forces the order that objects are returned via the EntityCollection Enumerators. The solution uses reflection and won't be guaranteed to work in future versions of the framework. Essentially it finds the HashSet object used by EntityCollection to store its values and re-orders the data structure that the HashSet loops through in its enumerator.

Once a sort order has been applied to the entity collection the only code that runs against the collection must only work via the hashsets enumerator. I.e. use a context object solely to get all the objects for the re-order process and only ever read from the collections that have been re-ordered after that. Any attempt to add or remove items will cause the HashSet object to blow up as its underlying data structures have been messed with.

The code is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using System.Collections;
using System.Reflection;

namespace Examples

{
    public static class EntityUtil
    {
        /// <summary>
        /// Reorders the entities in an entity collection by the keySelector function.
        /// </summary>
        /// <remarks>
        /// This class uses reflection to re-write the slots in the underlying hashset used by the entity
        /// collection to contain the references.
        ///
        /// The only action that should be taken on the entity collection after this method has been called
        /// is via its enumerators, as the hash set is not left in a consistant state.
        /// </remarks>
        /// <typeparam name="TSource">The type of the entity objects.</typeparam>
        /// <typeparam name="TKey">The type of the key to order by.</typeparam>
        /// <param name="source">The entity collection source to re-order.</param>
        /// <param name="keySelector">The key selector function that returns the key to order by.</param>
        public static void ReorderEntities<TSource, TKey>(
            this EntityCollection<TSource> source, Func<TSource, TKey> keySelector)
            where TSource : class, IEntityWithRelationships
        {
            HashSet<TSource> relatedEntities = (HashSet<TSource>)
                typeof(EntityCollection<TSource>).GetProperty("RelatedEntities", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(source, null);
            // re-order the sets slots to fix the ordering, (but mess up everything else probably)
            Type slotType = typeof(HashSet<TSource>).Assembly.GetType("System.Collections.Generic.HashSet`1+Slot").MakeGenericType(new Type[] { typeof(TSource) });

            FieldInfo slotField = typeof(HashSet<TSource>).GetField("m_slots", BindingFlags.Instance | BindingFlags.NonPublic);
            FieldInfo lastIndex = typeof(HashSet<TSource>).GetField("m_lastIndex", BindingFlags.Instance | BindingFlags.NonPublic);
            System.Array slots = Array.CreateInstance(slotType, relatedEntities.Count);
            int index = 0;
            foreach (TSource obj in new List<TSource>(relatedEntities).OrderBy(keySelector))
            {
                object test = Activator.CreateInstance(slotType);
                slotType.GetField("hashCode", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(test, obj.GetHashCode());
                slotType.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(test, obj);
                slotType.GetField("next", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(test, -1);
                slots.SetValue(test, index++);
            }
            slotField.SetValue(relatedEntities, slots);
            lastIndex.SetValue(relatedEntities, slots.Length);
        }
    }

}

 

The reflection code itself is quite interesting as it find and constructs an inner type (Slot) of a generic type. The inner type is a struct so the Activator class is used as struct's don't have a constructor to invoke.

An example use of the code is:

Customer customer;
using (ContextContainer context = new ContextContainer())
{
    // cause a load of static data types first
    customer = context.Customers
        .Include("Addresses")
        .Include("Addresses.AddressLines")
        .First(e => e.CustomerId == customerId);
}

// reorder the structure in memory
customer.Addresses.ReorderEntities(e => e.Order);

foreach (Address a in customer.Addresses)
{
    a.AddressLines.ReorderEntities(e => e.Order);

}

 

The structure will now have the correct serialisation order. FYI an example xslt snippet that works correctly with ordered referenced entites is:

<xsl:for-each select="do:Addresses/do:Address">
  <xsl:apply-templates select="self::node()[@z:Id]"/>
  <xsl:for-each select="self::node()[@z:Ref]">
    <xsl:variable name ="ref" select="@z:Ref"/>
    <xsl:apply-templates  select="//do:Address[@z:Id = $ref]"/>
  </xsl:for-each>
</xsl:for-each>

Data Authorisation with ADO.NET Entity Framework

An application I'm writing has a requirement to only allow authorised users access to particular areas of data, generally referred to as 'Data Authorisation'. Generally when restricting access to data it's a good idea to create a framework that applies authorisation at the layer that the rest of the application uses, i.e. all data access is protected, stopping coding mistakes made at the higher layers exposing unauthorised data.

The application is a typical data entry web site using ADO.NET entities so I've decided to apply data authorisation to data in the ADO.NET Entity classes. Whilst trying to do this it turns out that Entities doesn't appear to allow a fully featured mechanism to prevent data access. For example, you cannot add the ability to restrict queries from returning un-authorised data.

The web site has many EntityDataSource's that generally load data via the entity set name plus a few where clauses. As we cannot generically alter the data returned from these queries I found that if you want to keep on using EntityDataSource's you will need to add joins to the tables containing the access control lists (ACL's) for an entity at the web tier. This isn't ideal but the application is mainly data entry orientated so having the web tier close to the data tier isn't too bad.

What I have found you can do though is to throw exceptions when un-authorised data has been loaded from the database. Although a developer has to know about the access control tables when writing queries, if they get things wrong exceptions will be thrown, highlighting any incorrect code. I.e. even though the web tier defines the join's to the access control tables, if it gets it wrong then the data tier throws exceptions. In addition pages that allow editing of a single entity that load the entity via a query string are protected as the underlying data layer will throw an exception if incorrect data is loaded via the manipulation of the query string.

The application then uses three main mechanisms for authorising data access:

  • All queries are written to join to the access control tables when they need to display lists filtered by ACL's.
  • All entity objects requiring data authorisation have methods added in partial files to check the ACL's when those entities are loaded from the database.
  • When the context saves changes checks are performed to ensure the user is authorised to perform those changes.

 

The code added to each entity to check access control lists is in general as below. It assumes that each entity has a single identifier, in this case called EntityTypeId (we name our identifies in relation to the type name, for example CustomerId, AddressId...). The code below is invoked whenever an entity is constructed from the database, it makes an assumption that the id is never set when creating new objects (our identifiers are auto generated).

/// <summary>
/// An entity identifier is changed when the object is materialised
/// from the database. Therefore check for permissions here.
/// </summary>
/// <param name="value">The id being changed</param>
partial void OnEntityTypeIdChanging(long value)
{
    if (this.EntityState == System.Data.EntityState.Detached)
    {
        DataAccessControl.AssertEntityTypeAccess(value);
    }
    else
    {
        // all other access potentially alters the acl's so re-cache
        DataAccessControl.AccessCache.ClearThreadAssociations();
    }
}

In my implementation the DataAccessControl class maintains a thread local cache of id's that can be accessed, this will be loaded on each web request and cleared down at the end of a request, or when a different user identity is found. Alternative mechanisms can be used though.

 

We can monitor when a context has it changes saved through the following code added to the partial class for the context:

partial void OnContextCreated()
{
    this.SavingChanges += new EventHandler(ContextContainer_SavingChanges);
}

void ContextContainer_SavingChanges(object sender, EventArgs e)
{
        List<ObjectStateEntry> checks =
            new List<ObjectStateEntry>(ObjectStateManager.GetObjectStateEntries(EntityState.Added));
        checks.AddRange(ObjectStateManager.GetObjectStateEntries(EntityState.Modified));

        PerformAccessChecks(checks);
}

The above code just gathers up all the changes and ensures that the user has access to objects being added or modified (deleted could be added to if required).

 

In general a generic data access authorisation mechanism can be added to ADO.NET Entities by performing the following:

  • Perform an access check when the entities primary key is changed, when the entity is detached via the partial OnChanging methods.
  • Perform an access check to all entities when being saved by listing to the SavingChanges event on the context.
Asp.Net HttpSessionState in Microsoft Unit Tests Framework

We have some unit tests that tests code which relies on a variable stored in the Asp.Net session state (HttpSessionState) accessed from the HttpContext.Current.Session property.

Our unit tests run within the Microsoft Unit Test framework that comes with Visual Studio, but I think that the technique should apply to other frameworks such as NUnit.

Without doing any work HttpContext.Current returns null. It appears quite easy to make set a current HttpContext to contain a session:

HttpContext.Current = new HttpContext(new HttpRequest("", "http://localhost/", ""), new HttpResponse(new System.IO.StringWriter()));

Now HttpContext.Current returns a context, but HttpContext.Current.Session still returns null. You can attach a basic session object with the following line:

System.Web.SessionState.SessionStateUtility.AddHttpSessionStateToContext(
 HttpContext.Current, new HttpSessionStateContainer("",
  new SessionStateItemCollection(),
  new HttpStaticObjectsCollection(), 20000, true,
  HttpCookieMode.UseCookies, SessionStateMode.Off, false));

Now HttpContext.Current.Session can be assigned to and read from allowing my unit tests to run.

I think the code is best put in the a method marked with a [TestInitialize] attribute. The context should also be cleared when a test has completed:

[TestCleanup]
public void Clean()
{
 HttpContext.Current = null;
}

I'm not expecting the HttpContext to work well for any other calls, but it might allow more than just interaction with the session.