Blog Home  Home Feed your aggregator (RSS 2.0)  
artiso Blog - Thursday, November 01, 2007
Neues rund um's Thema .Net
 
 Thursday, November 01, 2007

Ich habe mir gerade ein kleines Beispiel für den Zugriff auf einen Webservice per JavaScript per AJAX zusammengebaut. Da ich hier einige Informationen aus verschiedenen Quellen zusammengetragen habe, möchte ich das Beispiel hier kurz posten, vielleicht hilft es dem einen oder anderen, einige Informationen gesammelt zu finden.

Hier zunächst der Code für den Webservice:

   1: namespace ValuePlanner_2008.Services
   2: {
   3:     /// <summary>
   4:     /// Summary description for INNProjectExplorerWebService
   5:     /// </summary>
   6:     [WebService(Namespace = "http://tempuri.org/")]
   7:     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
   8:     [GenerateScriptType(typeof(myData))]
   9:     [ToolboxItem(false)]
  10:     [ScriptService]
  11:     public class INNProjectExplorerWebService : System.Web.Services.WebService
  12:     {
  13:         [WebMethod]
  14:         [ScriptMethod]
  15:         public myData HelloWorld(int ID)
  16:         {
  17:             return new myData { ID = ID, Text = "Hello World" + ID.ToString() };
  18:         }
  19:     }
  20:     
  21:     public class myData
  22:     {
  23:         public int ID { get; set; }
  24:         public string Text { get; set; }
  25:     }
  26: }


Kurz zu erwähnen sind hier folgende Details: In Zeile 10 wird über ein entsprechndes Attribut der Webservice für die Zusammenarbeit mit AJAX konfiguriert. Das selbe gilt in Zeile 14 für die Methode, die der Webservicebereitstellt. Da die Methode einen eigenen Datentyp (myData) zurückgibt, wird in Zeile 8 dieser Typ ebenfalls für AJAX veröffentlicht. Man kan so beliebig komplexe Datenstrukturen zwichen JavaScript und dem WebService austauschen, solange diese auf Datentypen basieren, dieJavaScript auch verarbeiten kann (also z.B. keine generischen Listen etc.). Der restliche Code wird automatisch über das Webservice Template erzeugt bzw. sollte keiner weiteren Erklärung bedürfen.

In der ASPX-Seite muss ein Scriptmanager eingebaut werden und der Service darauf registriert werden. Da ich de Scriptmanager in meinem Beispiel in der Master-Page habe, verwende ich einen ScriptmanagerProxy, der es erlaubt, die Registrierungen für einzelne Seiten vorzunehmen und an den Scriptmanager in der Master-Page weiterleitet. Dies ist deshalb notwendig, da auf einer Seite nur ein einziger Scriptmanager vorkommen darf.

   1: <asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
   2:     <Services>
   3:         <asp:ServiceReference Path="~/Services/INNProjectExplorerWebService.asmx" />
   4:     </Services>
   5: </asp:ScriptManagerProxy>


Noch ein wichtiger Hinweis: Ich hatte zuerst den ToolScriptManager aus dem AJAX Component Toolkit in meiner Masterpage. Dieser scheint nicht mit dem ScriptManagerProxy zusammenzuarbeiten, jedenfalls wurde bei mir die Proxy-Klasse nicht erstellt. Erst als ich einen "normalen" ScriptManager verwendet habe, funktionierte das problemlos.

Auf dem Client wird nun eine Proxy-Klasse erstellt, die die Datentypen und die Methoden des Webservices in JavaScript nachgebildet werden. Darüber lässt sich der WebService sehr einfach aufrufen.

   1: <script language="javascript" type="text/javascript">
   2:     
   3:    function fnCallWebService()
   4:     {
   5:         ValuePlanner_2008.Services.INNProjectExplorerWebService.HelloWorld(5, fnHelloWorlSuccess, fnHelloWorlFailed);
   6:     }
   7:     
   8:     function fnHelloWorlSuccess(results, context, methodName)
   9:     {
  10:         alert(results.Text);
  11:     }
  12:     
  13:     function fnHelloWorlFailed(results, context, methodName)
  14:     {
  15:         alert ("Error calling webservice");
  16:     }
  17: </script> 


