Montag, 9. November 2009

NUnit im STA - Modus

Neulich hatte ich die Anforderung, dass ein Programm Daten der Zwischenablage nutzen und modifizieren sollte. Ich habe (natürlich) angefangen einen Test zu schreiben. Leider war dies mit dem Unit-Test-Tool meiner Wahl (NUnit) nicht möglich. Der Fehler:
System.Threading.ThreadStateException: Der aktuelle Thread muss in den STA-Modus (Singlethreadapartment) gesetzt werden, bevor OLE-Aufrufe durchgeführt werden können.
Der Fehler tritt im Übrigen auch auf, wenn man versucht UserControls zu testen und sollte für WPF und WinForms gleich sein.
Die Lösung des Problems: NUnit-Tests im Single-Threaded Apartment (STA) statt in der Vorgabe (Multithreaded Apartment (MTA)) laufen lassen. Das Vorgehen: Man erstellt eine einfache app.config mit folgendem Inhalt:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="NUnit">
            <section name="TestRunner" type="System.Configuration.NameValueSectionHandler"/>
        </sectionGroup>
    </configSections>
    <NUnit>
        <TestRunner>
            <!-- Valid values are STA,MTA. Others ignored. -->
            <add key="ApartmentState" value="STA" />
            <!-- See ThreadPriority enum for other valid values -->
            <add key="ThreadPriority" value="Normal" />
        </TestRunner>
    </NUnit>
</configuration>
Damit läuft NUnit im STA, und die es kann auf die Zwischenablage zugegriffen werden.
Die Lösung habe ich von hier.

Dienstag, 3. November 2009

ReportPropertyChanged vs. OnPropertyChanged in Entities

Gerade etwas länger mit folgender Fehlermeldung gerungen: System.ArgumentException: Die Eigenschaft 'Foo' weist keine gültige Entitätszuordnung für das Entitätsobjekt auf. Weitere Informationen finden Sie in der Dokumentation zu Entity Framework.
Der Fehler trat immer auf,  wenn ich Foo einen wert zuweisen wollte.

Ursache des Fehlers war, das ich meinem eigenen Post über Zusammengesetzte Properties nicht gefolgt war, sondern statt dessen auf diversen Settern noch ReportPropertyChanged("Foo") programmiert hatte.
ReportPropertyChanged ist abernicht das gleiche wie OnPropertyChanged. ReportPropertyChanged scheint noch mehr zu tun und funktioniert daher nur auf Properties aus Enities mit Entitätszuordnung.
Nachdem ich den Fehler gefunden hatte machte die Fehlermeldung auf einmal auch viel mehr sinn.
Die Lösung des Problems war also einfach nicht ReportPropertyChanged zu benutzen, sondern OnPropertyChanged.
(Und bei dieser gelegenheit noch einmal mein Vorgehen in diesem Fall zu überdenken, und mich etwas mehr an meine eigenen Posts halten.)

Sonntag, 25. Oktober 2009

BeforeSaveEvent in EntityFramework

Neulich habe ich nach so etwas wie BeforeSave gesucht für meine Entities. Folgendes war meine Lösung (In einer partial class):
public partial class MyEntities
{
    partial void OnContextCreated()
    {
        this.SavingChanges += HandleSavingChanges;
    }

    private static void HandleSavingChanges(object sender, EventArgs e)
    {
        MyEntities context = (MyEntities) sender;
        //code....
    }
}
In meiner partial-Klasse kann ich keinen Konstruktor erstellen. (Jedenfalls keinen, der schon an anderer Stelle definiert wurde...) Daher verwende ich OnContextCreated. Dies ist eine Methode, die vom erzeugten Code ausgeführt wird. Hier hänge ich einfach einen Eventhandler an SavingChanges.

Freitag, 2. Oktober 2009

"Zusammengesetzte" Properties in EntityFramework

Ich habe für EF-Objekte gerne mal "eigene" Properties in partial classes. Diese sind dann immer vonmindestens einer, gerne auch mehreren Properties der Entity abhängig.
Damit dann auch die richtigen PropertyChanged & -Changing Events erzeugt werden habe ich gerade folgendes gefunden: Alle Properties der EF-entities implementieren eine partial OnFooChanging bzw. OnFooChanged Methode, an die man sich in der partial class einfach "anhängen" kann. Etwa so:
public string MyTitle
{
   get
   {
      return this.Title;
   }
   set
   {
      this.Title = value;
   }
}

partial void OnTitleChanging(string unused)
{
   OnPropertyChanging("MyTitle");
}

partial void OnTitleChanged()
{
   OnPropertyChanged("MyTitle");
}
BTW: "Navigierbare Properties" (damit meine ich Properties in EF-Objekten, die auf andere EF-Objekte verweisen) lösen keinen PropertiyChanged Events aus. Hier findet sich ein kleiner Workaround dazu.

