code it

Martins Tech Blog

Wo ist eigentlich Temp?

Immer wieder gern verwendet ist der temporäre Ordner, um da Dateien zwischenzuspeichern die später noch gebraucht werden - sei es, um dort Dateioperationen auszuführen, die In-Memory nicht möglich sind oder um Dateien abzulegen, die dann von einem anderen Programm geöffnet werden sollen.

Aber wo ist eigentlich Temp und hab ich da immer Schreibrechte? Die kurze Antwort lautet: a) es hängt von der Umgebung ab und b) ja, dort sind Schreibrechte vorhanden.

Lokale Anwendungen
Lokale Anwendungen, die unter einem Benutzeraccount ausgeführt werden verwenden den Pfad, der in den Pfadangaben des Benutzers als TEMP angegeben ist. Also in der Regel "/AppData/Local/Temp" im Benutzerprofil.

Webseiten im IIS Express
Der IIS Express (also der Entwicklungsserver für z.B. ASP.NET Webseiten) läuft ebenfalls unter dem Benutzeraccount des aktuellen Benutzers und damit landen temporäre Dateien genau wie bei anderen lokalen Anwendungen im TEMP-Ordner des Benutzerprofils.

Webseiten im IIS
Im IIS sieht das Ganze schon etwas anders aus. Hier gibt es auch einen Temp-Ordner. Allerdings wird hier der System-Temp-Ordner verwendet - dieser sollte sich im Standard unter C:\Windows\Temp befinden.

Webseiten im Azure
Auch im Azure gibt es einen temporären Ordner auf dem man Schreibrechte hat. Dieser befindet sich unter "C:\DWASFiles\Sites\<sitename>\Temp".

Noch ein kleiner Hinweis: Der temporäre Ordner ist sehr komfortabel, aber bitte liebe Entwickler räumt dort auch wieder auf und löscht Dateien die ihr erstellt habt und nicht mehr braucht auch wieder.

Mit dem Service Broker auf Datenänderungen reagieren

Im SQL Server gibt es seit längerem den SQL Server Service Broker. Mit dessen Hilfe kann man live auf Datenänderungen reagieren. Beispiel gefällig?

Ich habe eine sehr einfache Bibliotheksdatenbank. Enthalten ist nur eine Tabelle (dbo.Books) mit den Spalten ID, Author und Title. Und ich habe eine WPF-Anwendung in der in der MainView per Entitiy Framework die Bücher geladen und an die Liste gebunden werden.
<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding BookList}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Title}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

public class MainViewModel
{
    public MainViewModel()
    {
        ReloadData();
    }

    private void ReloadData()
    {
        using (var entitites = new LibraryEntities())
        {
            bookList.Clear();
            foreach (var book in entitites.Books)
            {
                bookList.Add(book);   
            }
        }
    }

    private readonly ObservableCollection bookList = new ObservableCollection();

    public ObservableCollection BookList
    {
        get
        {
            return bookList;
        }
    }
}

So weit erst einmal kein Hexenwerk. Der spannende Teil kommt jetzt.

Seit ADO.NET 2.0 gibt es die Klasse SqlDependency, mit der Änderungen an den Daten überwacht werden können. Diese kann für diesen Zweck auch hier verwendet werden.

SqlDependency verfügt über 2 statische Methoden Start und Stop und wie der Name schon vermuten lässt, kann man damit das Tracking starten und auch wieder beenden. In meinem Beispiel sind diese im Konstrukor und im Dispose des ViewModels. Der restliche Code wird in der Reload-Methode angefügt...
public class MainViewModel : IDisposable
{
    private string connectionString;

    public MainViewModel()
    {
        connectionString = new LibraryEntities().Database.Connection.ConnectionString;
        dispatcher = Dispatcher.CurrentDispatcher;
        SqlDependency.Start(connectionString);
        ReloadData();
    }

    private void ReloadData()
    {
        using (var entitites = new LibraryEntities())
        {
            bookList.Clear();
            foreach (var book in entitites.Books)
            {
                bookList.Add(book);   
            }
        }

        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
            using (var command = new SqlCommand("SELECT Title, Author FROM dbo.Books", connection))
            {
                command.Notification = null;
                var dependency = new SqlDependency(command);
                dependency.OnChange += OnDependencyChange;
                command.ExecuteReader(CommandBehavior.CloseConnection);
            }
        }
    }

    private void OnDependencyChange(object s, SqlNotificationEventArgs e)
    {
        ((SqlDependency)s).OnChange -= OnDependencyChange;

        if (e.Type == SqlNotificationType.Change)
        {
            dispatcher.Invoke(this.ReloadData);
                
        }
    }

    private readonly ObservableCollection<Books> bookList = new ObservableCollection<Books>();

    private Dispatcher dispatcher;

    public ObservableCollection<Books> BookList
    {
        get
        {
            return bookList;
        }
    }

    public void Dispose()
    {
        SqlDependency.Stop(connectionString);
    }
}