in Zeile 5 wird der WebService mit der dem Namespace und dem Namen der Webservice Klasse aufgerufen. Zunächst wird der Parameter der HelloWorld-Methode übergeben. Da der Aufruf asynchron erfolgt kann wird noch eine Methode angegeben angegeb, die im Erfolgsfall aufgerufen wird, optional noch eine Methode, wenn der Aufruf fehlschlägt. In Zeile 10 sieht man schön, dass man in JavaScript nun auch das Datenobjekt des Rückgabewertes in gewohnter Weise zugreifen kann.

Thursday, November 01, 2007 9:10:45 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 
 Monday, October 29, 2007

Eines der neuen coolen Features in VS2008 ist der Intellisense-Support und das einfärben für Javascript-Code. Das hat bei mir aber nicht funktioniert. Das Problem liegt daran, dass bei der Installation zwei Registry-keys nicht korrekt gesetzt werden.

Setzt man folgende Registry-Keys korrekt dann funktioniert das Intellisense einwandfrei:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\CLSID\{014E9A41-54E5-44ED-B15E-EFFA8758BFFC}]

"CodeBase"=file:///C:\\Program Files\\Microsoft Visual Studio 9.0\\Common7\\IDE\\Microsoft.JScript.AuthoringServices.dll

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\CLSID\{014E9A41-54E5-44ED-B15E-EFFA8758BFFC}\InprocServer32\9.0.0.0]

"CodeBase"=file:///C:\\Program Files\\Microsoft Visual Studio 9.0\\Common7\\IDE\\Microsoft.JScript.AuthoringServices.dll

 

Nähere Infos findet man hier.

Monday, October 29, 2007 5:17:18 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 
 Thursday, October 25, 2007

Auf dieser Seite sind jede Menge Beispiele zum Thema LINQ thematisch geordnet aufgelistet. Hier findet man wirklich für viele Einsatzgebiete entsprechende Beispiele. Wirklich empfehlenswert! 

101 LINQ Samples

Thursday, October 25, 2007 8:30:39 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [1]    |   | 

Wenn man eine Datei löscht, die in der Quellcode-Verwaltung eingecheckt war, kann man diese leicht aus der Quellcode-Verwaltung wiederherstellen. Dazu muss man aber zuerst eine kleine Einstellung vornehmen. Unter Tools / Options / Source Control / Visual Studio Team Foundation Server kann man die Option "Show deleted items in the Source Control Explorer" aktivieren.

image

Dann werden im Source Control Explorer gelöschte Elemente angezeugt, die man einfach "undeleten" kann.

image

Thursday, October 25, 2007 5:09:17 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [1]    |   | 
 Wednesday, October 24, 2007

Wer mal etwas mehr mit WCF programmiert hat, wird feststellen, dass es recht müßig ist, für jeden Service einen Host zu implementieren. Deshalb habe ich zusammen mit meinem Kollegen Tobi einen WCF Service-Managerr entwickelt. Dieser Servicemanager wird auf dem Application-Server als Windows-Dienst ausgeführt und ist in der Lage Services die in DLLs bereitgestellt werden dynamisch zu laden und auszuführen. Zur Steuerung dient ein Client der natürlich per WCF mit dem Service-Manager kommuniziert. Damit kann auch eine größere Zahl von Services auf einem Application-Server effizient verwaltet werden.

image

Der Service-Manager kann inkl. einer kleinen Doku kostenlos heruntergeladen und verwendet werden. Über ein Feedback würde ich mich freuen.

artisoServiceManager.zip (5,94 MB) Version 0.9.0
Wednesday, October 24, 2007 10:11:09 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |   | 
 Thursday, October 18, 2007

Während einer Präsentation hat man oft das Problem, dass man einen Bereich vergrößern möchte um ihn für das Publikum besser lesbar zu machen. Das kann man mit dem kleinen kostenlosen Tool ZoomIt ganz einfach bewerkstelligen. Zusätzlich bietet das Tool auch die Möglichkeit, dass man direkt auf dem Desktop Striche zeichnet. Dadurch kann man z.B. bestimmte Bereiche während der Präsentation kennzeichnen.

Den Download gibt's hier

Thursday, October 18, 2007 11:55:51 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 
 Wednesday, October 17, 2007

