How to create 2 versions of an EF Odata service?

The Problem

I Like the Odata support that you get from the EF provider. It’s easy to set up and it supports updating write out of the box. To good to be true? Well, yes. Sort of any way. It’s easy to get WOWed by all the helloworld (and a bit more complex) examples you can find on the web. There are some disadvantes (in my opinion) to the procedure.

In particular the tight coupling to the data model / context is a problem to me.Why? Well what if you have constructed an Odata services and you need to change it? To change the structure of the service you need to change the database. If the service is uses you can not do that at once.

What do you do when you need to create a new version of this nice Odata service? I have searched the web for the answer to this question, but I did not find it. There is advice on when you schould create a new version of your service, how to call it but never how to actually do it. Since I had a business requirement to provide a new version of my web service I tried to crack this nut on my own…

The options

There are many ways to skin a goat. And there are many ways I tried to create a second version of my Odata service. Here’s a summary of my attempts:

  1. Create an extra abstraction using the reflection provider. I was thinking of a setup like this:
    1. DB -> EF datacontext -> Reflection datacontext version 1 -> WCF Odata service
    2. DB -> EF datacontext -> Reflection datacontext version 2 -> WCF Odata service
    3. I wanted to use the reflecion provider to only expose what was agreed on for each version of the service and the EF context to provide acces to my latest and greatest datamodel. Unfortunatly I could not get it to work. The problems:
      1.  Multiple versions of the same class (altough in different namespaces of course) where confusing EF.
      2. Some linq operations to transform the schape of my service wheren’t support by linq to sql.
      3. Altough I liked the abstrion I did not like the fact that I lost all update facilities and would now have to provide this code myself (did not try that because i and ii made read-only already impossible).
  2. Create a new application with the new version.
    1. This would work but require a second database (not feasable for me) or at least a EF context that operated on a view (probably possible but I am not an experienced sql server administrator and decided to come back to this option if nesecary.
  3. Create two EF datacontext that targeted the same database (a bit a like a code version of a view).
    1. I Liked this idea because it migt mean I will not have to come up with code to handle updates, have just one database and will not have to learn new sql server “tricks”. The good news is I got this scenario to work. The bad news? lots of things whent wrong, hence this post 🙂

A Solution

For starters, lets make clear what my goal was: I needed to versions of an Odata service to give my users a time frame to transiton from the old version to the new:

  • //myApplication.MyDomain.something/Service/v1/MyOdataService.svc : provides access to the current version of my service
  • //myApplication.MyDomain.something/Service/v2/MyOdataService.svc : provide access to the next version of my service.

Each wcf service will get it’s own data context. This data context will be mapped to dto classes that represent the entities that will be used in the service. The mapping to these object will not by automatic. I will code them using the fluent api to ensure the right tables and fields are mapped to my objects.

One extra data context will be used to provide acces to the latest version of the database. This context will be used to generate database updates and provide access to the data from a mvc website.

There is a problem with this setup. Some classes are represented three times (v1, v2 and “current” and this confuses entity framework because namespaces are ignored by the db context. It will generate errors complaining that it doesn’t now how to choose between the class definitions. Microsoft claims this behaviour is intendend to make it more easy for developers to use EF to get started. To me a maintainable solutions seems more importand then an easy start but I might be missing the clue here…

After reading about a “loadFromAssembly” method on the MetaDataWorkspace property of System.Data.Entity I got hope that using different assemblies might help acommodating my scenario of three dbcontexts over the same set of db tables. And it did!!

The way I solved the problem is by creating three seperate projects (and assemblies):

  • MyMvcWebSite

This projects containts the website and the current version of my object model. The datacontext is used to migrate the database and server data to my website only.

I also has a folder named “Services”. This folder contains the dataservice class for each version of my webservice. But ONLY the class itself and NOT the db context. It now has two classes:

  1. MyOdataServiceV1.svc.cs : this class references the db context of version 1
  2. MyOdataServiceV2.svc.cs : this class references the db context of version 2

Apart from these two classes theres only one (but vital) change to be made for each (new) version of the service:

In the global.asax, during the Application_Start() I have added the following line to prevent the service data context from medeling (updating) the database and form verifing the consistency between the database and the model (there not consitent of course):

Database.SetInitializer<ServiceV1.MyDbContext>(null);

Database.SetInitializer<ServiceV2.MyDbContext>(null);

Probably there is a Database.SetInitializer(MyRealDbContextInitializer) as well. If it’s in another place, that place will be suitable as well.

  • ServiceV1

This class libary containts a reference to EF, the defintion of the dto’s of the version 1 service a datacontext (ServiceV1.MyDbContext) and the mapping from the db model to the service model. Because the same technique and conventions are used very little mapping is required. I needed to map a renamed column, but other then that the conventions worked nicely for me.

  • ServiceV2

This class library is identical to the ServiceV1, but with slightly different dto’s and a sligtly diffrent mapping.

Does it work?

It certenly seems like it. We are still in test but we will probably go live with this version soon. If it proves to be stable on this one service we will apply it to others as well. The scary thing is that I have not found Microsoft documentation describing this behaviour (only searching voor classes with the “right” name in the assembly of the dbContext or a referenced one) so an new version of EF/WCF might spoil the fun…

A Question?

If you have any experience serving more then one version of a ODATA/EF/WCF feed and think my way of dealing with it is bad or you just know an even better one. Please let me know.

Geplaatst in .Net | Tags: | Een reactie plaatsen

SQLite draait niet op AnyCpu platform

Je kunt kiezen voor x86 (32 bits) of x64 maar dus niet voor AnyCpu. Omdat ik SQLite alleen voor mijn unit test projecten gebruik is dit niet zo erg maar het is wel belangrijk om te weten dat je met SQLite dus geen applicaties kunt bouwen met AnyCpu als build target.

Geplaatst in .Net | Een reactie plaatsen

Wcf webservices draaien op je ontwikkel machine

Windows 7 (en de andere versies ook) is niet in staat om een wcf service te hosten zonder dat je hier expliciet toestemming voor geeft. Je krijgt een foutmelding die er ongeveer zo uit ziet:

SetUp : System.ServiceModel.AddressAccessDeniedException : HTTP could not register URL http://+:8732/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details).
—-> System.Net.HttpListenerException : Access is denied

Dat toestemming verlenen doe je vanaf de commandline met het volgende commando (voorbeeld):

netsh http add urlacl url=http://+:8732/ user=domain\user

Als het lukt is dit de reactie:

URL reservation successfully added

Geplaatst in .Net | Een reactie plaatsen

Terug van weggeweest

Ik heb een tijd niet meer op mijn blog geschreven. Maar ben van plan dat toch weer een beetje op te pakken. Heb inmiddels een nieuwe pc en moet dus “alles” opnieuw instellen en testen Bedroefde emoticon

Geplaatst in Geen categorie | Een reactie plaatsen

What if clearTimeout causes the timeout function not to fire?

Today I had a problem with the JavaScript setTimeOut function. I was trying to validate the correct execution of some code that was executed on a regular interval like this:

setInterval(function () {
            var to = window.setTimeout("alert('no (timely) response');", 100);
            alert("get data from server and update page");
            clearTimeout(to);
			}, 1000);

My expectation was to see an alert when the “update” took over 100 ms, but… nothing happened. I think what happens is this:

After the alert-box is dismissed clearTimeout gets called even when a time out has occurred. Apparently the time-out function will not executed until the JavaScript run time is in some kind of “wait” or “pause” mode and thus has time to handle the event. When it finally does, the timeout is already cleared and nothing happens.

My explanation might be wrong, but the following code does work:

setInterval(function () {
            if (to != null)
			{
			   clearTimeout(to);
			}
            to = window.setTimeout("alert('no (timely) response');", 100);
            
			alert("get data from server and update page");
            
			}, 1000);

By calling clearTimeout(to) at the beginning of the interval, the JavaScript runtime does something like this:

  1. start interval
  2. clear timeout if present
  3. set timeout
  4. do stuff
  5. * wait *
  6. goto 6  Smile

I think that during step 5 the run time handles all pending events, including my timeout.

Geplaatst in JavaScript | Tags: | Een reactie plaatsen

StructureMap problem when using MVC2 AND MVC3

After updating structureMap on a web project from version 2.6.1.0 tot version 2.6.2.o, I got the following error:

Server Error in '/' Application.
--------------------------------------------------------------------------------

StructureMap configuration failures:
Error:  170
Source:  Registry:  StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.2.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
Unable to find the exported Type's in assembly Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null.  One or more of the assembly's dependencies may be missing.

Method 'GetControllerSessionBehavior' in type 'Microsoft.Web.Mvc.MvcDynamicSessionControllerFactory' from assembly 'Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
System.TypeLoadException: Method 'GetControllerSessionBehavior' in type 'Microsoft.Web.Mvc.MvcDynamicSessionControllerFactory' from assembly 'Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
  at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes)
  at System.Reflection.RuntimeAssembly.GetExportedTypes()
  at StructureMap.Graph.TypePool.<>c__DisplayClass2.<.ctor>b__0(Assembly assembly) in c:\code\structuremap\Source\StructureMap\Graph\AssemblyScanner.cs:line 24


-----------------------------------------------------------------------------------------------------



Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: StructureMap.Exceptions.StructureMapConfigurationException: StructureMap configuration failures:
Error:  170
Source:  Registry:  StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.2.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
Unable to find the exported Type's in assembly Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null.  One or more of the assembly's dependencies may be missing.

Method 'GetControllerSessionBehavior' in type 'Microsoft.Web.Mvc.MvcDynamicSessionControllerFactory' from assembly 'Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
System.TypeLoadException: Method 'GetControllerSessionBehavior' in type 'Microsoft.Web.Mvc.MvcDynamicSessionControllerFactory' from assembly 'Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
  at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes)
  at System.Reflection.RuntimeAssembly.GetExportedTypes()
  at StructureMap.Graph.TypePool.<>c__DisplayClass2.<.ctor>b__0(Assembly assembly) in c:\code\structuremap\Source\StructureMap\Graph\AssemblyScanner.cs:line 24


