/// <summary> /// Aktualisiert den Kanal unter Berücksichtigung der entsprechenden Subklassen-Attribute. /// Es wird abhängig vom Typ des Kanals die Kanal-Tabelle und die Tabellen für die Subklassen aktualisiert. /// </summary> /// <param name="channel">Das Objekt vom Typ Kanal oder vom Typ eines der Unterklassen.</param> /// <exception cref="DatabaseException">Wirft DatabaseException, wenn Aktualisierung des Kanals fehlschlägt.</exception> public void UpdateChannelWithSubclass(Channel channel) { if (channel == null) { Debug.WriteLine("No valid channel object passed to the StoreChannel method."); return; } // Frage das Mutex Objekt ab. Mutex mutex = DatabaseManager.GetDatabaseAccessMutexObject(); // Fordere Zugriff auf die Datenbank an. if (mutex.WaitOne(DatabaseManager.MutexTimeoutValue)) { using (SQLiteConnection conn = DatabaseManager.GetConnection()) { try { // Starte eine Transaktion. using (var statement = conn.Prepare("BEGIN TRANSACTION")) { statement.Step(); } // Setze Aktualisierungsquery für Kanal-Tabelle ab. using (var updateChannelStmt = conn.Prepare("UPDATE Channel " + "SET Name=?, Description=?, CreationDate=?, ModificationDate=?, " + "Type=?, Term=?, Location=?, Dates=?, Contact=?, Website=?, Deleted=?, NotificationSettings_NotifierId=? " + "WHERE Id=?;")) { updateChannelStmt.Bind(1, channel.Name); updateChannelStmt.Bind(2, channel.Description); updateChannelStmt.Bind(3, DatabaseManager.DateTimeToSQLite(channel.CreationDate)); updateChannelStmt.Bind(4, DatabaseManager.DateTimeToSQLite(channel.ModificationDate)); updateChannelStmt.Bind(5, (int)channel.Type); updateChannelStmt.Bind(6, channel.Term); updateChannelStmt.Bind(7, channel.Locations); updateChannelStmt.Bind(8, channel.Dates); updateChannelStmt.Bind(9, channel.Contacts); updateChannelStmt.Bind(10, channel.Website); updateChannelStmt.Bind(11, (channel.Deleted) ? 1 : 0); updateChannelStmt.Bind(12, (int)channel.AnnouncementNotificationSetting); updateChannelStmt.Bind(13, channel.Id); updateChannelStmt.Step(); Debug.WriteLine("Update channel with id {0}.", channel.Id); } // Aktualisiere auch Subklassen-Tabelle abhängig vom Typ des Kanals. switch (channel.Type) { case DataModel.Enums.ChannelType.LECTURE: Lecture lecture = (Lecture)channel; // Aktualisierung von Vorlesungs-Tabelle. using (var updateLectureStmt = conn.Prepare("UPDATE Lecture SET " + "StartDate=?, EndDate=?, Lecturer=?, Assistant=? " + "WHERE Channel_Id=?;")) { updateLectureStmt.Bind(1, lecture.StartDate); updateLectureStmt.Bind(2, lecture.EndDate); updateLectureStmt.Bind(3, lecture.Lecturer); updateLectureStmt.Bind(4, lecture.Assistant); updateLectureStmt.Bind(5, channel.Id); updateLectureStmt.Step(); } Debug.WriteLine("Updated lecture."); break; case DataModel.Enums.ChannelType.EVENT: Event channelEvent = (Event)channel; // Aktualisierung von Event-Tabelle. using (var updateEventStmt = conn.Prepare("UPDATE Event SET Cost=?, Organizer=? " + "WHERE Channel_Id=?;")) { updateEventStmt.Bind(1, channelEvent.Cost); updateEventStmt.Bind(2, channelEvent.Organizer); updateEventStmt.Bind(3, channel.Id); updateEventStmt.Step(); } Debug.WriteLine("Updated event."); break; case DataModel.Enums.ChannelType.SPORTS: Sports channelSports = (Sports)channel; // Aktualisierung von Sports-Tabelle. using (var updateSportsStmt = conn.Prepare("UPDATE Sports SET Cost=?, NumberOfParticipants=? " + "WHERE Channel_Id=?;")) { updateSportsStmt.Bind(1, channelSports.Cost); updateSportsStmt.Bind(2, channelSports.NumberOfParticipants); updateSportsStmt.Bind(3, channel.Id); updateSportsStmt.Step(); } Debug.WriteLine("Updated sports."); break; default: Debug.WriteLine("There is no subclass for channel type OTHER and STUDENT_GROUP, so updating is already complete."); break; } // Commit der Transaktion. using (var statement = conn.Prepare("COMMIT TRANSACTION")) { statement.Step(); } } catch (SQLiteException sqlEx) { Debug.WriteLine("SQLiteException has occurred in UpdateChannelWithSubclass. The message is: {0}." + sqlEx.Message); // Rollback der Transaktion. using (var statement = conn.Prepare("ROLLBACK TRANSACTION")) { statement.Step(); } throw new DatabaseException("Update of the channel with id " + channel.Id + " has failed."); } catch (Exception ex) { Debug.WriteLine("Exception has occurred in UpdateChannelWithSubclass. The message is: {0}, " + "and the stack trace is: {1}.", ex.Message, ex.StackTrace); // Rollback der Transaktion. using (var statement = conn.Prepare("ROLLBACK TRANSACTION")) { statement.Step(); } throw new DatabaseException("Update of channel has failed."); } finally { mutex.ReleaseMutex(); } } // Ende using Block. } else { Debug.WriteLine("Couldn't get access to database. Time out."); throw new DatabaseException("Could not get access to the database."); } }
/// <summary> /// Hilfsmethode, die aus einem durch eine Query zurückgelieferten Statement ein Objekt des Typs Kanal extrahiert. /// Je nach Typ des Kanals werden zusätzliche Informationen aus Subklassen-Tabellen abgefragt und ein Objekt /// der Subklasse extrahiert. /// </summary> /// <param name="conn">Eine aktive Connection zur Datenbank, um Informationen aus Subklassen-Tabellen abfragen zu können.</param> /// <param name="stmt">Das Statement, aus dem die Informationen extrahiert werden sollen.</param> /// <returns>Ein Objekt vom Typ Channel.</returns> private Channel retrieveChannelObjectFromStatement(SQLiteConnection conn, ISQLiteStatement stmt) { // Channel Objekt. Channel channel = null; try { // Initialisierung der Variablen. int id; string name, description, term, location, dates, contact, website, startDate, endDate, lecturer, assistant, cost, organizer, participants; bool deleted; ChannelType type; Faculty faculty; NotificationSetting announcementNotificationSetting; DateTimeOffset creationDate, modificationDate; // Frage Kanal-Werte ab. id = Convert.ToInt32(stmt["Id"]); name = (string)stmt["Name"]; description = (string)stmt["Description"]; type = (ChannelType)Enum.ToObject(typeof(ChannelType), stmt["Type"]); creationDate = DatabaseManager.DateTimeFromSQLite(stmt["CreationDate"].ToString()); modificationDate = DatabaseManager.DateTimeFromSQLite(stmt["ModificationDate"].ToString()); term = (string)stmt["Term"]; location = (string)stmt["Location"]; dates = (string)stmt["Dates"]; contact = (string)stmt["Contact"]; website = (string)stmt["Website"]; deleted = ((long)stmt["Deleted"] == 1) ? true : false; announcementNotificationSetting = (NotificationSetting)Enum.ToObject(typeof(NotificationSetting), stmt["NotificationSettings_NotifierId"]); // Falls notwendig, hole Daten aus Tabelle der Subklasse. switch (type) { case ChannelType.LECTURE: using (var getLectureStmt = conn.Prepare("SELECT * FROM Lecture WHERE Channel_Id=?;")) { getLectureStmt.Bind(1, id); // Hole Ergebnis der Query. if (getLectureStmt.Step() == SQLiteResult.ROW) { faculty = (Faculty)Enum.ToObject(typeof(Faculty), getLectureStmt["Faculty"]); startDate = (string)getLectureStmt["StartDate"]; endDate = (string)getLectureStmt["EndDate"]; lecturer = (string)getLectureStmt["Lecturer"]; assistant = (string)getLectureStmt["Assistant"]; // Erstelle Lecture Objekt und füge es der Liste hinzu. Lecture lecture = new Lecture(id, name, description, type, creationDate, modificationDate, term, location, dates, contact, website, deleted, faculty, startDate, endDate, lecturer, assistant); channel = lecture; } } break; case ChannelType.EVENT: using (var getEventStmt = conn.Prepare("SELECT * FROM Event WHERE Channel_Id=?;")) { getEventStmt.Bind(1, id); // Hole Ergebnis der Query. if (getEventStmt.Step() == SQLiteResult.ROW) { cost = (string)getEventStmt["Cost"]; organizer = (string)getEventStmt["Organizer"]; // Erstelle Event Objekt und füge es der Liste hinzu. Event eventObj = new Event(id, name, description, type, creationDate, modificationDate, term, location, dates, contact, website, deleted, cost, organizer); channel = eventObj; } } break; case ChannelType.SPORTS: using (var getSportsStmt = conn.Prepare("SELECT * FROM Sports WHERE Channel_Id=?;")) { getSportsStmt.Bind(1, id); // Hole Ergebnis der Query. if (getSportsStmt.Step() == SQLiteResult.ROW) { cost = (string)getSportsStmt["Cost"]; participants = (string)getSportsStmt["NumberOfParticipants"]; // Erstelle Sports Objekt und füge es der Liste hinzu. Sports sportsObj = new Sports(id, name, description, type, creationDate, modificationDate, term, location, dates, contact, website, deleted, cost, participants); channel = sportsObj; } } break; default: // Keine Subklasse, also erzeuge Kanal Objekt. channel = new Channel(id, name, description, type, creationDate, modificationDate, term, location, dates, contact, website, deleted); break; } // Füge announcementNotificationSetting Einstellung noch dem Objekt hinzu. channel.AnnouncementNotificationSetting = announcementNotificationSetting; } catch(SQLiteException sqlEx){ Debug.WriteLine("SQLiteException has occurred in retrieveChannelObjectFromStatement. The message is: {0}.", sqlEx.Message); throw new DatabaseException("Retrieve channel has failed."); } catch(Exception ex) { Debug.WriteLine("Exception has occurred in retrieveChannelObjectFromStatement. The message is: {0}, " + "and the stack trace: {1}.", ex.Message, ex.StackTrace); throw new DatabaseException("Retrieve channel has failed."); } return channel; }
/// <summary> /// Aktualisiere den Kanal. Bei dieser Methode werden nur die Kanalattribute in der Datenbank /// aktualisiert. Mögliche Attribute von Subklassen der Kanal Klasse werden ignoriert. /// </summary> /// <param name="channel">Eine Objektinstanz von Kanal mit den aktualisierten Daten für den Kanal.</param> /// <exception cref="DatabaseException">Wirft DatabaseException, wenn Aktualisierung des Kanals fehlschlägt.</exception> public void UpdateChannel(Channel channel) { if (channel == null) { Debug.WriteLine("No valid channel object passed to the UpdateChannel method."); return; } // Frage das Mutex Objekt ab. Mutex mutex = DatabaseManager.GetDatabaseAccessMutexObject(); // Fordere Zugriff auf die Datenbank an. if (mutex.WaitOne(DatabaseManager.MutexTimeoutValue)) { using (SQLiteConnection conn = DatabaseManager.GetConnection()) { try { // Aktualisiere Daten in Kanal-Tabelle. using (var updateChannelStmt = conn.Prepare("UPDATE Channel SET Name=?, Description=?, " + "CreationDate=?, ModificationDate=?, Type=?, Term=?, Location=?, Dates=?, Contact=?, " + "Website=?, Deleted=?, NotificationSettings_NotifierId=? " + "WHERE Id=?;")) { updateChannelStmt.Bind(1, channel.Name); updateChannelStmt.Bind(2, channel.Description); updateChannelStmt.Bind(3, DatabaseManager.DateTimeToSQLite(channel.CreationDate)); updateChannelStmt.Bind(4, DatabaseManager.DateTimeToSQLite(channel.ModificationDate)); updateChannelStmt.Bind(5, (int)channel.Type); updateChannelStmt.Bind(6, channel.Term); updateChannelStmt.Bind(7, channel.Locations); updateChannelStmt.Bind(8, channel.Dates); updateChannelStmt.Bind(9, channel.Contacts); updateChannelStmt.Bind(10, channel.Website); updateChannelStmt.Bind(11, (channel.Deleted) ? 1 : 0); updateChannelStmt.Bind(12, (int)channel.AnnouncementNotificationSetting); updateChannelStmt.Bind(13, channel.Id); updateChannelStmt.Step(); Debug.WriteLine("Update channel with id {0}.", channel.Id); } } catch (SQLiteException sqlEx) { Debug.WriteLine("SQLiteException has occurred in UpdateChannel. The message is: {0}." + sqlEx.Message); throw new DatabaseException("Update of the channel with id " + channel.Id + " has failed."); } catch (Exception ex) { Debug.WriteLine("Exception has occurred in UpdateChannel. The message is: {0}, " + "and the stack trace is: {1}.", ex.Message, ex.StackTrace); throw new DatabaseException("Update of channel has failed."); } finally { mutex.ReleaseMutex(); } } // Ende using Block. } else { Debug.WriteLine("Couldn't get access to database. Time out."); throw new DatabaseException("Could not get access to the database."); } }
/// <summary> /// Aktualisiert die für die View relevanten Properties des im ViewModel verwalteten Kanals. /// </summary> /// <param name="currentChannel">Der aktuell im ViewModel verwaltete Kanal.</param> /// <param name="newChannel">Der Kanal mit den aktualisierten Daten.</param> private void updateViewRelatedPropeties(Channel currentChannel, Channel newChannel) { currentChannel.Name = newChannel.Name; currentChannel.Term = newChannel.Term; }
/// <summary> /// Speichere einen Kanal in der lokalen Datenbank. /// </summary> /// <param name="channel">Das Kanal Objekt mit den Daten des Kanals.</param> /// <exception cref="DatabaseException">Wirft DatabaseException, wenn die Kanaldaten nicht in der DB gespeichert werden konnten.</exception> public void StoreChannel(Channel channel) { if(channel == null) { Debug.WriteLine("No valid channel object passed to the StoreChannel method."); return; } // Frage das Mutex Objekt ab. Mutex mutex = DatabaseManager.GetDatabaseAccessMutexObject(); // Fordere Zugriff auf die Datenbank an. if (mutex.WaitOne(DatabaseManager.MutexTimeoutValue)) { using (SQLiteConnection conn = DatabaseManager.GetConnection()) { try { // Starte eine Transaktion. using (var statement = conn.Prepare("BEGIN TRANSACTION")) { statement.Step(); } // Speichere Kanaldaten. using (var insertStmt = conn.Prepare("INSERT INTO Channel (Id, Name, Description, " + "CreationDate, ModificationDate, Type, Term, Location, Dates, Contact, Website, Deleted, NotificationSettings_NotifierId) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")) { insertStmt.Bind(1, channel.Id); insertStmt.Bind(2, channel.Name); insertStmt.Bind(3, channel.Description); insertStmt.Bind(4, DatabaseManager.DateTimeToSQLite(channel.CreationDate)); insertStmt.Bind(5, DatabaseManager.DateTimeToSQLite(channel.ModificationDate)); insertStmt.Bind(6, (int)channel.Type); insertStmt.Bind(7, channel.Term); insertStmt.Bind(8, channel.Locations); insertStmt.Bind(9, channel.Dates); insertStmt.Bind(10, channel.Contacts); insertStmt.Bind(11, channel.Website); insertStmt.Bind(12, (channel.Deleted) ? 1 : 0); // Channel hat zu Begin immer Default Benachrichtigungseinstellungen. channel.AnnouncementNotificationSetting = NotificationSetting.APPLICATION_DEFAULT; insertStmt.Bind(13, (int)channel.AnnouncementNotificationSetting); insertStmt.Step(); } // Speichere Subklassen Parameter abhängig vom Typ des Kanals. switch (channel.Type) { case DataModel.Enums.ChannelType.LECTURE: Lecture lecture = (Lecture)channel; // Speichere Vorlesungsdaten. using (var insertLectureStmt = conn.Prepare("INSERT INTO Lecture (Channel_Id, " + "Faculty, StartDate, EndDate, Lecturer, Assistant) " + "VALUES (?, ?, ?, ?, ?, ?);")) { insertLectureStmt.Bind(1, channel.Id); insertLectureStmt.Bind(2, (int)lecture.Faculty); insertLectureStmt.Bind(3, lecture.StartDate); insertLectureStmt.Bind(4, lecture.EndDate); insertLectureStmt.Bind(5, lecture.Lecturer); insertLectureStmt.Bind(6, lecture.Assistant); insertLectureStmt.Step(); } Debug.WriteLine("Stored lecture channel."); break; case DataModel.Enums.ChannelType.EVENT: Event eventChannel = (Event)channel; // Speichere Eventdaten. using (var insertEventStmt = conn.Prepare("INSERT INTO Event (Channel_Id, Cost, Organizer) " + "VALUES (?, ?, ?);")) { insertEventStmt.Bind(1, channel.Id); insertEventStmt.Bind(2, eventChannel.Cost); insertEventStmt.Bind(3, eventChannel.Organizer); insertEventStmt.Step(); } Debug.WriteLine("Stored event channel."); break; case DataModel.Enums.ChannelType.SPORTS: Sports sportsChannel = (Sports)channel; // Speichere Sportgruppendaten. using (var insertSportsStmt = conn.Prepare("INSERT INTO Sports (Channel_Id, Cost, NumberOfParticipants) " + "VALUES (?, ?, ?);")) { insertSportsStmt.Bind(1, channel.Id); insertSportsStmt.Bind(2, sportsChannel.Cost); insertSportsStmt.Bind(3, sportsChannel.NumberOfParticipants); insertSportsStmt.Step(); } Debug.WriteLine("Stored sports channel."); break; default: Debug.WriteLine("There is no subclass for channel type OTHER and STUDENT_GROUP, so storing is already complete."); break; } // Commit der Transaktion. using (var statement = conn.Prepare("COMMIT TRANSACTION")) { statement.Step(); } } catch (SQLiteException sqlEx) { Debug.WriteLine("SQLiteException has occurred in StoreChannel. Exception message is: {0}.", sqlEx.Message); // Rollback der Transaktion. using (var statement = conn.Prepare("ROLLBACK TRANSACTION")) { statement.Step(); } throw new DatabaseException("Storing channel data in database has failed."); } catch (Exception ex) { Debug.WriteLine("Exception has occurred in StoreChannel. " + "Exception message is: {0}, and stack trace is {1}.", ex.Message, ex.StackTrace); // Rollback der Transaktion. using (var statement = conn.Prepare("ROLLBACK TRANSACTION")) { statement.Step(); } throw new DatabaseException("Storing channel data in database has failed."); } finally { mutex.ReleaseMutex(); } } // Ende using block. } else { Debug.WriteLine("Couldn't get access to database. Time out."); throw new DatabaseException("Could not get access to the database."); } }
/// <summary> /// Aktualisiert die für die View relevanten Properties eines aktuell vom ViewModel gehaltenen /// Kanal-Objekts. /// </summary> /// <param name="currentChannel">Das aktuell vom ViewModel gehaltene Channel-Objekt.</param> /// <param name="newChannel">Das Channel-Objekt mit den aktualisierten Daten.</param> private void updateViewRelatedChannelProperties(Channel currentChannel, Channel newChannel) { currentChannel.Name = newChannel.Name; currentChannel.Description = newChannel.Description; currentChannel.Term = newChannel.Term; currentChannel.CreationDate = newChannel.CreationDate; currentChannel.ModificationDate = newChannel.ModificationDate; currentChannel.Locations = newChannel.Locations; currentChannel.Dates = newChannel.Dates; currentChannel.Contacts = newChannel.Contacts; currentChannel.Website = newChannel.Website; switch (currentChannel.Type) { case ChannelType.LECTURE: Lecture currentLecture = currentChannel as Lecture; Lecture newLecture = newChannel as Lecture; currentLecture.StartDate = newLecture.StartDate; currentLecture.EndDate = newLecture.EndDate; currentLecture.Lecturer = newLecture.Lecturer; currentLecture.Assistant = newLecture.Assistant; break; case ChannelType.EVENT: Event currentEvent = currentChannel as Event; Event newEvent = newChannel as Event; currentEvent.Cost = newEvent.Cost; currentEvent.Organizer = newEvent.Organizer; break; case ChannelType.SPORTS: Sports currentSportsObj = currentChannel as Sports; Sports newSportsObj = newChannel as Sports; currentSportsObj.Cost = newSportsObj.Cost; currentSportsObj.NumberOfParticipants = newSportsObj.NumberOfParticipants; break; } }
/// <summary> /// Hilfsmethode, welche die Validierung der Properties eines Kanalobjekts ausführt. /// Die Properties werden unter Berücksichtigung des Typs des Kanals und möglicher /// Properties entsprechender Subklassen ausgeführt. Falls Validierungsfehler auftreten, /// werden die über die Report-Schnittstelle an das ViewModel gemeldet. /// </summary> /// <param name="channel">Das Kanalobjekt, das validiert werden soll.</param> /// <returns>Liefert true, wenn keine Validierungsfehler aufgetreten sind, liefert false bei Validierungsfehlern.</returns> private bool validateChannelProperties(Channel channel) { switch (channel.Type) { case ChannelType.LECTURE: Lecture lecture = channel as Lecture; if (lecture != null) { lecture.ClearValidationErrors(); lecture.ValidateAll(); } if (lecture.HasValidationErrors()) { reportValidationErrors(lecture.GetValidationErrors()); return false; } break; case ChannelType.EVENT: Event eventObj = channel as Event; if (eventObj != null) { eventObj.ClearValidationErrors(); eventObj.ValidateAll(); } if (eventObj.HasValidationErrors()) { reportValidationErrors(eventObj.GetValidationErrors()); return false; } break; case ChannelType.SPORTS: Sports sportObj = channel as Sports; if (sportObj != null) { sportObj.ClearValidationErrors(); sportObj.ValidateAll(); } if (sportObj.HasValidationErrors()) { reportValidationErrors(sportObj.GetValidationErrors()); return false; } break; default: channel.ClearValidationErrors(); channel.ValidateAll(); if (channel.HasValidationErrors()) { reportValidationErrors(channel.GetValidationErrors()); return false; } break; } return true; }
/// <summary> /// Fügt einen Kanal den lokal verwalteten Kanaldatensätzen hinzu. /// </summary> /// <param name="newChannel">Der hinzuzufügende Kanal.</param> /// <exception cref="ClientException">Wirft ClientException, wenn Hinzufügen fehlschlägt.</exception> public void AddToLocalChannels(Channel newChannel) { try { channelDatabaseManager.StoreChannel(newChannel); } catch (DatabaseException ex) { Debug.WriteLine("Couldn't store channel."); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } }
/// <summary> /// Legt einen neuen Kanal an. Die Daten des Kanals werden in Form eines /// Channel Objekts übergeben. Es wird ein Request an den Server übermittelt, um /// auf dem Server eine neue Kanalressource anzulegen. /// </summary> /// <param name="newChannel">Die Daten des neu anzulegenden Kanals in Form eines Objekts der Channel Klasse.</param> /// <returns>Liefert true, wenn der Kanal erfolgreich angelegt wurde, ansonsten false.</returns> /// <exception cref="ClientException">Wirft eine ClientException, wenn ein Fehler während des Erstellungsvorgangs auftritt.</exception> public async Task<bool> CreateChannelAsync(Channel newChannel) { if (newChannel == null) return false; Moderator activeModerator = GetLocalModerator(); if (activeModerator == null) return false; // Führe Validierung der Kanaldaten durch. Abbruch bei aufgetretenen Validierungsfehlern. clearValidationErrors(); bool validationSuccessful = validateChannelProperties(newChannel); // Breche ab, wenn Validierungsfehler aufgetreten sind. if (!validationSuccessful) return false; // Generiere JSON-Dokument aus Objekt. string jsonContent = jsonParser.ParseChannelToJsonString(newChannel); if (jsonContent == null) { Debug.WriteLine("Error during serialization from channel object to json string. Could " + "not create a channel. Execution is aborted."); return false; } string serverResponse = null; try { // Setzte Request zum Anlegen eines Kanals ab. serverResponse = await channelApi.SendCreateChannelRequestAsync( activeModerator.ServerAccessToken, jsonContent); } catch (APIException ex) { // Bilde ab auf ClientException. throw new ClientException(ex.ErrorCode, "Server rejected create channel request."); } // Extrahiere erhaltene Channel Resource aus JSON-Dokument. Channel createdChannel = jsonParser.ParseChannelFromJson(serverResponse); if (createdChannel != null) { try { // Speichere Kanal lokal ab. channelDatabaseManager.StoreChannel(createdChannel); // Füge den Moderator als verwantwortlichen Moderator hinzu. channelDatabaseManager.AddModeratorToChannel( createdChannel.Id, activeModerator.Id, true); } 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 channel object in local DB has failed"); } } else { throw new ClientException(ErrorCodes.JsonParserError, "Parsing of server response has failed."); } return true; }
/// <summary> /// Ersetzt die lokal verwaltete Version des Kanals durch eine neue /// Version desselben Kanals. /// </summary> /// <param name="newChannel">Die neue Version des Kanals.</param> ///<exception cref="ClientException">Wirft ClientException, wenn Aktion aufgrund eines /// Fehlers nicht ausgeführt werden kann.</exception> public void ReplaceLocalChannel(Channel newChannel) { try { channelDatabaseManager.UpdateChannelWithSubclass(newChannel); } catch (DatabaseException ex) { Debug.WriteLine("Couldn't perform the replace local channel functionality."); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } }
/// <summary> /// Ersetzt einen lokalen Kanal unter Beibehaltung der vom Nutzer eingestellten Anwendungseinstellungen. /// </summary> /// <param name="newChannel">Die neue Kanalressource, welche die alte Ressource lokal ersetzt.</param> public void ReplaceLocalChannelWhileKeepingNotificationSettings(Channel newChannel) { try { Channel oldChannel = channelDatabaseManager.GetChannel(newChannel.Id); // Übernehme NotificationSettings von lokalem Kanal. newChannel.AnnouncementNotificationSetting = oldChannel.AnnouncementNotificationSetting; // Ersetze lokalen Datensatz durch neuen Datensatz. ReplaceLocalChannel(newChannel); } catch (DatabaseException ex) { Debug.WriteLine("ReplaceLocalChannelWhileKeepingNotificationSettings: Error occurred!"); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } }
/// <summary> /// Prüft, ob alle Beziehungen zwischen Kanalressourcen und dem aktuell eingeloggten /// Moderator aktuell sind und aktualisiert diese falls notwendig. /// </summary> /// <param name="managedChannels">Liste von aktuell verwalteten Kanälen des Moderators.</param> /// <exception cref="ClientException">Wirft ClientException, wenn Aktualisierung fehlschlägt.</exception> //public void UpdateManagedChannelsRelationships(List<Channel> managedChannels) //{ // Moderator activeModerator = GetLocalModerator(); // if (activeModerator == null) // return; // try // { // // Prüfe, ob der aktuell eingeloggte Moderator schon in der Datenbank enthalten ist. // if (!moderatorDatabaseManager.IsModeratorStored(activeModerator.Id)) // { // Debug.WriteLine("Need to store the moderator with id {0} in local DB.", activeModerator.Id); // moderatorDatabaseManager.StoreModerator(activeModerator); // } // // Prüfe, ob Moderator auch lokal als Verantwortlicher für Kanäle eingetragen ist. // foreach (Channel channel in managedChannels) // { // if (!channelDatabaseManager.IsResponsibleForChannel(channel.Id, activeModerator.Id)) // { // // Kanal gefunden, der vorher noch nicht von diesem Moderator verwaltet wurde. // // Trage Moderator ein. // Debug.WriteLine("Need to add the moderator with id {0} as a responsible moderator " + // "for the channel with id {1}.", activeModerator.Id, channel.Id); // channelDatabaseManager.AddModeratorToChannel(channel.Id, activeModerator.Id, true); // // Stoße das Herunterladen der für den neu hinzugekommenen Kanal relevanten Daten an. // // Bemerkung: Falls das fehlschlägt wird kein Fehler geworfen. Die Daten können auch im Fehlerfall später nachgeladen werden. // Task.Run(() => retrieveAndStoreManagedChannelInfoAsync(activeModerator, channel.Id)); // } // } // // Frage verwaltete Kanäle aus der DB ab. // List<Channel> managedChannelsFromDB = channelDatabaseManager.GetManagedChannels(activeModerator.Id); // // Prüfe, ob es darin noch einen Kanal gibt, der nicht mehr in der aktuellen Liste von Kanälen steht. // for (int i = 0; i < managedChannelsFromDB.Count; i++) // { // bool isContained = false; // foreach (Channel channel in managedChannels) // { // if (channel.Id == managedChannelsFromDB[i].Id) // { // isContained = true; // } // } // if (!isContained) // { // RemoveChannelFromManagedChannels(activeModerator, managedChannelsFromDB[i]); // } // } // } // catch (DatabaseException ex) // { // Debug.WriteLine("Database exception occurred in UpdateManagedChannelsRelationships. Msg is {0}.", ex.Message); // throw new ClientException(ErrorCodes.LocalDatabaseException, "Failed to update managed channels relationships."); // } //} /// <summary> /// Nimmt einen Kanal aus der Liste der verwalteten Kanäle raus und räumt /// mit diesem Kanal in Verbindung stehende Ressourcen weg, die dann nicht mehr /// benötigt werden. /// </summary> /// <param name="activeModerator">Der gerade aktive Moderator, für den der Kanal aus der Liste der /// verwalteten Kanäle ausgetragen wird.</param> /// <param name="channel">Der Kanal, der aus der Liste genommen wird.</param> public void RemoveChannelFromManagedChannels(Moderator activeModerator, Channel channel) { try { // Setzte Verantwortlichkeit auf inaktiv für diesen Kanal. Debug.WriteLine("Need to set moderator isActive to false for channel with id {0}.", channel.Id); channelDatabaseManager.AddModeratorToChannel( channel.Id, activeModerator.Id, false); // Prüfe, ob der Kanal abonniert ist. bool isSubscribed = channelDatabaseManager.IsChannelSubscribed(channel.Id); if (!isSubscribed) { // Kanal nicht noch vom lokalen Nutzer abonniert. Lösche Announcements und Moderator-Info. Debug.WriteLine("Channel with id {0} not subscribed. Delete announcements and moderator info.", channel.Id); channelDatabaseManager.DeleteAllAnnouncementsOfChannel(channel.Id); channelDatabaseManager.RemoveAllModeratorsFromChannel(channel.Id); } // Lösche in jedem Fall die Reminder. Debug.WriteLine("Deleting reminders for channel with id {0}.", channel.Id); channelDatabaseManager.DeleteRemindersForChannel(channel.Id); } catch (DatabaseException ex) { // Keine weitere Aktion, da diese Funktionalität im Hintergrund abläuft und // nicht vom Nutzer aktiv ausgelöst wird. Debug.WriteLine("Error during removal of managed channel. No further action is taken." + "Error message is: {0}.", ex.Message); } }
/// <summary> /// Fügt den übergebenen Kanal der Liste der verwalteten Kanäle hinzu für den übergebenen /// Moderator. Trägt dabei den übergebenen Moderator als Verantwortlichen für den übergebenen Kanal /// ein und schaut ob der lokale Datensatz des Kanals hinzugefügt oder aktualisiert werden muss. /// </summary> /// <param name="moderator">Der Moderator, für den der Kanal zur Liste der verwalteten Kanäle hinzukommt.</param> /// <param name="newManagedChannel">Der hinzuzufügende Kanal.</param> public void AddChannelToLocalManagedChannels(Moderator moderator, Channel newManagedChannel) { if (moderator == null || newManagedChannel == null) return; try { // Prüfe, ob der aktuell eingeloggte Moderator schon in der Datenbank enthalten ist. // Falls nicht, speichere ihn ab. if (!moderatorDatabaseManager.IsModeratorStored(moderator.Id)) { Debug.WriteLine("SynchronizeLocalManagedChannels: Need to store the moderator with id {0} in local DB.", moderator.Id); moderatorDatabaseManager.StoreModerator(moderator); } // Prüfe ob Kanal schon in lokalen Datensätzen vorhanden ist. if (!channelDatabaseManager.IsChannelContained(newManagedChannel.Id)) { Debug.WriteLine("AddChannelToLocalManagedChannels: Adding channel with id {0} to local channels.", newManagedChannel.Id); // Wenn nicht, füge Kanal hinzu. AddToLocalChannels(newManagedChannel); } else { // Frage lokale Version ab und prüfe, ob diese aktualisiert werden muss. Channel localChannel = GetChannel(newManagedChannel.Id); if (DateTimeOffset.Compare(localChannel.ModificationDate, newManagedChannel.ModificationDate) < 0) { Debug.WriteLine("SynchronizeLocalManagedChannels: Need to update channel with id {0}.", localChannel.Id); // Übernehme NotificationSettings von lokalem Kanal. newManagedChannel.AnnouncementNotificationSetting = localChannel.AnnouncementNotificationSetting; // Ersetze lokalen Datensatz durch neuen Datensatz. ReplaceLocalChannel(newManagedChannel); } } // Füge den Moderator als Verantwortlichen für den Kanal hinzu. channelDatabaseManager.AddModeratorToChannel(newManagedChannel.Id, moderator.Id, true); // Stoße das Herunterladen der für den neu hinzugekommenen Kanal relevanten Daten an. // Bemerkung: Falls das fehlschlägt wird kein Fehler geworfen. Die Daten können auch im Fehlerfall später nachgeladen werden. Task.Run(() => retrieveAndStoreManagedChannelInfoAsync(moderator, newManagedChannel.Id)); } catch (DatabaseException ex) { Debug.WriteLine("Couldn't add channel to local managed channels. Msg is {0}.", ex.Message); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } }
/// <summary> /// Bereitet ein Objekt vom Typ Channel vor, welches alle Properties enthält, die sich geändert haben. /// Die Methode bekommt eine alte Version eines Channel Objekts und eine neue Version und ermittelt /// dann die Properties, die eine Aktualisierung erhalten haben und schreibt diese in eine neue Channel /// Instanz. Die von der Methode zurückgelieferte Channel Instanz kann dann direkt für die Aktualisierung auf /// dem Server verwendet werden. Achtung: Hat sich überhaupt keine Property geändert, so gibt die Methode null zurück. /// </summary> /// <param name="oldChannel">Das Channel Objekt vor der Aktualisierung.</param> /// <param name="newChannel">Das Channel Objekt mit den aktuellen Werten.</param> /// <returns>Ein Objekt der Klasse Channel, bei dem die Properties, die sich geändert haben, mit den /// aktualisierten Werten gefüllt sind.</returns> private Channel prepareUpdatableChannelInstance(Channel oldChannel, Channel newChannel) { bool hasChanged = false; Channel updatedChannel = new Channel(); // Vergleiche zunächst Properties der allgemeinen Channel Klasse. if (oldChannel.Name != newChannel.Name) { hasChanged = true; updatedChannel.Name = newChannel.Name; } if (oldChannel.Description != newChannel.Description) { hasChanged = true; updatedChannel.Description = newChannel.Description; } if (oldChannel.Term != newChannel.Term) { hasChanged = true; updatedChannel.Term = newChannel.Term; } if (oldChannel.Locations != newChannel.Locations) { hasChanged = true; updatedChannel.Locations = newChannel.Locations; } if (oldChannel.Dates != newChannel.Dates) { hasChanged = true; updatedChannel.Dates = newChannel.Dates; } if (oldChannel.Contacts != newChannel.Contacts) { hasChanged = true; updatedChannel.Contacts = newChannel.Contacts; } if (oldChannel.Website != newChannel.Website) { hasChanged = true; updatedChannel.Website = newChannel.Website; } // Vergleiche, ob kanalspezifische Felder sich geändert haben bei den Kanälen eines Typs mit solchen Feldern. if (oldChannel.Type == newChannel.Type) { switch (oldChannel.Type) { case ChannelType.LECTURE: Lecture updatedLecture = new Lecture() { Name = updatedChannel.Name, Description = updatedChannel.Description, Type = ChannelType.LECTURE, Term = updatedChannel.Term, Locations = updatedChannel.Locations, Dates = updatedChannel.Dates, Contacts = updatedChannel.Contacts, Website = updatedChannel.Website }; Lecture oldLecture = oldChannel as Lecture; Lecture newLecture = newChannel as Lecture; if (oldLecture.Lecturer != newLecture.Lecturer) { hasChanged = true; updatedLecture.Lecturer = newLecture.Lecturer; } if (oldLecture.Assistant != newLecture.Assistant) { hasChanged = true; updatedLecture.Assistant = newLecture.Assistant; } if (oldLecture.Faculty != newLecture.Faculty) { hasChanged = true; updatedLecture.Faculty = newLecture.Faculty; } if (oldLecture.StartDate != newLecture.StartDate) { hasChanged = true; updatedLecture.StartDate = newLecture.StartDate; } if (oldLecture.EndDate != newLecture.EndDate) { hasChanged = true; updatedLecture.EndDate = newLecture.EndDate; } // Setze updatedChannel neu. updatedChannel = updatedLecture; break; case ChannelType.EVENT: Event updatedEvent = new Event() { Name = updatedChannel.Name, Description = updatedChannel.Description, Type = ChannelType.EVENT, Term = updatedChannel.Term, Locations = updatedChannel.Locations, Dates = updatedChannel.Dates, Contacts = updatedChannel.Contacts, Website = updatedChannel.Website }; Event oldEvent = oldChannel as Event; Event newEvent = newChannel as Event; if (oldEvent.Cost != newEvent.Cost) { hasChanged = true; updatedEvent.Cost = newEvent.Cost; } if (oldEvent.Organizer != newEvent.Organizer) { hasChanged = true; updatedEvent.Organizer = newEvent.Organizer; } // Setze updatedChannel neu. updatedChannel = updatedEvent; break; case ChannelType.SPORTS: Sports updatedSport = new Sports() { Name = updatedChannel.Name, Description = updatedChannel.Description, Type = ChannelType.SPORTS, Term = updatedChannel.Term, Locations = updatedChannel.Locations, Dates = updatedChannel.Dates, Contacts = updatedChannel.Contacts, Website = updatedChannel.Website }; Sports oldSport = oldChannel as Sports; Sports newSport = newChannel as Sports; if (oldSport.Cost != newSport.Cost) { hasChanged = true; updatedSport.Cost = newSport.Cost; } if (oldSport.NumberOfParticipants != newSport.NumberOfParticipants) { hasChanged = true; updatedSport.NumberOfParticipants = newSport.NumberOfParticipants; } // Setze updatedChannel neu. updatedChannel = updatedSport; break; case ChannelType.OTHER: updatedChannel.Type = ChannelType.OTHER; break; case ChannelType.STUDENT_GROUP: updatedChannel.Type = ChannelType.STUDENT_GROUP; break; } } // Prüfe, ob sich überhaupt eine Property geändert hat. if (!hasChanged) { Debug.WriteLine("No Property of channel has been updated. Method will return null."); updatedChannel = null; } return updatedChannel; }
/// <summary> /// Hilfsmethode, welche aus den eingegebenen Daten ein Objekt /// vom Typ Channel erstellt. Eventuelle Subklassen-Attribute /// werden ebenfalls gesetzt. /// </summary> /// <returns>Ein Objekt der Klasse Channel, welches die vom Nutzer eingegebenen Daten enthält.</returns> private Channel createChannelObjectFromEnteredData() { // Setze den String für das Semester zusammen. string termString = string.Empty; if (TermYear != null) { if (IsSummerTermSelected) { termString += "S" + TermYear; } else if (IsWinterTermSelected) { termString += "W" + TermYear; } } // Erzeuge Instanz aus den eingegebenen Daten. Channel newChannel = null; switch (SelectedChannelType) { case ChannelType.LECTURE: Lecture lecture = new Lecture() { Name = ChannelName, Description = ChannelDescription, Type = SelectedChannelType, Term = termString, Locations = this.Locations, Dates = this.Dates, Contacts = this.Contacts, Website = this.Website, Faculty = SelectedFaculty, StartDate = LectureStartDate, EndDate = LectureEndDate, Lecturer = this.Lecturer, Assistant = this.Assistant }; newChannel = lecture; break; case ChannelType.EVENT: Event eventObj = new Event() { Name = ChannelName, Description = ChannelDescription, Type = SelectedChannelType, Term = termString, Locations = this.Locations, Dates = this.Dates, Contacts = this.Contacts, Website = this.Website, Cost = EventCost, Organizer = EventOrganizer }; newChannel = eventObj; break; case ChannelType.SPORTS: Sports sportsObj = new Sports() { Name = ChannelName, Description = ChannelDescription, Type = SelectedChannelType, Term = termString, Locations = this.Locations, Dates = this.Dates, Contacts = this.Contacts, Website = this.Website, Cost = SportsCost, NumberOfParticipants = AmountOfParticipants }; newChannel = sportsObj; break; default: newChannel = new Channel() { Name = ChannelName, Description = ChannelDescription, Type = SelectedChannelType, Term = termString, Locations = this.Locations, Dates = this.Dates, Contacts = this.Contacts, Website = this.Website }; break; } return newChannel; }
/// <summary> /// Führt Aktualisierung des Kanals 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 Abonnenten über die Änderung informiert. /// </summary> /// <param name="oldChannel">Der Datensatz des Kanals vor der Aktualisierung.</param> /// <param name="newChannel">Der Datensatz mit neu eingegebenen Daten.</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> UpdateChannelAsync(Channel oldChannel, Channel newChannel) { if (oldChannel == null || newChannel == null) return false; Moderator activeModerator = GetLocalModerator(); if (activeModerator == null) return false; // Validiere zunächst die neu eingegebenen Daten, bei Validierungsfehlern kann hier gleich abgebrochen werden. clearValidationErrors(); bool validationSuccessful = validateChannelProperties(newChannel); if (!validationSuccessful) return false; // Erstelle ein Objekt für die Aktualisierung, welches die Daten enthält, die aktualisiert werden müssen. Channel updatableChannelObj = prepareUpdatableChannelInstance(oldChannel, newChannel); if (updatableChannelObj == null) return true; // Keine Aktualisierung nötig. // Erstelle JSON-Dokument für die Aktualisierung. string jsonContent = jsonParser.ParseChannelToJsonString(updatableChannelObj); if (jsonContent == null) { Debug.WriteLine("Channel object could not be translated to a json document."); return false; } // Server Request. string serverResponse = null; try { // Führe Request zur Aktualisierung des Inhalts aus. serverResponse = await channelApi.SendUpdateChannelRequestAsync( activeModerator.ServerAccessToken, oldChannel.Id, jsonContent); } catch (APIException ex) { if (ex.ErrorCode == ErrorCodes.ChannelNotFound) { Debug.WriteLine("Channel not found on server. Channel probably deleted."); // Behandlung Not Found. Kanal wahrscheinlich gelöscht. MarkChannelAsDeleted(oldChannel.Id); } // Bilde ab auf ClientException. throw new ClientException(ex.ErrorCode, ex.Message); } // Führe lokale Aktualisierung des Datensatzes aus. try { Channel updatedChannel = jsonParser.ParseChannelFromJson(serverResponse); if (updatedChannel == null) { throw new ClientException(ErrorCodes.JsonParserError, "Couldn't parse server response."); } // Notification Settings bleiben unverändert. updatedChannel.AnnouncementNotificationSetting = oldChannel.AnnouncementNotificationSetting; if (updatedChannel != null) { channelDatabaseManager.UpdateChannelWithSubclass(updatedChannel); } } catch (DatabaseException ex) { Debug.WriteLine("DatabaseException. Couldn't perform local channel update."); throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message); } return true; }
/// <summary> /// Aktualisiert nur die Properties, welche für die View aktuell relevant sind, also Properties, die /// per Databinding an die View gebunden sind. Aktualisiert dabei die /// </summary> /// <param name="updatableChannel">Das zu aktualisierende Channel Objekt.</param> /// <param name="newChannel">Das Channel Objekt mit den neuen Daten.</param> protected void updateViewRelatedPropertiesOfChannel(Channel updatableChannel, Channel newChannel) { updatableChannel.Name = newChannel.Name; updatableChannel.Term = newChannel.Term; }
/// <summary> /// Erstellt ein JSON Dokument aus einem Channel Objekt. /// </summary> /// <param name="channel">Das Objekt, das umgewandelt werden soll.</param> /// <returns>JSON-Dokument des Objekts, oder null, falls Serialisierung fehlgeschlagen ist.</returns> public string ParseChannelToJsonString(Channel channel) { if (channel == null) return null; string jsonContent = null; try { switch (channel.Type) { case ChannelType.LECTURE: Lecture lecture = channel as Lecture; jsonContent = JsonConvert.SerializeObject(lecture); break; case ChannelType.EVENT: Event eventObj = channel as Event; jsonContent = JsonConvert.SerializeObject(eventObj); break; case ChannelType.SPORTS: Sports sportObj = channel as Sports; jsonContent = JsonConvert.SerializeObject(sportObj); break; default: jsonContent = JsonConvert.SerializeObject(channel); break; } } catch (JsonException jsonEx) { Debug.WriteLine("JsonParsingManager: Exception during serialization of an channel object."); Debug.WriteLine("JsonParsingManager: Message is: {0}.", jsonEx.Message); } return jsonContent; }