API Anleitung

Beispielprogramm in .NET

Als Beispiel für die einfachhe Handhabung betrachten wir den Zugriff mit C# auf die API mittels .NET 8. Das komplette Programm können Sie hier herunterladen.

Weiterführende Informationen zum Umgang mit OData-Schnittstellen, und zahlreiche weitere Codebeispiele in C# finden Sie auch in der .NET Dokumentation.

Service-Referenz erstellen

Um auf die API zuzugreifen, muss man zunächst eine Datenservice-Referenz erstellen. Dies wird von Visual Studio durch ein paar Mausklicks und der Angabe der URI der LogMyTime API automatisch generiert. Die genaue Vorgehensweise hierzu ist in folgendem Artikel geschildert.

Nun kann eine Instanz einer solchen Api-Referenz erschaffen werden. Für die Authentifizierung per HTTP Header weisen wir die Api-Referenz an, bei jedem Request eine Funktion namens AttachAuthHeader aufzurufen.

Beispiel: Servicereferenz erstellen, inklusive Authentifizierung mittels LogMyTime API sowie den laut LogMyTime Nutzungsbedingungen nötigen UserAgent als Http header:


    private static APIDataSource GetDataSource()
    {
        var dataSource = new APIDataSource(new Uri("https://api.logmytime.de/V1/Api.svc"));
        dataSource.SendingRequest2 += AttachXHeaders;
        //die folgende Zeile ist optional und sorgt dafür, dass die API keine Fehler wirft, 
        //wenn auf der Serverseite weitere Datenfelder hinzugefügt werden
        dataSource.IgnoreMissingProperties = true;
        return dataSource;
    }

    private static void AttachXHeaders(object? sender, SendingRequest2EventArgs e)
    {
        var requestMessage = (HttpWebRequestMessage)e.RequestMessage;
        
        //API Schlüssel und UserAgent als HTTP Header hinzufügen
        //sie finden Ihren API-Schlüssel auf der LogMyTime Webseite unter Verwaltung -> Mein Profil
        requestMessage.SetHeader("X-LogMyTimeApiKey", _apiKey);
        requestMessage.SetHeader("X-UserAgent", ".NET Beispielclient v.1.1");
    }

Lesezugriff

Nun können wir mit dem eigentlichen Zugriff auf die API beginnen. Die Service-Referenz besitzt für jeden Ressourcentyp der LogMyTime API ein IQueryable-Objekt, aus dem diese Ressourcen ausgelesen werden können.

Beim Lesezugriff können auch vielfältige Filterparameter mittels AddQueryOption angegeben werden. Zusätzlich ist es mit Expand bei Bedarf möglich, daten aus relational verknüpften Tabellen ebenfalls in einer Abfrage zu laden, so dass z.B. zusätzlich zu Zeiteinträgen auch die enstsprechenden Projekte, Kunden und tätigkeiten geladen werden.

Beispiel: Alle Zeiteinträge aus dem letzten Monat inklusiven Projekten und Tätigkeiten laden und ausgeben:

private static async Task LoadAndPrintAllTimeEntriesForTheLast30Days(APIDataSource dataSource)
{
    var oneMonthAgo = DateTime.Now.AddMonths(-1);
    var timeEntries = await ExecuteQueryAsync(dataSource.TimeEntries
        .AddQueryOption("$filter",
            $"month(StartTime) eq {oneMonthAgo.Month} and year(StartTime) eq {oneMonthAgo.Year}")
        .Expand("Project")
        .Expand("Task"));

    foreach (var timeEntry in timeEntries)
        Console.WriteLine("Von {0} bis {1}: {2} / {3}",
            timeEntry.StartTime,
            timeEntry.EndTime,
            timeEntry.Project.Name,
            timeEntry.Task != null ? timeEntry.Task.Description : null);
}

private static async Task<T[]> ExecuteQueryAsync<T>(DataServiceQuery<T> query)
{
    var taskCompletionSource = new TaskCompletionSource<T[]>();

    query.BeginExecute(ar =>
    {
        try
        {
            var result = query.EndExecute(ar);
            taskCompletionSource.SetResult(result.ToArray());
        }
        catch (Exception e)
        {
            taskCompletionSource.SetException(e);
        }
    }, null);

    return await taskCompletionSource.Task;
}

Ausgabe:

Von 27.07.2010 15:34:23 bis 27.07.2010 17:48:26: Marsexpedition / Recherche
Von 27.07.2010 12:31:30 bis 27.07.2010 14:06:31: Marsexpedition / Meeting
Von 27.07.2010 01:00:00 bis 27.07.2010 04:00:00: Mondlandung / Triebwerk B montieren
...