Preisfrage: Was passiert bei folgendem Code:

Dictionary<int, string> Names = new Dictionary<int, string> { { 1, "Thomas" }, { 2, "Chris" } };
Names[5] = "Luka";


Ich hätte erwartet, dass der eine KeyNotFoundException wirft. Tut er aber nicht. Statt dessen wird einfach ein neues Element dem Dictionary hinzugefügt mit dem Key 5 und dem Value "Luka". Ob man das verwenden sollte sei mal dahingestellt, aber zumindest gut zu wissen.

Wednesday, October 17, 2007 5:13:36 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 

Möchte man den kleinesten Key aus einem Dictionary abfragen, dann kann man das jetzt mit Hilfe von LINQ ganz einfach tun. Das Dictionary-Objekt ist in C#3.0 mit einer ensprechenden Extension versehen wordurch das Ganze so einfach ist, wie man sich das immer gewünscht hat.

Dictionary<int, double> ListObject = new Dictionary<int,double>
{
    {2004, 1.5}, {2005, 2.7}, {2006, 3.8}
};
int minYear = ListObject.Min(l => l.Key);
Wednesday, October 17, 2007 10:25:48 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |  |   | 

Ich habe mich entschieden, von Zeit zu Zeit Kurzbeschreibungen zu Büchern, die ich aktuell lese zu posten. Vielleicht erhält der eine oder andere dadurch die Anregung, sich mit einem Buch näher zu beschäftigen. Ich poste hier meine ganz private Meinung und bekomme auch keine Provision dafür, wenn jemand das Buch kauft. Ich werde so objektiv wie möglich berichten, wie ich die Bücher einschätze.

image Den Anfang möchte ich mit einem Buch von Dino Esposito machen. In "ASP.Net AJAX Programmierung beschreibt Dino fundiert aber dennoch leicht verständlich, wie AJAX funktioniert, wie ASP.NET AJAX aufgebaut ist und wie das ASP.NET AJAX Component Toolkit eingesetzt werden kann.

Das Buch setzt entsprechende Kenntnisse in der Programmierung von ASP.NET und auch HTML und Javascript voraus und richtet sich damit an Entwickler die bisher bereits Web-Anwendungen mit ASP.NET entwickelt haben. Es bietet eine gute Grundlage für die Programmierung von AJAX Anwendungen und vermittelt auch die Hintergründe zur Technologie, so dass man bestimmte Probleme und Effekte besser verstehen kann. Dino bietet jede Menge Tipps, wie man mit ASP.NET AJAX seine Anwendungen aufbauen sollte und welche Fehler man vermeiden sollte. Damit ist das Buch eine wertvolle Unterstützung für alle die ihre ASP.NET Anwendungen mit Hilfe von AJAX optimieren wollen.

Die Beispiele sind nicht übermäßig ausführlich aber präzise beschrieben. Die oben Beschriebenen Grundkenntnisse vorausgesetzt kann man schnell folgen und vieles davon auch gleich in der Praxis einsetzen. Der Stil des Buches ist angenehm und gut lesbar, so dass ich dieses Buch auf jeden Fall empfehlen kann.


Microsoft ASP.NET AJAX-Programmierung von Dino Esposito erschienen bei Microsoft-Press

Wednesday, October 17, 2007 9:48:44 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 

Vor kurzem hatte ich in einem Post beschrieben, wie man mit C#3.0 Listen effizient initialisieren kann. Das Ganze funktioniert übrigens auch mit Dictionaries. Das siehtr dann einfach so aus:

productVersion.Sales = new Dictionary<int,double>
{
    {2004, 1.5}, {2005, 2.7}, {2006, 3.8}
};
Wednesday, October 17, 2007 9:29:20 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |  |   | 
 Monday, October 08, 2007

Ich hatte gerade ein kleines Problem mit der Object Test Bench. Die Object Test Bench ermöglicht es, im Visual Studio Instanzen von Objekten zu erzeugen, auf denen man dann direkt Methoden aufrufen kann, ohne die Anwendung zu starten. Dazu geht man in den Class View oder den Class Designer, klickt die entsprechende Klasse mit der rechten Maustaste an und ruft dann aus dem Kontext-Menü den Befehl "Create Instance" auf ... Tja, wenn der Befehl denn da wäre. Bei mir war da nichts zu sehen. Nach einigem Versuchen habe ich dann rausgefunden, dass dies wohl nur auf dem Projekt funktioniert, das als Startprojekt ausgewählt wurde. Also habe ich meine Library als Startprojekt gekennzeichnet und siehe da, es funktioniert.