-----------------------------------------------------------------------------------------------------




Source Error: 


Line 97:             */
Line 98: 
Line 99:             Bootstrapper
Line 100:                .Including
Line 101:                .Assembly(Assembly.GetExecutingAssembly())
 

Source File: C:\work\Intranet-hg\src\Intranet\Global.asax.cs    Line: 99 

Stack Trace: 


[StructureMapConfigurationException: StructureMap configuration failures:
Error:  170
Source:  Registry:  StructureMap.Configuration.DSL.Registry, StructureMap, Version=2.6.2.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
Unable to find the exported Type's in assembly Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null.  One or more of the assembly's dependencies may be missing.

Method 'GetControllerSessionBehavior' in type 'Microsoft.Web.Mvc.MvcDynamicSessionControllerFactory' from assembly 'Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
System.TypeLoadException: Method 'GetControllerSessionBehavior' in type 'Microsoft.Web.Mvc.MvcDynamicSessionControllerFactory' from assembly 'Microsoft.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
   at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes)
   at System.Reflection.RuntimeAssembly.GetExportedTypes()
   at StructureMap.Graph.TypePool.<>c__DisplayClass2.<.ctor>b__0(Assembly assembly) in c:\code\structuremap\Source\StructureMap\Graph\AssemblyScanner.cs:line 24


