Zur Zeit wird gefiltert nach: fehler
Filter zurücksetzen
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. ;-)
Lesson learnt: Oracle mag ich nicht
Wer kennt das Sprichwort nicht: Lächle und sei froh, es könnte schlimmer kommen, ich lächelte und war froh und es kam schlimmer.
Vor allem ist es immer wieder interessant, dass man Dinge erleben kann, die sich wie eine unglaubliche Geschichte anhören. In einem der letzten Projekte ging es mir so.
Das Hauptproblem in einem Projekt war, dass ein DWH, dass auf einer eingerichteten Oracle-Schreibmaschine, die nicht den Anspruch einer soliden Grundlage erfüllte, problemlos lief. Auf dem Zielsystem unterlag die Performance jedoch starken Schwankungen. Einen Hinweis zur Definition Schreibmaschine: So nenne ich Rechner, die eigentlich nur für Office vorgesehen sind und unter dem Windows-Leistungsindex von 4 liegen.
Nach dem ersten Review kam u. a. heraus, dass die Umgebung virtualisiert war und es mehrere Zonen gab. Was für mich die Performanceschwankungen erklärte, stellte aber für den Performance-Spezialist (Guru) nicht das Problem dar und er präsentierte auch ein paar angepasste SQL-Skripts. Um dies auch rechtfertigen zu können, wurden kurzerhand die spezifizierten Hardwareanforderungen auf die tatsächliche Umgebung angepasst. Das ist mal agiles Projektmanagement aus einem anderen Blickwinkel. Für mich kam dadurch eine neue Herausforderung hinzu: Wie mache ich aus einer Schreibmaschine einen Taschenrechner? Der RAM ist ja schnell begrenzt, aber die CPU-Leistung? So bin ich auch mit cpulimit in Berührung gekommen.
Für mich war das eine sehr wichtige Erfahrung, denn so konnte ich auch lernen, dass Gutes SQL in Oracle-Umgebungen gleichzusetzen ist mit produktspezifisch und Tricky. Ein weiterer Vorteil, die Empfehlungen aus den Review habe ich in einer Testumgebung auf einem SQL-Server testen können.
Nach diesen ersten Tests habe ich doch mal im Duden zur Bedeutung von Oracle nachschlagen müssen und da steht Folgendes:
Ora|kel das; -s, - <lat.; »Sprechstätte«>:
a) Stätte (bes. im Griechenland der Antike), wo Priester[innen], Seher[innen] o. Ä. Weissagungen verkündeten oder [rätselhafte, mehrdeutige] Aussagen in Bezug auf gebotene Handlungen, rechtliche Entscheidungen o. Ä. machten;
b) durch das Orakel (a) erhaltene Weissagung, [rätselhafte, mehrdeutige] Aussage Duden - Das Fremdwörterbuch, 9. Aufl. Mannheim 2007 [CD-ROM]
Rätselhafte, mehrdeutige Aussage da habe ich doch ein Déjà-vu. Denn im ersten Review wurde bspw. ein Punkt kritisiert, welcher im zweiten wieder empfohlen wurde. Auch gab es Kritikpunkte an ein paar Einstellungen, die wir auf Empfehlung des Oracle-DBA implementierten. Die Literatur, die ich mir zum Thema organisierte, war für den Guru natürlich nicht die beste und er musste den Autor Donald K. Burleson eines Werkes auch kritisieren. Das hat System, denn in Oracle-Umgebungen macht man gerne den Developer zum Sündenbock.
Ganz ehrlich besser hätte Oracle seinen Namen gar nicht treffen können, bildlich stelle ich mir das so vor:.