Schade ist allerdings, dass Methoden, die generische Datentypen zurückliefern nicht von der OTB unterstützt werden.

Weiterführende Informationen findet man hier: http://blogs.msdn.com/parthopdas/archive/2005/05/04/414704.aspx

Monday, October 08, 2007 1:23:09 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 
 Saturday, October 06, 2007

Objekte und Listen lassen sich jetzt in C# 3.0 sehr schön initialisieren. War bisher um ein relativ einfaches Objekt zu initialisieren sehr viel Code erforderlich, hat sich das nun deutlich verkürzt:

Bisher:

List<cProduct> Products = new List<cProduct>();

cProduct Product = new cProduct();
Product.ID = 1;
Product.BusinessDevelopmentComment = "";
Product.MarketingComments = "";
Product.VersionDescriptions = new List<cProductVersionDescription>();
Product.INNs = new List<cProductINNItem>();

cProductVersionDescription VersionDescription = new cProductVersionDescription();
VersionDescription.ID = 1;
VersionDescription.LastPCMDate = DateTime.Now;
VersionDescription.LastPCMDecision = "Go";
VersionDescription.NPV = 689.54;
VersionDescription.ProductID = 1;
VersionDescription.ProductVersionID = 1;
VersionDescription.VersionName = "Version1";
Product.VersionDescriptions.Add(VersionDescription);

cProductINNItem INN = new cProductINNItem();
INN.INN = 8;
INN.Dosage = 1.5;
INN.DosageUnit = 1;
INN.Volume = 1;
INN.Comment = "Test-Comment";
INN.INNOrder = 1;
Product.INNs.Add(INN);

INN = new cProductINNItem();
INN.INN = 2;
INN.Dosage = 2.5;
INN.DosageUnit = 2;
INN.Volume = 2;
INN.Comment = "Test-Comment2";
INN.INNOrder = 2;
Product.INNs.Add(INN);
 

Neu mit C#3.0:

List<cProduct> Products = new List<cProduct>
{
    new cProduct{ ID = 1, BusinessDevelopmentComment = "", MarketingComments = "", 
        VersionDescriptions = new List<cProductVersionDescription>
        {
            new cProductVersionDescription{ ID=1, LastPCMDate=DateTime.Now, LastPCMDecision="Go", NPV=689.54, ProductID=1, ProductVersionID=1, VersionName="Version1"}
        }, 
        INNs = new List<cProductINNItem>
        {
            new cProductINNItem{ INN=8, Dosage=1.5, DosageUnit=1, Volume=1, Comment="Test-Comment", INNOrder=1},
            new cProductINNItem{ INN=2, Dosage=2.5, DosageUnit=2, Volume=2, Comment="Test-Comment2", INNOrder=2}
        }
    }
};

 

Bisher konnte man den Code noch etwas vereinfachen, indem man einen entsprechenden Konstruktor für die Objekte erstellt hat, in dem die Initialisierungswerte übergeben werden konnten. Hier bietet die neue Version aber den Vorteil, dass diese leichter zu leesen ist, da die Parameter hier entsprechend bezeichnet sind. Beim Konstruktor musste immer Intellisense zu Hilfe genommen werden um herauszufinden, um welchen Parameter es sich handelt.

Saturday, October 06, 2007 5:17:22 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |  |   | 

Möchte man ASP.Net Anwendungen mit VS2008 debuggen, dann ist entscheidend, welchen Servermode man in den Einstellungen für das Web-Projekt ausgewählt hat.

image

Mit dem Visual Studio Developer Server hat das bei mir auf anhieb funktioniert. Allerdings mit dem IIs gab es Probleme. Hier habe ich zuerst diese NMeldung bekommen:

Unable to start debugging on the web server. Strong name validation failed.