Einzelne Ressourcen laden

Einzelne Ressourcen lassen sich analog laden, nur das hierbei FirstOrDefault() verwendet wird, anstatt alle ergebnisse zu iterieren:

Beispiel: Ein projekt mit einer bestimmten ID von der API laden

private static async Task<Project?> LoadProjectWithSpecificID(APIDataSource dataSource, int newProjectID)
{
    var query = dataSource.Projects
        .Where(p => p.ID == newProjectID)
        .Take(1);

    var uri = new Uri(query.ToString() ?? throw new InvalidOperationException("Invalid Uri string"));

    // Execute the query asynchronously
    var projects = await Task.Factory.FromAsync(dataSource.BeginExecute<Project?>(uri, null, null), dataSource.EndExecute<Project?>);

    return projects.FirstOrDefault();
}

Schreibzugriff

Ressourcen erstellen

Der Schreibzugriff gestaltet sich ähnlich einfach. Für das Erstellen neuer Ressourcen muss der Ressourcen-Stub mit der AddTo{RessourceListenName}-Methode in die Service-Referenz eingefügt werden. Anschließend weist man die Service-Referenz an, die neue Ressource zum Server zu übertragen.

Beispiel: Ein neues aktives Projekt mit einem eindeutigen Namen erstellen und zur LogMyTime API hochladen:

private static async Task<Project> CreateNewProjectAsync(APIDataSource dataSource)
{
    var newProject = new Project
    {
        //wir verwenden einen zufälligen Namen, um sicherzustellen, dass dieser nicht bereits auf dem Server existiert
        Name = "Testprojekt - "+Guid.NewGuid().ToString(),
        Active = true,
        LastChangeTime = DateTime.Now
    };
    dataSource.AddToProjects(newProject);
    await SubmitChangesAsync(dataSource);
    return newProject;
}

Ressourcen ändern

Beim Ändern von Ressourcen muss man der Service-Referenz explizit mitteilen, dass die am Referenz-Stub gemachten Änderungen mit dem Server synchronisiert werden sollen.

Beispiel: Ein Projekt mit einer bestimmten ID umbenennen:

private static async Task RenameProjectAsync(APIDataSource dataSource, Project newProject)
{
    newProject.Name = "Testprojekt 2"+Guid.NewGuid();
    dataSource.UpdateObject(newProject);
    await SubmitChangesAsync(dataSource);
}

Ressourcen löschen

Das Löschen ist ist ähnlich einfach wie das Erstellen von Ressourcen. Man kann hierzu zunächst die ressource laden und anschliessend mit dem Datenkontext löschen.

Beispiel: Ein Projekt mit einer bestimmten ID löschen:

private static async Task DeleteProjectAsync(APIDataSource dataSource, Project newProject)
{
    dataSource.DeleteObject(newProject);
    await SubmitChangesAsync(dataSource);
}

ChangesDigest herunterladen

Die LogMyTime API bietet zusätzlich zu ressourcentabellen auch noch einige Service-Operationen, die z.B. die Synchronisation von Daten stark vereinfachen und das Steuern der Stoppuhr über die API ermöglichen. Weitere Informationen hierzu finden Sie in der API-Anleitung. Als Beispiel wird im Beispielprogramm ein ChangesDigest-Objekt mit den Änderungen der letzten 24 Stunden heruntergeladen.

Beispiel: ChangesDigest herunterladen

private static async Task GetChangesDigestAsync(APIDataSource dataSource,
    bool ignoreOtherUsersTimeEntries,
    DateTime referenceTime)
{
    var uri = new Uri(string.Format(
            "{0}/GetChangesDigest?LastSynchronizationTime={1}&IgnoreOtherUsersResources={2}",
            dataSource.BaseUri,
            referenceTime.ToString("s"),
            ignoreOtherUsersTimeEntries),
        UriKind.RelativeOrAbsolute);

    var result = await ExecuteAsync(dataSource, uri);
    return result.First();
}

.NET Beispielprogramm

Das oben gezeigte Beispiel ist als vollständiges Visual Studio Projekt umgesetzt. Es enthält die wesentlichen Funktionen zum Lesen, Schreiben, Ändern und Löschen von Ressourcen über die LogMyTime API. Sie können es hier herunterladen.

Weiterführende Informationen

Weiterführende Informationen zum Umgang mit OData-Schnittstellen, und zahlreiche weitere Codebeispiele in C# finden Sie auch in der .NET Dokumentation.