- Abbildung 1 Oracle-Consulting, demnächst bei Mike Shiva?
Auch wenn Oracle einen grösseren Funktionsumfang bietet, so sind damit Sachen möglich, die man besser nicht machen sollte, da sie zu Performanceeinbussen führen.
Was sich aus den Tests auf dem SQL Server ableiten lässt, ist Folgendes:
- Ein Oracle-Developer der SQL für den SQL-Server schreiben soll, wird keine Probleme bekommen, da dieser sehr tolerant und robust ist.
- Ein SQL-Server-Developer sollte vorher die Tipps und Tricks-Kurse der Gurus besuchen. Da diese auch Geld verdienen wollen, werden sicherlich nicht alle Tricks im ersten Kurs verraten. Mit dieser Salami-Taktik lässt sich gut verdienen und mit ein bisschen Geschick nimmt der Guru den eigentlichen Kunden auch gleich mit. Um dies zu umgehen, sollten die Schulungen nicht bei „Wir machen alles Unternehmen“ gebucht werden, die einen noch die Unterstützung anbieten. ;-)
- Während man in der Oracle-Umgebung mit dem Feintuning beschäftigt ist und womöglich noch auf das schlechte SQL der Developer schimpft, weil mal wieder der Append-Hint bei grossen Datenmengen fehlt, lassen sich mit dem SQL-Server schon die ersten Kundenanforderungen realisieren. ;-)
- Der mit Oracle mitgelieferte SQL Developer ist ein schlechter Aprilscherz.
- Dem Oracle-Developer und Guru muss man vorher vielleicht noch das Lizenzmodell erklären, sonst könnte es passieren, dass er viele Funktionalitäten der Tools nicht nutzt, in der Annahme diese seien nicht lizenziert.
So etwas passiert eben, wenn die Erwartungen zu hoch sind und deswegen habe ich jetzt um so mehr Freude daran, einen Oracle-Server in die ewigen Jagdgründe migrieren zu dürfen, so als Wiedergutmachung.
Parametrisierte Text Templates und System.Runtime.Serialization.SerializationException
Zurzeit beschäftige ich mich mit den T4 Templates. Ein sporadisches Problem ala Murphy trat bei parametrisierten Vorlagen auf. Wenn ich diese innerhalb einer anderen Textvorlage aufgerufen habe und einen Parameter mit einem benutzerdefinierten Objekt übergeben wollte begrüsste mich die Fehlermeldung:
Auszug Fehlermeldung Error Running transformation: System.Runtime.Serialization.SerializationException: Unable to find assembly 'UmlHelper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Server stack trace: at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly() at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name) at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable) at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record) at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum) at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.DeserializeObject(MemoryStream stm) at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage.FixupForNewAppDomain() at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm) at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(Object[] args) Exception rethrown at [0]: at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type) at Microsoft.VisualStudio.TextTemplating.ITextTemplatingSessionHost.set_Session(ITextTemplatingSession value) at Microsoft.VisualStudio.TextTemplatingBD1EC50CBA912372FF5EDDFDC3DFE64D.GeneratedTextTransformation.GetGeneratedOutputWithTemplate(String templateName, ObjectData element) at Microsoft.VisualStudio.TextTemplatingBD1EC50CBA912372FF5EDDFDC3DFE64D.GeneratedTextTransformation.TransformText() at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result)
Der Aufruf der Parameter-Vorlage erfolgte in der Hauptvorlage nach folgenden Muster:
C# Class Feature Block
private string GetGeneratedOutputWithTemplate(string templateName, TestUML.ObjectData element)
{
string templateFile = this.Host.ResolvePath(templateName);
string templateContent = File.ReadAllText(templateFile);
TextTemplatingSession session = new TextTemplatingSession();
session["Element"] = element;
// requires host specific true
var sessionHost = (ITextTemplatingSessionHost) this.Host;
sessionHost.Session = session;
Engine engine = new Engine();
string generatedContent = engine.ProcessTemplate(templateContent, this.Host);
return generatedContent;
}
In der Session wurde ein Objekt übergeben, das in der aktuellen Vorlage erstellt wurde. Im Template mit dem Parameter soll dann nur noch die Ausgabe aufbereitet werden.
Die parametrisierte Vorlage ist so weniger komplex und enthält nur die Ausgabelogik:
Parametrisierte T4 Vorlage <#@ template debug="false" hostspecific="false" language="C#" #> <#@ output extension=".cs" #> <#@ import namespace="System.Collections.Generic" #> <#@ parameter name="Element" type="TestUML.ObjectData" #> ///namespace <#= Element.Namespace #> { using System; public partial class <#= Element.ObjectName #> { <# foreach (var item in Element.ObjectAttributes) { #> public <#= item.TypeName #> <#= item.Name #> { get; set; } <# } #> } }
Wie kam nun dieser Fehler zustande und warum? Der Code wird innerhalb einer AppDomain ausgeführt. So war es sporadisch möglich, dass das Helper-Objekt nicht gefunden werden konnte, da die Templates bzw. die Dll-Datei nicht innerhalb der gleichen AppDomain liefen. Lösen konnte ich das Problem, indem ich der Hilfsklasse die Parent-Klasse MarshalByRefObject spendierte. Diese ermöglicht den Zugriff auf das Objekt über die AppDomain-Grenzen hinaus.
C# (Hilfsobjekt)
[Serializable]
public class ObjectData : MarshalByRefObject
{
public string Namespace { get; set; }
public string ObjectName { get; set; }
public List ObjectAttributes { get; set; }
}
Mal sehen, ob die sporadische Fehlermeldung nun auch definitiv beseitigt ist.



Social Bookmarking