Diesen Fehler kann man beheben indem man folgendes Procedere durchführt:

  1. Visual Studio beenden!
  2. Den "Visual Studio Command Prompt" mit "Run as Administrator" starten.
  3. Dann folgenden Befehl ausführen: sn.exe -Vr "%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\iisresolver.dll"

Damit sollte diese Meldung behoben sein. Dann kam aber bei mir der nächste Fehler:

Unable to start debugging on the web server. IIS does not list an application that matches the launched URL.

Um diesen Fehler zu beseitigen muss man die Windows Authentification aktivieren. Dazu geht man folgendermaßen vor:

  1. Im Explorer auf "Computer" Rechtsklick und dann "Manage"
  2. Den IIS Manager öffnen und die entsprechende Anwendung auswählen

    image
  3. "Authentication" unter IIS doppelklicken und dann "Windows Athentication" auf Enabled setzen

    image

Damit hat bei mir das Debuggen dann auch mit dem IIS funktioniert.

Saturday, October 06, 2007 4:39:24 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |   | 
 Tuesday, September 18, 2007

Ich habe in einer Anwendung ein Objekt, das über ca. 30 List<double> Properties verfügt. Dieses Objekt möchte ich nun mit LINQ to SQL in die Datenbank schreiben. Natürlich unterstützt LINQ 1:n-Beziehungen in Objekten aber in der Datenbank müsste ich dann für jedes List-Property eine eigene Tabelle anlegen und für jedes Property einen eigenen Typ definieren. Das schien mir doch sehr umständlich.

Ich habe dann nach einer Möglichkeit gesucht, die List-Elemente etwas effizieter zu speichern, am einfachsten in einem Feld je Liste, da ich die für die Abfrage eh nicht brauche. Aber wie kann ich die Ausgabe von LINQ in die Datenbank steuern? Wie kann ich erreichen, dass LINQ meine Daten entsprechend formatiert?

Googeln brachte keine vernünftigen Ergebnisse und sonst bin ich auch nicht fündig geworden, aber dann kam die Idee (manchmal ist es schneller erst nachzudenken bevor man googelt, aber das vergisst man oft ;-)). Die Grundidee war, für jede Liste noch ein zusätzliches Property anlegen, das die Daten im Getter und Setter entsprechend formatiert. Die Hoffnung war, wenn ich dieses Property mit einem Column-Attribut versehe, wird LINQ den formatierten Inhalt schreiben und beim Lesen wieder zurückformatieren.

public List<string> Sales { get; set; }
[Column(Name="Sales", DbType = "nvarchar(4000)")]
private string SalesString
{
    get
    {
        return string.Join("|", Sales.ToArray());
    }
    set
    {
        if (value != null)
            Sales = value.Split('|').ToList();
    }
}

 

Ich habe einfach eine Text-Spalte in der Datenbank verwendet und die einzelnen List-Elemente mit einem Trennzeichen verbunden (der Einfachkeit halber habe ich mit einer List of Strings gearbeitet. Für die Tests ist das ausreichend und sollte auf beliebige andere Typen übertragbar sein).

Einen kleinen Stolperstein gibt es noch. Beim Ausführen erhielt ich folgende Meldung:

image

LINQ verwendet Optimistic Locking für das Schreiben und hat wohl Probleme, wenn der Inhalt des Property im Getter verändert wird. Abhilfe schafft hier das zusätzliche Attribut UpdateCheck, also sollte das Attribut zu dem Property so aussehen:

[Column(Name="Sales", DbType = "nvarchar(4000)", UpdateCheck=UpdateCheck.Never)]

 

Hier sind natürlich auch noch andere Möglichkeiten der Datenrepräsentation möglich. Hier mal ein Beispiel mit XML-Serialisierung und diesesmal auch mit Doubl-Werten.

public List<double> Sales { get; set; }
[Column(Name="Sales", DbType = "xml", UpdateCheck=UpdateCheck.Never)]
private string SalesString
{
    get
    {                
        UTF8Encoding encoding = new UTF8Encoding();
        MemoryStream ms = new MemoryStream();
        XmlSerializer xmlSer = new XmlSerializer(typeof(List<double>));
        xmlSer.Serialize(ms, Sales);
        return encoding.GetString(ms.ToArray());
    }
    set
    {
        if (value != null)
        {
            UTF8Encoding encoding = new UTF8Encoding();
            MemoryStream ms = new MemoryStream(encoding.GetBytes(value));
            XmlSerializer xmlSer = new XmlSerializer(typeof(List<double>));
            Sales = (List<double>)xmlSer.Deserialize(ms);
        }
    }
}

 