... und hier beginnt es etwas schmutzig zu werden.

SqlDependency basiert auf den Möglichkeiten von ADO.NET 2.0. Damit wird hier eine SqlConnection benötigt und ein SqlCommand, das mit einem DataReader auch ausgeführt werden muss. Und es gibt noch ein paar weitere Punkte zu beachten:
  1. Auf der Datenbank muss der Broker aktiviert sein.
  2. Der Benutzer benötigt ausreichende Berechtigungen (SUBSCRIBE QUERY NOTIFICATIONS)
  3. Der Command bei der Initialisierung der SqlDependency muss bestimmten Voraussetzungen entsprechen (also z.B. nicht SELECT * FROM ....)

Alle diese Einschränkungen kann man ausführlich nochmal auf CodeProject nachlesen.

Während der Ausführung ist es dann wichtig zu wissen, dass der Event ein One-Shot ist - heißt er wird nur bei der ersten Änderung ausgelöst. Deshalb muss dann wenn die Daten neu geladen werden auch der Event wieder registriert werden.

Wenn man das alles berücksichtigt, dann kann man damit aber recht coole Sachen machen. Und man ist nicht auf die WPF beschränkt. Mit der Hilfe von OWIN und SignalR auch weitreichender über Datenänderungen informieren.

Bedingte Kompilierung im Android Studio

Wer wie ich aus der .NET-Welt kommt, der kennt das Feature der bedingten Kompilierung. Ein klassisches Beispiel dafür ist ein Codeschnipsel wie dieser hier:
var config = new Config();
#if DEBUG
    config.EndPoint = "http://localhost/testendpoint/";
#else
    config.EndPoint = "http://myapp.endpointsoftheworld/";
#endif
    new Wizard().DoSomeMagic(config);
Was passiert hier? Je nach aktueller Buildkonfiguration können Variablen definiert werden. Sind diese gesetzt kann mit Precompiler Switches auf diese Variablen zugegriffen werden und ein bestimmter Teil des Codes wird in die Kompilierung einbezogen oder auch nicht. Die wohl bekannteste dieser Precompiler Variablen ist DEBUG, welche wie der Name schon vermuten lässt im Debug-Build gesetzt ist.

Damit kann man erreichen, dass beispielsweise in Testbuilds anderer Code ausgeführt oder andere Einstellungen verwendet werden oder dass man bestimmte Module in seiner Anwendung zur Verfügung hat oder auch nicht. Als Szenario wäre hier denkbar dass man eine Free- und eine Bezahlvariante seiner Anwendung anbietet.

Genau diese Anwendungsfälle sind auch im Bereich der App-Entwicklung durchaus gängige Szenarien. Allerdings unterstützt Java und damit auch Android solche Preprozessor Direktiven nicht. 

Eine durchaus gängige Lösung ist es, mit anwendungsweiten Konstanten zu arbeiten:
public final class Debug {
  //set to false to allow compiler to identify and eliminate
  //unreachable code
  public static final boolean ON = true;
} 
var config = new Config();
if (Debug.ON) {
    config.EndPoint = "http://localhost/testendpoint/";
} else {
    config.EndPoint = "http://myapp.endpointsoftheworld/";
}
new Wizard().DoSomeMagic(config);
Der Kompiler erkennt die Konstante und im eigentlichen Code ist dann auch nur der Code enthalten, der gewünscht ist. 

Diese Lösung funktioniert zwar und ist auch recht ähnlich zu den Precompiler Switches, hat aber einen entscheidenden Nachteil: Die Konstante muss jedes mal von Hand auf den korrekten Wert gesetzt werden. Ein Automatismus wie bei .NET ist nicht vorgesehen und wie schnell können so Fehler passieren und plötzlich laufen Testcalls gegen die echte API und zerstören Daten.

In Android Studio wird Gradle als Buildssystem verwendet. Damit sind automatisierte Builds möglich. Und genau hier kann man ansetzen, um das Problem zu lösen. Denn Gradle unterstützt Sourcesets und Flavors. "Debug" und "Release" sind bereits mit dem Projektsetup enthalten. Mit diesem Wissen kann man nun einfach neben dem eigentlichen Main-Ordner auf gleicher Ebene zwei neue Ordner an und benennt diese "debug" und "release". Hier kann man nun Packages und Quellcodedateien ablegen, die je nach Buildvariante enthalten sein sollen.


In obiger Konfiguration wird nun die Klasse Configuration aus debug verwendet, wenn ich assembleDebug aufrufe und nach gleicher Logik die gleichnamige Klasse Configuration aus release beim Aufruf von assembleRelease.

Meine Empfehlung ist in Java wie auch in .NET, mit solchen Switches umsichtig umzugehen. Die Komplexität der Anwendung steigt ungemein je mehr solcher Switches verwendet werden und verringern die Wartbarkeit.