Zur Zeit wird gefiltert nach: murphy
Filter zurücksetzen
Entity Framework 4.3 - Building Blocks oder wie steuer ich die Datenbankversion
Gelegentlich kommt es vor, dass die Datenbankversionen pro Umgebung unterschiedlich sein können. Bei meinen ersten versuchen ist mir das mit den SQL-Server Versionen 2005 und 2008 so ergangen.
Sofort ersichtlich wurde das, wenn die folgende Fehlermeldung im Log zu sehen war:
System.ArgumentException: The version of SQL Server in use does not support datatype 'datetime2'
Um dies in den Griff zu bekommen gibt es das Attribut "ProviderManifestToken" in der EDMX-Datei. Mit dem ERM-Ansatz lassen sich, neben unterschiedlen Datenbanken, auch unterschiedliche Versionen einer Datenbank verwalten und mappen. Ein sehr interessanter Ansatz bei der Realisierung von Standardsoftware.
Nun stellt sich die Frage, wie mache ich das in Code First mit Schema Migrations?
Der Schlüssel liegt dabei in der Klasse DbModelBuilder. Beim erstellen kann damit die Datenbankversion definiert werden.
// default
var model = builder.Build(connection);
// with version
var model = builder.Build(new DbProviderInfo("System.Data.SqlClient", "2005"));
Gerade mit der nächsten Version von SQL Server 2012 wird es sicherlich ähnliche Probleme geben können, wenn die Systemumgebungen trotz ITIL nicht ganz so synchron sind. Bei der erstmaligen Erstellung des Models wird das sicherlich nicht von Bedeutung sein, wohl aber bei Schema Migrations.
Im Blogbeitrag von Arthur Vickers finden sich noch andere interessante Punkte die interessant sind, so auch die Aussage das Pluggable Conventions für den Durchschnittsentwickler frühstens mit EF6 nutzbar werden, wenn überhaupt.
Weitere Informationen zum Thema:
Sharepoint Deployment API zwischen Version 2007 und 2010
Meine Migrationsstory geht weiter. Nachdem der Export vorlag, stand ich das erste Mal an, weil mich bei einem Testimport die Fehlermeldung:
The version of the package 12.0.10.0 is different from the current version this program supports, 14.0.0.0
begrüsste. Nach ein bisschen suchen fand ich einen Ansatz. Die Package Files, speziell die SystemData.xml muss angepasst werden, damit sich der Inhalt aus Sharepoint 2007 in Sharepoint 2010 importieren lässt.
In der XML-Datei muss dazu auf SchemaVersion-Ebene die Version, dass aktuelle Patchlevel (Build) und die Datenbankversion (DatabaseVersion) angegeben werden. Nachfolgendes XML zeigt einen Ausschnitt aus der SystemData.xml:
<?xml version="1.0" encoding="utf-8"?>
<SystemData xmlns="urn:deployment-systemdata-schema">
<SchemaVersion Version="14.0.0.0" Build="14.0.6109.5002" DatabaseVersion="133739" SiteVersion="0" ObjectsProcessed="27" />
<ManifestFiles>
<ManifestFile Name="Manifest.xml" />
</ManifestFiles>
<SystemObjects>
...
</SystemObjects>
<RootWebOnlyLists />
</SystemData>
Müssten die Daten nicht noch bereinigt und transformiert werden, wäre ich bereits am Ende. Nun geht es daran, die XML-Dateien so aufzubereiten, damit die Daten in das Format der neuen Lösung des Zielsystems importiert werden können.
ETL geht auch einfacher, aber nicht unbedingt wenn Sharepoint im Spiel ist. ;-)
Weitere Informationen zum Thema:
Sharepoint 2010 Best Practices
Sharepoint gehört immer noch zu meinen Aufgabengebieten. Gute Anleitungen zu finden ist auch nicht einfach, gerade im Bezug was man tun oder lassen sollte.
Das Produktteam hat eine Übersicht mit Best Practices unter dem Link bereitgestellt.
Mal sehen, ob es brauchbar ist...
Weitere Informationen zum Thema:
Entity Framework 4 - Der Designer stottert bei zusammengesetzten Schlüsseln (CompositeKey)
Heute wurde ich mit einem interessanten Problem im Entity Designer bei zusammengesetzten Schlüsseln konfrontiert. Das Modell wurde mit dem Assistenten angelegt. Dabei war die Einstellungen "Fremdschlüsselspalten in das Modell einbeziehen" aktiviert und es wurde die Standard-Code Generierungsvorlage verwendet.
Die passende Fehlermeldung dazu lautet:
Die Personen.Test.CommentTest.Comment_Insert_Test-Testmethode hat eine Ausnahme ausgelöst: System.Data.UpdateException: EntitySet 'Comment' kann nicht aktualisiert werden, denn es hat eine DefiningQuery, und im <ModificationFunctionMapping>-Element ist kein <InsertFunction>-Element zur Unterstützung des aktuellen Vorgangs vorhanden.
Im ersten Moment beginnt das überlegen, DefiningQuery? Warum legt der Designer ein DefiningQuery für diese Tabelle an? In diesem Fall kann der Insert-Mechanismus gar nicht funktionieren, weil keine Prozeduren vorhanden sind. Ein Blick in das XML (Öffnen mit XML Text Editor) bestätigt auch die Fehlermeldung:
<edmx:StorageModels>
<Schema Namespace="Model.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="ModelStoreContainer">
<EntitySet Name="Comment" EntityType="Model.Store.Comment" store:Type="Tables" store:Schema="dbo" store:Name="Comment">
<DefiningQuery>SELECT
[Comment].[PersonID] AS [PersonID],
[Comment].[CommentTypeID] AS [CommentTypeID],
[Comment].[Note] AS [CO_Note]
FROM [dbo].[Comment] AS [Comment]</DefiningQuery>
</EntitySet>
Prozeduren will ich für dieses Szenario auf keinen Fall generieren, also bleibt mir nichts anderes übrig, als das XML anzupassen. Dazu gehe ich folgendermassen vor:
- DefinigQuery-Tag mit Inhalt entfernen
- store:Name=“Comment“ entfernen
- store:Schema=“dbo“ ändern in Schema=“dbo“
- XML-Datei speichern
Ist das Ganze angepasst, bleibt folgendes XML übrig:
<EntitySet Name="Comment" EntityType="Model.Store.Comment" store:Type="Tables" Schema="dbo" >
</EntitySet>
Daraus lässt sich schliessen, dass auch im Entity Framework 4 ein wenig Basiswissen zum Aufbau der XML-Datei nicht schaden kann. Den Nachteil dieser Variante will ich auch nicht vorenthalten. Mit jeder Modell-Anpassung müssen diese Schritte wiederholt werden.
Dieses Problem kann bei der codezentrierten Anwendungsentwicklung mit dem DbContext sicherlich auch entstehen, da der DbContext das XML zur Laufzeit erstellt. Dann besteht nicht die einfache Möglichkeit, dass XML anzupassen. Wer mit anderen Datenbanken wie zum Beispiel MySQL im codezentrierten Ansatz gearbeitet hat, kennt sicherlich die Probleme jenseits des dbo-Schemas. ;-)
T4 und der Zugriff auf die App.config
Zur Zeit sitze ich mal wieder tief in der T4-Materie. Dabei war mal wieder die zufällige Erstellung von AppDomains eine kleine Bremse. Das ist eine ganz spezielle Eigenart, die zum Beispiel auch dafür sorgt, dass auf die App.config der Solution nicht direkt zugegriffen werden kann.
Im Blog von Sky Sander fand ich einen schönen Workaround, damit der Zugriff doch wieder ermöglicht werden kann.
Bei Gelegenheit muss ich mal überprüfen, ob sich die zufällige Erstellung der T4-AppDomains irgendwie in den Griff bekommen lässt.
Nachfolgend der Workaround, der bei mir in Form einer Include-Datei mit Namen ConfigurationAccessor.CS.ttinclude verwendet wird:
<#@ assembly name="System.Configuration" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#+
/// <summary>
/// Provides strongly typed access to the hosting EnvDTE.Project and app.config/web.config
/// configuration file, if present.
///
/// Typical usage from T4 template:
/// <code>ConfigurationAccessor config = new ConfigurationAccessor((IServiceProvider)this.Host);</code>
/// </summary>
/// <author>Sky Sanders [sky.sanders@gmail.com, http://skysanders.net/subtext]</author>
/// <date>01-23-10</date>
/// <copyright>The contents of this file are a Public Domain Dedication.</copyright>
public class ConfigurationAccessor
{
/// <summary>
/// Typical usage from T4 template:
/// <code>ConfigurationAccessor config = new ConfigurationAccessor((IServiceProvider)this.Host);</code>
/// </summary>
public ConfigurationAccessor(IServiceProvider host)
{
// Get the instance of Visual Studio that is hosting the calling file
EnvDTE.DTE env = (EnvDTE.DTE)host.GetService(typeof(EnvDTE.DTE));
// Gets an array of currently selected projects. Since you are either in this file saving it or
// right-clicking the item in solution explorer to invoke the context menu it stands to reason
// that there is 1 ActiveSolutionProject and that it is the parent of this file....
_project = (EnvDTE.Project)((Array)env.ActiveSolutionProjects).GetValue(0);
string configurationFilename=null;
// examine each project item's filename looking for app.config or web.config
foreach (EnvDTE.ProjectItem item in _project.ProjectItems)
{
if (Regex.IsMatch(item.Name,"(app|web).config",RegexOptions.IgnoreCase))
{
// TODO: try this with linked files. is the filename pointing to the source?
configurationFilename=item.get_FileNames(0);
break;
}
}
if(!string.IsNullOrEmpty(configurationFilename))
{
// found it, map it and expose salient members as properties
ExeConfigurationFileMap configFile = null;
configFile = new ExeConfigurationFileMap();
configFile.ExeConfigFilename=configurationFilename;
_configuration = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None);
}
}
private EnvDTE.Project _project;
private System.Configuration.Configuration _configuration;
/// <summary>
/// Provides access to the host project.
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/envdte.project.aspx</remarks>
public EnvDTE.Project Project
{
get { return _project; }
}
/// <summary>
/// Convenience getter for Project.Properties.
/// Examples:
/// <code>string thisAssemblyName = config.Properties.Item("AssemblyName").Value.ToString();</code>
/// <code>string thisAssemblyName = config.Properties.Item("AssemblyName").Value.ToString();</code>
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/envdte.project_properties.aspx</remarks>
public EnvDTE.Properties Properties
{
get { return _project.Properties;}
}
/// <summary>
/// Provides access to the application/web configuration file.
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.aspx</remarks>
public System.Configuration.Configuration Configuration
{
get { return _configuration; }
}
/// <summary>
/// Provides access to the appSettings section of the configuration file.
/// Behavior differs from typical AppSettings usage in that the indexed
/// item's .Value must be explicitly addressed.
/// <code>string setting = config.AppSettings["MyAppSetting"].Value;</code>
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.appsettings.aspx</remarks>
public KeyValueConfigurationCollection AppSettings
{
get { return _configuration.AppSettings.Settings;}
}
/// <summary>
/// Provides access to the connectionStrings section of the configuration file.
/// Behavior is as expected; items are accessed by string key or integer index.
/// <code>string northwindProvider = config.ConnectionStrings["northwind"].ProviderName;</code>
/// </summary>
/// <remarks>see http://msdn.microsoft.com/en-us/library/system.configuration.configuration.connectionstrings.aspx</remarks>
public ConnectionStringSettingsCollection ConnectionStrings
{
get { return _configuration.ConnectionStrings.ConnectionStrings;}
}
}#>



Social Bookmarking