Dank an Bernhard Gojer für den Lösungsansatz.

Tuesday, September 18, 2007 11:09:29 AM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    |   | 
 Monday, September 17, 2007

Vom 10.Oktober bis 12.Oktober findet in Berlin die XTOPIA statt. Dabei handelt es sich um eine Web-Konferenz von Microsoft, die sich an Entwickler, Entscheider und Designer richtet. Im Zentrum dabei steht Silverlight.

Ich werde auf der XTOPIA als ATE (Ask The Expert) zum Thema Visual Studio Team System vertreten sein.

image

Monday, September 17, 2007 7:53:46 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 

Nutzt man LINQ in einer klassischen 2-tier Anwendung, dann funktioniert das wunderbar. Nur leider ist das eher ein Auslaufmodell. Bei modernen Anwendungen kommt häufig ein 3-tier Ansatz vor (SOA etc.). An einer kleinen WCF-Anwendung möchti ich das Problem kurz schildern:

Eine kleine Anwendung soll Daten darstellen und bearbeiten können. nehmen wir einfach mal eine Adress-Verwaltung. Die Anwendung soll service-orientiert augebaut sein, d.h. es gibt einen zetralen Service, der die Schnittstelle zur Datenbank implementiert.

Soll nun eine Liste der Adressen geladen werden, ruft der Client auf dem Service eine entsprechende Methode auf. Diese Methode nutzt nun LINQ um die Adressen aus der Datenbank zu lesen und als List<cAddress> zurückzuliefern. Diese kann der Client nun darstellen. So weit, so gut. Aber was passiert jetzt, wenn eine Adresse bearbeitet wird und diese wieder gespeichert werden soll. Der Client ruft eine entsprechende Methode auf dem Service auf und übergibt das neue Adress-Objekt. Durch die Übertragung zum Client und wieder zurück muss das Objekt serialisiert und wieder deserialisiert werden. Dadurch stellt es eine komplett andere Instanz dar, wenn es wieder beim Service ankommt und hat daher keine Verbindung mehr zum DataContext (vorausgesetzt dieser wäre überhaupt noch vorhanden, dazu müsste der in einer Session aufbewahrt werden, was recht problematisch ist). Wie bekommt man nun das neue Adress-Objekt in die Datenbank?

Die von Microsoft vorgschlagene Vorgehensweise geht über die Attach-Methode. Dabei erzeugt man einfach einen neuen DataContext und fügt das bearbeitete Objekt diesem hinzu. Das Problem dabei ist das Change-Tracking des DataContext. Dieser möchte nämlich wissen, was sich an dem Objekt geändert hat. Führt man nur einen Attach durch, dann bewirkt das gar nichts. Man kann entweder das gesamte Objekt als bearbeitet deklarieren, so wie ich das in diesem Post demonstriert habe, oder man muss ein Referenz-Objekt mit angeben, gegen das das Objekt evrglichen werden kann. Eine weitere Möglichkeit ist auch noch die Zuweisung aller geänderten Eigenschaften nach dem Attach, da ab diesem Zeitpunkt das Change-Tracking alle Änderungen registriert. Das ist alles nicht so schön.

Mit einem kleinen Trick kan man sich hier die Sache etwas einfacher machen, auch wenn das nicht gerade sauber ist. Man kann das Objekt einfach löschen und dann neu hinzufügen. Das angenehme ist, dass der DataContext das recht intelligent handhabt und intern aus dem Delete and Insert ein Update macht. Der Code sieht dann ungefähr so aus:

public void UpdateCompany(LINQTest.Contracts.DataContracts.cContactCompany company)
{
    cContactDataContext db = new cContactDataContext("Data Source=NOTEBOOK_VISTA;Initial Catalog=LINQ;Persist Security Info=True;");

    cContactCompany OrigCompany = db.Companies.Single<cContactCompany>(c => c.ID == company.ID);
    if (OrigCompany != null)
    {
        db.Companies.Remove(OrigCompany);
    }
    db.Companies.Add(company);
    db.SubmitChanges();
}

 