Mittwoch, 30. September 2009

ValidationErrors in xaml Controls

Validation auf xaml-controls findet man "überall" im Netz.. z.B. kann man das so machen:
<Binding Path="Person" 
         Mode="TwoWay">
 <Binding.ValidationRules>
  <ui:PersonValidationRule />
 </Binding.ValidationRules>
</Binding>
Nun habe ich aber keine möglichkeit auf einem Window sowas zu machen wie Window.ChildsHaveErrors(). Aus dieser Lage hilft das Folgende:
<Window [blah...]
  Validation.Error="ValidationError">

<Binding Path="Person" 
         Mode="TwoWay"
   NotifyOnValidationError="True">
    <Binding.ValidationRules>
        <ui:PersonValidationRule />
    </Binding.ValidationRules>
</Binding>
und im Code-Behind:
private int errorsInForm;
private void ValidationError(object sender, ValidationErrorEventArgs e)
{
   switch (e.Action)
   {
      case ValidationErrorEventAction.Added:
         this.errorsInForm++;
         break;
      case ValidationErrorEventAction.Removed:
         this.errorsInForm--;
         break;
   }
}
private bool WindowHasErrors
{
   get
   {
      return this.errorsInForm>0;
   }
}
  • Auf der Window-Deklaration im xaml den Validation.Error-Event setzten (Validation.Error ist übrigens eine "attached dependency property", sollte also auf fast allen Elementen funktionieren...)
  • Auf der Binding-Deklaration NotifyOnValidationError auf true setzen, damit wird der Fehler aus dem eigentlichen Element auf dem dieser entsteht hinaus "gebubbelt" (z.B. bis zum Window...)
  • Im Code-Behind, im ValidationError-EventHandler werden einfach die entstandenen Fehler gezählt (ValidationErrorEventAction.Removed ist ein "ehemaliger Fehler"...)
  • In WindowHasError wird dann nur noch geprüft, ob die aktuelle Zahl der Fehler größer ist als 0.
Meinungen dazu ?

VS2008 benötigt CLR-Typen von MS SQL 2008

Ich habe leztens alles deinstalliert, das "*SQL*2008*" im Namen hatte, und meinen SQL-Server 2005 DeveloperED wieder installiert.
Ein paar Tage später musste ich feststellen, dass DB-Verbindungen mit meinem VS2008 nicht mehr möglich waren.Der Fehler war: "Could not load file or assembly: Microsoft.SqlServer.Management.Sdk.Sfc in Visual Studio 2008 Server Explorer"


Lange Rede, kurzer Sinn: VS2008 benötigt für die DB-Verbindungen:
  • Microsoft SQL Server 2008 Management Objects (SMO) und
  • Microsoft SQL Server System CLR Types 
Beides gibts zum Download bei Microsoft.
Nachdem ich mir die Finger wund gesucht hatte um der Lösung auf die Schliche zu kommen habe ich auch diesen Post gefunden, der das gleiche Problem beschreibt.

Verschiedene Implementationen des selben Interfaces mit Unity "injecten"

PersonRepository und VorgangRepository sind zwei Implementationen eines Interfaces: IRepoistory
Um nun meinen DBAdapter, der (natürlich IDBAdapter implementiert und) beide IRepositories als parameter (natürlich in der richtigen Reihenfolge...) benötigt (Ob das eine tolle Idee war sei mal dahin gestellt.) instanziieren zu können, habe ich folgendes gemacht:
IUnityContainer container = UnityContainerProvider.UnityContainer;
container.RegisterType<IDbAdapter, DbAdapter>();
container.RegisterType<IRepository, PersonRepository>("Person");
container.RegisterType<IRepository, VorgangRepository>("Vorgang");
container.Configure<InjectedMembers>().ConfigureInjectionFor<DbAdapter>(
 new InjectionConstructor(
  new ResolvedParameter<IRepository>("Person"),
  new ResolvedParameter<IRepository>("Vorgang")
  ));
  • Configure() - Ich will die Member der Injection steuern...
  • ConfigureInjectionFor() - auf den DBAdapter instanzen
  • new InjectionConstructor() - der Konstruktor soll gesteuert werden
  • new ResolvedParameter("Person") - IRepository soll mit dem Key "Person" aufgelöst werden.
Etwas störend finde ich die Notwendigkeit dies über zwei Strings zu steuern, also wenn jemand eine bessere Idee hat.. Immer her damit.

Edit: Etwas besser ist natürlich das IRepository noch zwei mal abzuleiten (wenn dies möglich ist...) einmal als
IPersonenRepository und einmal als IVorgangsRepository.