/// <summary>
        /// Trete der Gruppe bei, die durch die angegebene Id repräsentiert wird.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe.</param>
        /// <param name="password">Das Passwort der Gruppe, das für den Request verwendet wird.</param>
        /// <returns>Liefer true, wenn der Beitritt erfolgreich war, ansonsten false.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn der Beitritt fehlschlägt
        ///     oder der Server diesen ablehnt.</exception>
        public async Task<bool> JoinGroupAsync(int groupId, string password)
        {
            HashingHelper.HashingHelper hashHelper = new HashingHelper.HashingHelper();
            string hash = hashHelper.GenerateSHA256Hash(password);

            // Erstelle JSON-Dokument für Passwortübergabe.
            string jsonContent = jsonParser.CreatePasswordResource(hash);

            // Frage zunächst die Gruppen-Ressource vom Server ab.
            Group group = await GetGroupAsync(groupId, false);

            // Setze Request zum Beitreten in die Gruppe ab.
            try
            {
                await groupAPI.SendJoinGroupRequest(
                    getLocalUser().ServerAccessToken,
                    groupId,
                    jsonContent);
            }
            catch (APIException ex)
            {
                Debug.WriteLine("JoinGroupAsync: Failed to join group. Msg is: {0}.", ex.Message);
                throw new ClientException(ex.ErrorCode, ex.Message);
            }

            // Frage die Teilnehmer der Gruppe ab.
            List<User> participants = await GetParticipantsOfGroupAsync(groupId, false);

            try
            {
                // Speichere die Teilnehmer und die Gruppe lokal ab.
                userController.StoreUsersLocally(participants);
                StoreGroupLocally(group);

                // Füge Teilnehmer der Gruppe hinzu. Hierbei wird auch der lokale Nutzer als Teilnehmer eingetragen.
                AddParticipantsToGroup(groupId, participants);
            }
            catch (ClientException ex)
            {
                Debug.WriteLine("JoinGroupAsync: Storing of data has failed. Msg is: {0}.", ex.Message);

                // Lösche Gruppe wieder. Teilnehmer-Referenzen werden durch On Delete Cascade 
                // automatisch gelöscht. Die Nutzer-Ressourcen selbst können gespeichert bleiben.
                DeleteGroupLocally(groupId);

                // Trage den Nutzer wieder von der Gruppe aus.
                await RemoveParticipantFromGroupAsync(groupId, getLocalUser().Id);

                throw new ClientException(ex.ErrorCode, ex.Message);
            }

            // Frage noch Konversationen-Daten ab.
            try
            {
                List<Conversation> conversations = await GetConversationsAsync(groupId, true, false);
                groupDBManager.BulkInsertConversations(groupId, conversations);

                if (conversations != null)
                {
                    foreach (Conversation conversation in conversations)
                    {
                        StoreConversationMessages(groupId, conversation.Id, conversation.ConversationMessages);
                    }
                }
            }
            catch (DatabaseException ex)
            {
                Debug.WriteLine("JoinGroupAsync: Failed to store the conversation data.");
                Debug.WriteLine("JoinGroupAsync: error msg is {0}.", ex.Message);
                // Werfe hier keinen Fehler an Aufrufer. Daten können später einfach nachgeladen werden.
            }
            catch (ClientException ex)
            {
                Debug.WriteLine("JoinGroupAsync: Failed to retrieve the conversation data.");
                Debug.WriteLine("JoinGroupAsync: error msg is {0}.", ex.Message);
                // Werfe hier keinen Fehler an Aufrufer. Daten können später einfach nachgeladen werden.
            }

            // Frage noch die Abstimmungsdaten ab.
            try
            {
                List<Ballot> ballots = await GetBallotsAsync(groupId, true, false);
                StoreBallots(groupId, ballots);
            }
            catch (ClientException ex)
            {
                Debug.WriteLine("JoinGroupAsync: Failed to retrieve or store the ballot data.");
                Debug.WriteLine("JoinGroupAsync: error msg is {0} and error code is {1}.", ex.Message, ex.ErrorCode);
                // Werfe hier keinen Fehler an Aufrufer. Daten können später einfach nachgeladen werden.
            }

            return true;
        }
        /// <summary>
        /// Führt Aktualisierung der Gruppe aus. Es wird ermittelt welche Properties eine Aktualisierung 
        /// erhalten haben. Die Aktualisierungen werden an den Server übermittelt, der die Aktualisierung auf
        /// dem Serverdatensatz ausführt und die Teilnehmer über die Änderung informiert. Aktualisiert lokal jedoch
        /// nicht die Benachrichtigungseinstellungen für die Gruppe. Hierfür gibt es eine separate Funktion.
        /// </summary>
        /// <param name="oldGroup">Der Datensatz der Gruppe vor der Aktualisierung.</param>
        /// <param name="newGroup">Der Datensatz mit aktualisierten Daten.</param>
        /// <param name="ignorePassword">Gibt an, ob das Passwort Property bei der Aktualisierung ignoriert werden soll.</param>
        /// <returns>Liefert true, wenn die Aktualisierung erfolgreich war, ansonsten false.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Fehler während des Aktualisierungsvorgangs auftritt.</exception>
        public async Task<bool> UpdateGroupAsync(Group oldGroup, Group newGroup, bool ignorePassword)
        {
            if (oldGroup == null || newGroup == null)
                return false;

            User localUser = getLocalUser();
            if (localUser == null)
                return false;

            // Gruppen-Admin sollte nicht 0 sein, falls das der Fall ist, setzte ihn auf den alten Wert.
            if (newGroup.GroupAdmin == 0)
            {
                newGroup.GroupAdmin = oldGroup.GroupAdmin;
            }

            // Führe Validierung aus. Wenn Validierung fehlschlägt, kann abgebrochen werden.
            clearValidationErrors();
            newGroup.ClearValidationErrors();
            newGroup.ValidateAll();

            if (newGroup.HasValidationErrors() && !ignorePassword)
            {
                reportValidationErrors(newGroup.GetValidationErrors());
                return false;
            }
            else if (ignorePassword)
            {
                // Prüfe alle Properties bis auf Passwort separat.
                if (newGroup.HasValidationError("Name") ||
                    newGroup.HasValidationError("Term") || 
                    newGroup.HasValidationError("Description"))
                {
                    Dictionary<string, string> validationErrors = newGroup.GetValidationErrors();
                    if (validationErrors.ContainsKey("Password"))
                    {
                        validationErrors.Remove("Password");
                    }
                    reportValidationErrors(validationErrors);
                    return false;
                }
            }

            // Erstelle ein Objekt für die Aktualisierung, welches die Daten enthält, die aktualisiert werden müssen.
            Group updatableGroupObj = prepareUpdatableGroupInstance(oldGroup, newGroup);

            if (updatableGroupObj == null)
                return false;   // Keine Aktualisierung notwendig.

            // Hash Passwort bei Änderung.
            if (updatableGroupObj.Password != null)
            {
                HashingHelper.HashingHelper hashHelper = new HashingHelper.HashingHelper();
                string hashedPassword = hashHelper.GenerateSHA256Hash(updatableGroupObj.Password);
                updatableGroupObj.Password = hashedPassword;
            }

            // Erstelle Json-Dokument für die Aktualisierung.
            string jsonContent = base.jsonParser.ParseGroupToJson(updatableGroupObj);
            if (jsonContent == null)
            {
                Debug.WriteLine("UpdateGroupAsync: Group object could not be translated to a json document.");
                throw new ClientException(ErrorCodes.JsonParserError, "Could not generate json document.");
            }

            // Server Request.
            string serverResponse = null;
            try
            {
                serverResponse = await groupAPI.SendUpdateGroupRequest(
                    getLocalUser().ServerAccessToken,
                    oldGroup.Id,
                    jsonContent);
            }
            catch (APIException ex)
            {
                //Keine explizite Behandlung von GroupNotFound, GroupParticipantNotFound, ... hier, 
                //da nur Admin normal diese Funktionalität ausführen kann.
                Debug.WriteLine("UpdateGroupAsync: Request failed. Error code: {0}, status code: {1}.", ex.ErrorCode, ex.ResponseStatusCode);
                throw new ClientException(ex.ErrorCode, ex.Message);
            }

            // Führe lokale Aktualisierung des Datensatzes aus.
            Group updatedGroup = base.jsonParser.ParseGroupFromJson(serverResponse);
            if (updatedGroup == null)
            {
                throw new ClientException(ErrorCodes.JsonParserError, "Couldn't parse server response.");
            }

            // Speichere neuen Datensatz ab.
            UpdateGroup(updatedGroup, false);

            return true;
        }
        /// <summary>
        /// Anlegen einer neuen Gruppe im System. Die Daten der Gruppe
        /// werden in Form eines Objekts der Klasse Group übergeben. Ein Request
        /// zum Anlegen der Gruppe auf dem Server wird ausgeführt und die erzeugte
        /// Ressource lokal gespeichert.
        /// </summary>
        /// <param name="newGroup">Das Objekt mit den Daten der neuen Gruppe.</param>
        /// <returns>Liefert true, wenn das Anlegen erfolgreich war, ansonsten false.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Erstellungsvorgang fehlschlägt.</exception>
        public async Task<bool> CreateGroupAsync(Group newGroup)
        {
            if (newGroup == null)
                return false;

            // Hole das lokale Nutzerobjekt.
            User localUser = getLocalUser();

            // Setze Nutzer als Gruppenadmin.
            newGroup.GroupAdmin = localUser.Id;

            // Führe Validierung durch.
            clearValidationErrors();
            newGroup.ClearValidationErrors();
            newGroup.ValidateAll();
            if (newGroup.HasValidationErrors())
            {
                // Melde Validierungsfehler.
                reportValidationErrors(newGroup.GetValidationErrors());
                // Breche ab.
                return false;
            }

            // Hashe das Passwort, das der Nutzer eingegeben hat.
            HashingHelper.HashingHelper hashingHelper = new HashingHelper.HashingHelper();
            newGroup.Password = hashingHelper.GenerateSHA256Hash(newGroup.Password);

            // Umwandeln des Objekts in Json.
            string jsonContent = jsonParser.ParseGroupToJson(newGroup);
            if (jsonContent == null)
            {
                Debug.WriteLine("Error during serialization from group object to json string. Could " +
                    "not create a group. Execution is aborted.");
                throw new ClientException(ErrorCodes.JsonParserError, "Failed to generate json document.");
            }

            // Absetzen des Erstellungsrequests.
            string serverResponse = null;
            try
            {
                // Setzte Request zum Anlegen eines Kanals ab.
                serverResponse = await groupAPI.SendCreateGroupRequest(
                    localUser.ServerAccessToken,
                    jsonContent);
            }
            catch (APIException ex)
            {
                // Bilde ab auf ClientException.
                throw new ClientException(ex.ErrorCode, "Server rejected create group request.");
            }

            // Extrahiere Group Objekt aus Antwort des Servers.
            Group responseGroupObj = jsonParser.ParseGroupFromJson(serverResponse);
            if (responseGroupObj != null)
            {
                // Speichere Gruppe lokal ab.
                try
                {
                    // Prüfe, ob Gruppenadmin (lokaler Nutzer in diesem Fall) als User lokal bereits gespeichert ist.
                    if (!userController.IsUserLocallyStored(localUser.Id))
                    {
                        // Speichere lokalen Nutzer in den lokalen Nutzerdatensätzen für Gruppen ab.
                        // Wird benötigt, da sonst Foreign Key constraints verletzt werden beim Speichern der Gruppe.
                        userController.StoreUserLocally(localUser);
                    }

                    groupDBManager.StoreGroup(responseGroupObj);

                    // Füge Teilnehmer der Gruppe hinzu.
                    AddParticipantToGroup(responseGroupObj.Id, getLocalUser());
                }
                catch (DatabaseException ex)
                {
                    Debug.WriteLine("Database Exception, message is: {0}.", ex.Message);
                    // Bilde ab auf ClientException.
                    throw new ClientException(ErrorCodes.LocalDatabaseException, "Storing process of " +
                        "created group object in local DB has failed");
                }   
            }
            else
            {
                throw new ClientException(ErrorCodes.JsonParserError, "Parsing of server response has failed.");
            }

            return true;
        }