Eben ging mein Gated Checkin noch und jetzt nicht mehr. Der Fehler ist wie immer wenig hilfreich:
System.InvalidOperationException: The Entity Framework provider type ‚System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089‘ for the ‚System.Data.SqlClient‘ ADO.NET provider could not be loaded.
Die Lösung ist eigentlich recht leicht nur eben mit ein paar Tricksereien im Umfeld von Unit Tests auf dem Server verbunden.
Screencast
Informationen
Zu diesem Thema wurde bereits so einiges gepostet. Beispielsweise:
- http://stackoverflow.com/questions/14695163/cant-find-system-data-entity-sqlserver-sqlproviderservices
- http://stackoverflow.com/questions/14033193/entity-framework-provider-type-could-not-be-loaded?rq=1
Das Problem ist nun, dass hier einige ganz nette Erklärungen und Lösungen gebracht werden, keine davon jedoch wirklich auf mein Problem anwendbar war. Aber eins nach dem anderen.
Schritte zum Problem
Ich habe eine bereits länger entwickeltes Solution. Hier habe ich ein Projekt, das per EF auf eine SQL Server Datenbank zugreift. Die Verweise werden per nuget verwaltet. Seit einiger Zeit folgt daraus nun, dass man immer 2 DLLs liefern muss, damit der Zugriff klappt:
- EntityFramework.dll
- EntityFramework.SqlServer.dll
Sowei so gut. Mein Unit-Test-Projekt sah also bis vor kurzem ca. so aus, dass auch dieses ein Nuget-Package auf EntityFramework hatte und alles war gut.
Dann kam ich auf die Idee, datengetriebene Tests zu integrieren. Hier ein Beispiel:
[TestClass] public class DateTimeExtensionsTest : DataDrivenTestBase { #region methods /// <summary> /// Tests the correctness of the extension-method <see cref="DateTimeExtensions.BeginOfDay"/>. /// </summary> [TestMethod] [DeploymentItem(@"Data Sources\DateTimeExtensionsTest_BeginOfDay.csv")] [DataSource(@"Microsoft.VisualStudio.TestTools.DataSource.CSV", "DateTimeExtensionsTest_BeginOfDay.csv", "DateTimeExtensionsTest_BeginOfDay#csv", DataAccessMethod.Sequential)] public void BeginOfDayTest() { // Arange var input = Convert.ToDateTime(TestContext.DataRow[0]); var expected = Convert.ToDateTime(TestContext.DataRow[1]); // Act var result = input.BeginOfDay(); // Assert Assert.AreEqual(expected, result); } #endregion }
Also ein relativ simples kleines Ding. Nun habe ich fröhlich einen Rechtsklick rein gemacht und „Run Test“ gewählt. Das Ergebnis ist toll:
Weil ich mein Visual Studio ja nun bereits eine Weile kenne, wähle ich „Run All“ im Test Explorer aus. Ergebnis:
Also nur noch mal zur Verdeutlichung: An den Tests, die nun fehlschlagen habe ich nichts verändert! Kommentiere ich Listing 1 aus, geht wieder alles.
Lösung
Das Problem scheint zu sein, dass in dem Szenario, in dem ich einen datengetriebenen Test starte, das Framework die zweite Dll EntityFramework.SqlServer.dll „wegrationalisiert“. Ich habe keine Ahnung, warum, aber ich weiß, wie man das verhindern kann.
Ich lasse alle meine Tests schon immer von einer abstrakten Klasse „TestBase“ erben. Das hat den Vorteil, dass ich bestimmte Dinge zentral für alle Tests auf einen Schlag ändern kann. In Listing 1 ist die Basisklasse „DataDrivenTestBase“, die aber selbst auch wieder von TestBase erbt. In der TestBase mache ich nun nichts weiter, als:
public abstract class TestBase { #region methods /// <summary> /// Is called by the testing framework before a test runs. /// </summary> [TestInitialize] public void Init() { // Handle an error in EF 6. var instance = System.Data.Entity.SqlServer.SqlProviderServices.Instance; } #endregion }
Wiederhole ich nun mein „Run All“ im Test Explorer, funktioniert alles wieder einwandfrei!
Man sieht in Listing 2 recht gut, dass einfach nur ein Typ aus der Assembly System.Data.Entity.SqlServer benutzt wird. Allein das Benutzen scheint auszureichen, damit der Testrunner versteht, dass er diese Assembly nicht „weglassen“ darf.
Lösung, die nicht reicht
An einigen Stellen wird vorgeschlagen, folgendes zu nutzen:
var instance = typeof(System.Data.Entity.SqlServer.SqlProviderServices)
Testet man mit dieser Variante lokal ist alles fein. Bei mir kam aber der oben beschriebene Fehler immer noch, wenn der Build auf dem Build-Server durchgeführt werden musste. Man sollte also bis auf weiteres die Variante aus Listing 2 nehmen.
Fazit
In letzter Zeit häufen sich die kleinen aber lästigen Fehlerchen in den Toolings von MS. Ich habe den Eindruck, dass die getrennte und damit häufigere Verteilung per Nuget und VS Gallery nicht eben der Stabilität zu Gute gekommen sind. Ich habe mit diesem Kram gerade 2 Stunden Arbeit versenkt. Wenn sich das noch mehr häuft, dann frisst sowas meine Produktivitätsgewinne durch VS + ALM aber ganz schnell wieder auf :-(.
Nerviges Problem und so ganz kann ich es auch nicht nachvollziehen. Genau das war auch mein Weg.
Wohl war!