-----------------------------------------------------------------------------------------------------


]
   StructureMap.Diagnostics.GraphLog.AssertFailures() in c:\code\structuremap\Source\StructureMap\Diagnostics\GraphLog.cs:68
   StructureMap.Container.Configure(Action`1 configure) in c:\code\structuremap\Source\StructureMap\Container.cs:327
   Bootstrap.StructureMap.StructureMapExtension.RegisterAll() +117
   Bootstrap.StructureMap.StructureMapExtension.RegisterImplementationsOfIRegistration() +96
   Bootstrap.Extensions.Containers.BootstrapperContainerExtension.InitializeRegistrations() +31
   Bootstrap.Extensions.Containers.BootstrapperContainerExtension.Run() +40
   Bootstrap.Bootstrapper.Start() +114
   Bootstrap.Extensions.BootstrapperExtensionOptions.Start() +26
   Intranet.MvcApplication.RegisterDependencyResolver() in C:\work\Intranet-hg\src\Intranet\Global.asax.cs:99
   Intranet.MvcApplication.Application_Start() in C:\work\Intranet-hg\src\Intranet\Global.asax.cs:61

 


--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.237 

And arrived in some kind of dll-hell. It was very hard to fix this problem. I have a project that was converted from mvc2 to mvc3. New pages are being developed using mvc3/razor but existing ones are left as they are (for the moment). All was well until I upgraded structureMap using Nuget to version 2.6.2.0. The project stopped working on my development machine.

It turns out StructureMap now scans the Microsoft.Web.Mvc assembly and has a problem with it. By telling StructureMap not to do that my site resumed business as usual. I’m using bootstrapper to set things up in global.asax and the code looks like this:

Bootstrapper
                .Including
                .Assembly(Assembly.GetExecutingAssembly())
                //.Excluding
                //.Assembly("Microsoft.Web.Mvc")
                .With
                .StructureMap()
                .Start();

Without the comments all’s well. With them I can not get it to work. I’m relativly new to .Net and Asp.net (almost a year) so I might well be missing some things and cutting some corners but I assume there is some conflict between mvc2 and mvc3 that is not relevant to my application but confuses StructureMap somehow.

Geplaatst in .Net, ASP.NET MVC | Tags: | Een reactie plaatsen

SQLite

Wanneer je Unit Test wilt schrijven voor software die van een naar de database schrijft, dan is dat om 2 redenen lastig: Je moet ervoor zorgen dat voor elke test de inhoud van de database eenduidig vastligt én een manier bedenken om een flink aantal tests uit te kunnen voeren binnen een redelijke termijn.

Het eerste probleem is redelijk goed op te lossen door de database bij de aanvang van elke test(serie) opnieuw te genereren en te vullen met testdata. Wanneer je MsSql CE gebruikt kan dit naar een “gewoon” bestand en heb je geen last van / belast je de database server niet.

Helaas waren de Unit test zo traag dat ik de DB tests uit de integratie build heb moeten halen om ervoor te zorgen dat ik nog wel een redelijk snelle reactie krijg na het pushen van nieuwe sources. De DB tests heb ik in een apart (NUnit) project gezet dat 1 keer per nacht wordt uitgevoerd.

Op zich was ik over deze werkwijze wel tevreden. Totdat ik over de (open source) database SQLite struikelde. Er is een .Net versie System.Data.SQLite beschikbaar en het is ook als NuGet package te downloaden en installeren in je project!

SQLite kan worden geconfigureerd als SQL2005 provider én is in staat om volledig in memory te werken! Dus supersnel op te bouwen en te vullen. Ideaal voor mijn Unit tests. Inrichten is natuurlijk lastig maar gelukkig weet ome Google raad. De volgende Nhibernate configuratie werkt voor mij:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory name="NhUnitTests.TestFactory">
    <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
    
    <!--
    SQL CE Connectie naar file:
    <property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property>
    <property name="connection.driver_class">NHibernate.Driver.SqlServerCeDriver</property>
    <property name="connection.connection_string">Data Source=.\UnitTestDb.sdf</property>
    
    SQL Express 2008 R2 connectie naar server
    <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">Data Source=<myserver>\sqlexpress;Initial Catalog=<mydb>;Integrated Security=True</property>
    -->

    <!-- SQLite connectie naar in memory DB (files en servers worden ook ondersteund hoor!) -->
    <property name="dialect">NHibernate.Dialect.SQLiteDialect</property>
    <property name="connection.driver_class">NHibernate.Driver.SQLite20Driver</property>
    <property name="connection.connection_string">Data Source=:memory:;Version=3;New=True;Pooling=True;Max Pool Size=1;</property>
    <property name="connection.release_mode">on_close</property>
    <property name="adonet.batch_size">10</property>
    <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
    <property name="command_timeout">60</property>
    <property name="show_sql">true</property>
  </session-factory>
</hibernate-configuration>

Ik weet (nog) niet wat alle instellingen en heb gewoon stukjes uit blogs en forums gevist tot het werkte. Als het nodig is ga ik zeker meer tijd besteden aan die instelling. Voor nu ben ik echter dik tevreden Smile

Ik heb de property connection.release_mode toegevoegd aan de configuratie file, zoals beschreven in de Nhibernate documentatie. Hopelijk helpt dit om mijn Unit test stabieler te maken. Nu lijkt het soms niet te lukken een nieuw database schema te genereren, waardoor de betreffende test mislukt.

Geplaatst in Tools | Tags: | Een reactie plaatsen