Auch wenn das, wie gesagt nicht besonders schön ist, ich konnte noch keine negativen "Nebenwirkungen" feststellen, der Code macht genau das, was ich von ihm erwarte und das auch noch mit relativ schönem SQL, wie mir der SQL-Profiler anzeigt. Erst wird mal ein SELECT gemacht um zu sehen, ob das Element schon vorhanden ist (diesen Punkt kann man sicher noch optimieren):

exec sp_executesql N'SELECT [t0].[ID], [t0].[CompanyName], [t0].[CompanyAddress], [t0].[CompanyTelephone]
FROM [tblCompanies] AS [t0]
WHERE [t0].[ID] = @p0',N'@p0 int',@p0=2

 

Dann wird der Update ausgeführt

exec sp_executesql N'UPDATE [tblCompanies]
SET [CompanyName] = @p1
WHERE [ID] = @p0',N'@p0 int,@p1 nvarchar(12)',@p0=2,@p1=N'Test-Company'

 

Das sieht doch eigentlich sehr schön aus.

Problematisch wird die Sache nun, wenn das Objekt Unterelemente enthält, also z.B. Ansprechpartner. Hier gibt es mit dem Attach keine saubere Lösung undauch der Delete and Insert Ansatz versagt hier zunächst. Es wird einfach für alle untergeordneten Elemente ein Insert ausgeführt, egal ob diese bereits existieren oder nicht. Das Ganze kann man lösen, indem man auch die Unterelemente zunächst löscht.

public void UpdateCompany(LINQTest.Contracts.DataContracts.cContactCompany company)
{
    cContactDataContext db = new cContactDataContext("Data Source=NOTEBOOK_VISTA;Initial Catalog=LINQ;Persist Security Info=True;User ID=sa;Password=admin");

    cContactCompany OrigCompany = db.Companies.Single<cContactCompany>(c => c.ID == company.ID);
    if (OrigCompany != null)
    {
        db.Companies.Remove(OrigCompany);

        foreach (cContactPerson p in OrigCompany.Persons)
        {
            db.Persons.Remove(p);
        }
    }
    db.Companies.Add(company);
    db.SubmitChanges();
}

 

Auch hier wird wieder ein schönes Update daraus gebaut.

Also so richtig toll finde ich das Ganze noch nicht. Hier bleibt zu hoffen, dass Microsoft an dieser Stelle schnellstens noch nachbessert. Das sieht im Moment aber eher schlecht aus, wie man hier und hier lesen kann. Vielleicht hat schon jemand von euch hier Erfahrungen gesammelt. Über einen Kommentar würde ich mich freuen, auch über "Bedenken" bezgl. des Delete and Insert Ansatzes. 

Monday, September 17, 2007 7:27:14 PM (Mitteleuropäische Zeit, UTC+01:00)  #    Comments [0]    | 
 Friday, September 14, 2007

Ein Benkannter hatte gerade ein seltsames Problem. Unter VB.Net kann man sehr einfach einen Splash-Screen einrichten. Offensichtlich scheint es da allerdings ein Problem zusammen mit dem Virtual Server zu geben. Jedesmal wenn er die Anwendung innerhalb des Virtual Servers ausgeführt hat, wurde der Splash-Screen angezeigt und er erhielt dann eine Nullreference Exception. In der Host-Umgebung funktionierte das problemlos. Beidesmal kam Win2000 als Betriebssystem zum Einsatz. Hat schn mal jemand so ein Problem gehabt?

Hier der Stacktrace:

   at System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)
   at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
   at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
   at System.Windows.Forms.Control.Invoke(Delegate method)
   at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.HideSplashScreen()
   at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.MainFormLoadingDone(Object sender, EventArgs e)

   at System.EventHandler.Invoke(Object sender, EventArgs e)
   at System.Windows.Forms.Form.OnLoad(EventArgs e)
   at System.Windows.Forms.Form.OnCreateControl()
   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   at System.Windows.Forms.Control.CreateControl()
   at System.Windows.Forms.Control.WmShowWindow(Message& m)
   at System.Windows.Forms.Contr