public Task AddTrackToAlbum(Album UpdatedAlbum) { throw new NotImplementedException(); }
/// <summary> /// Add a new track to an existing album in the In-Memory context, updating the related /// entities and ensuring the navigation properties are correctly updated. /// </summary> /// <param name="UpdatedAlbum">The new track, encapsulated within an album</param> /// <returns>An instance of Task so the method is awaitable</returns> /// <remarks> /// This method introduces a bottleneck to the asynchronous processing that /// happens to get here. All routines within this context are synchronous /// in nature because there are no methods that are called from them, such /// as List(T).Add methods that can be asynchronous. /// <para> /// This lock, actually makes the locks in the DataHelper methods redundant, /// Because this entire thread has access locked with this one. However /// There is no harm at all in leaving the other locks in place, should the /// application be extended in the future and allowing the other methods /// to be called from elsewhere. /// </para> /// <para> /// These methods all produce warning errors, complaining that they /// "lacking await" keywords and will therefore execute synchronously. /// These could be removed by calling await Task.Delay(0) which would /// remove the warning message but provide no improvement to the /// efficiency of the routines. /// </para> /// </remarks> public async Task AddTrackToAlbum(Album UpdatedAlbum) { // Get the existing album // get by Id var existingAlbum = this.Albums.FirstOrDefault(a => a.Id == UpdatedAlbum.Id); // If not then check by artist Name and Album Title if (existingAlbum == null) existingAlbum = this.Albums.FirstOrDefault(a => a.Title == UpdatedAlbum.Title && a.Artist.Name == UpdatedAlbum.Artist.Name); // Only process it if the ablum has been found. if (existingAlbum != null) { var albumIdx = this.Albums.IndexOf(existingAlbum); var newTrack = UpdatedAlbum.Tracks.FirstOrDefault(); lock (_AlbumsLock) { // 1. Check if track already exists var existingTrack = existingAlbum.Tracks.FirstOrDefault(t => t.Title.ToLowerInvariant() == newTrack.Title.ToLowerInvariant()); if (existingTrack != null) { // 1a. Update all the track related fields // Get the index of the track. var idx = existingAlbum.Tracks.IndexOf(existingTrack); // Add the new track to the tracks collection and update the Id and album reference // The updated album contains only the one track, this needs a list var updatedTrack = _helper.AddTracksToContext(this, UpdatedAlbum.Tracks, existingAlbum); // Now replace it in the Album tracks existingAlbum.Tracks[idx] = updatedTrack[0]; } else { // 1b. Add the track to the album // Add the new track to the tracks collection and update the Id and album reference // The updated album contains only the one track, this needs a list var updatedTrack = _helper.AddTracksToContext(this, UpdatedAlbum.Tracks, existingAlbum); // now add it to the albums tracks existingAlbum.Tracks.Add(updatedTrack[0]); } // Add any new Genres to the existing ones, duplicates will be removed when the Genres are // updated by the AddGenresToContext routine. foreach (var g in UpdatedAlbum.Genres) { var existingGenre = existingAlbum.Genres .FirstOrDefault(gn => gn.Name == g.Name); if (existingAlbum == null) existingAlbum.Genres.Add(g); } // 3 Add each genre to the Genres collection, ensure navigation properties are updated var updatedGenres = _helper.AddGenresToContext(this, UpdatedAlbum.Genres, existingAlbum); existingAlbum.Genres = updatedGenres; // Replace the Album reference in the Artist, as it has been updated to ensure the // navigation properties are up to date. It will find the artist, and the album too // so the album reference will be replaced. var updatedArtist = _helper.AddArtistToContext(this, existingAlbum.Artist, existingAlbum); // update the reference to the artist in the existingAlbum existingAlbum.Artist = updatedArtist; //Finally replace the Album in the Albums collection this.Albums[albumIdx] = existingAlbum; } } }
public Task<Album> CreateAlbum(Album newAlbum) { throw new NotImplementedException(); }
/// <summary> /// Add the album to the Artist, and if necessary, to the AlbumIds as well. /// </summary> /// <param name="Artist">The Artist</param> /// <param name="Album">The album being added</param> /// <remarks> /// Only add an AlbumId if to the AlbumIds collection if it's not already there. /// It won't be if the its is a New Album being created, but there will be when /// reloading the persisted data. /// </remarks> private void AddAlbumToArtist(Artist Artist, Album Album) { // Add the Album to the albums collection Artist.Albums.Add(Album); // Only add an AlbumId if to the AlbumIds collection if it's not already there. // It won't be if the its is a New Album being created, // but there will be when reloading the persisted data. if (!Artist.AlbumIds.Contains(Album.Id)) Artist.AlbumIds.Add(Album.Id); }
/// <summary> /// Adds a new album to the In-Memory context, updating the related entities. /// </summary> /// <param name="newAlbum">The Album to be Created and added to the context</param> /// <returns>The instance of the album created</returns> /// <remarks> /// This method introduces a bottleneck to the asynchronous processing that /// happens to get here. All routines within this context are synchronous /// in nature because there are no methods that are called from them, such /// as List(T).Add methods that can be asynchronous. /// <para> /// This lock, actually makes the locks in the DataHelper methods redundant, /// Because this entire thread has access locked with this one. However /// There is no harm at all in leaving the other locks in place, should the /// application be extended in the future and allowing the other methods /// to be called from elsewhere. /// </para> /// <para> /// These methods all produce warning errors, complaining that they /// "lacking await" keywords and will therefore execute synchronously. /// These could be removed by calling await Task.Delay(0) which would /// remove the warning message but provide no improvement to the /// efficiency of the routines. /// </para> /// </remarks> public async Task<Album> CreateAlbum(Album newAlbum) { lock (_AlbumsLock) { // 1. Calculate the new AlbumId and update the album newAlbum.Id = _helper.GenerateAlbumId(this); // 2. Add the album ref to each track and add each to the Tracks collection // and update the tracks collection to ensure the navigation properties are updated. var updatedTracks = _helper.AddTracksToContext(this, newAlbum.Tracks, newAlbum); newAlbum.Tracks = updatedTracks; // 3 Add each genre to the Genres collection, ensure navigation properties are updated var updatedGenres = _helper.AddGenresToContext(this, newAlbum.Genres, newAlbum); newAlbum.Genres = updatedGenres; // 4 Add the Artist to the Artist collection, ensure navigation properties are updated. var updatedArtist = _helper.AddArtistToContext(this, newAlbum.Artist, newAlbum); newAlbum.Artist = updatedArtist; // 5. Add the album to the Albums collection this.Albums.Add(newAlbum); } return newAlbum; }
/// <summary> /// Adds a new Artist to the Artists collection and updates the albums referenced /// </summary> /// <param name="UnitOfWork">In-Memory context</param> /// <param name="Artist">the New artist</param> /// <param name="Album">The Album it relates to</param> /// <returns>The New Artist</returns> /// <remarks> /// <para> /// Only add an AlbumId if to the AlbumIds collection if it's not already there. /// It won't be if the its is a New Album being created, but there will be when /// reloading the persisted data. /// </para> /// <para> /// Concurrency: /// </para> /// <Para> /// This routine provides a Monitor lock to ensure that only one instance of this /// can modify the Artists collection at a time. /// It locks from the point which a new Id is generated for the Artist being added /// and retains that until the Artist is added to the Genres collection. /// This type of lock is preferred to ensure the generated Id is updated within /// the Genres collection before any other thread can try and Add a Genre which /// would require the Id for this one, which woudl be used in the GenerateId /// methods. This is to avoid duplicate Id's being generated. /// </Para> /// <para> /// This is a very course grained way of locking but is acceptible within this /// application as it is a Windows 8 store app, and not subjected to multiple /// users, only the individual threads within the application. The performance /// hit should not be that noticable here. /// </para> /// </remarks> private Artist AddArtist(IUnitOfWork UnitOfWork, Artist Artist, Album Album) { // lock (_ArtistLock) { // Concurrency: Lock between getting the Id and performing the update, as the // this new Id won't be availble for checking purposes by another // thread that is looking to add an Artist. This should avoid the // possiblilties of duplicate Id's Artist.Albums = new List<Album>(); // i. Generate ArtistId, if there isn'[t one already, will be reloading persisted data. if (Artist.Id == 0) Artist.Id = GenerateArtistId(UnitOfWork); // ii. Add album ref to Artist AddAlbumToArtist(Artist, Album); // iii.Add Artist to Artists Collection UnitOfWork.Artists.Add(Artist); } return Artist; }
/// <summary> /// Updates the Artist in the Artists collection. /// </summary> /// <param name="UnitOfWork">The In-Memory context</param> /// <param name="Artist">The artist to be updated</param> /// <param name="Album">The album</param> /// <returns>The Updated Artist</returns> /// <remarks> /// The update artist is returned so that it can replace the /// Artist in the album, which means the navigation properties /// are up to date. /// <para> /// Concurrency: /// </para> /// <Para> /// This routine provides a Monitor lock to ensure that only one instance of this /// can modify the Artists collection at a time. /// It locks from the point where the Genre to be updated is retrieved from /// the Artist /// and retains that until the Artist is updated in the Artists collection. /// This type of lock is preferred to ensure that no other updates to the Artist /// are possible while this update occurs. /// </Para> /// <para> /// This is a very course grained way of locking but is acceptible within this /// application as it is a Windows 8 store app, and not subjected to multiple /// users, only the individual threads within the application. The performance /// hit should not be that noticable here. /// </para> /// </remarks> private Artist UpdateArtist(IUnitOfWork UnitOfWork, Artist Artist, Album Album) { int idx = 0; lock (_ArtistLock) { // Get the index of the Artist idx = UnitOfWork.Artists.FindIndex(a => a.Name == Artist.Name); // Get the Artist var updatedArtist = UnitOfWork.Artists[idx]; // If Album exists in the retrieved Artist. var al = updatedArtist.Albums.FirstOrDefault(a => a.Id == Album.Id); if (al != null) { // The Album is found, so replace it var alIdx = updatedArtist.Albums.IndexOf(al); updatedArtist.Albums[alIdx] = Album; } else { // Album dosn't exist in the Artist.Albums collection, so just add it. AddAlbumToArtist(updatedArtist, Album); } // Replace the Artist in the Artists collection UnitOfWork.Artists[idx] = updatedArtist; } // return the updated Artist return UnitOfWork.Artists[idx]; // return updatedArtist; }
/// <summary> /// Add the Artist to the In-Memory context. The updated /// Artist is returned to update the Album /// </summary> /// <param name="UnitOfWork">In-Memory context</param> /// <param name="Artist">The Artist to be updated</param> /// <param name="Album">The album reference</param> /// <returns>The updated Artist</returns> /// <remarks> /// <para> /// Only add an AlbumId if to the AlbumIds collection if it's not already there. /// It won't be if the its is a New Album being created, but there will be when /// reloading the persisted data. /// </para> /// </remarks> public Artist AddArtistToContext(IUnitOfWork UnitOfWork, Artist Artist, Album Album) { // 4a If exists, Add album reference to the Artist if (ArtistExists(UnitOfWork, Artist.Name)) { var updatedArtist = UpdateArtist(UnitOfWork, Artist, Album); return updatedArtist; } else { // 4b If doesn't exist, generate Artist Id var newArtist = AddArtist(UnitOfWork, Artist, Album); // iv. Replace Artist to Album.Artist return Artist; } }
/// <summary> /// Updates the Genre in the Genres collection. /// </summary> /// <param name="UnitOfWork">The In-Memory context</param> /// <param name="Genre">The Genre to be updated</param> /// <param name="Album">The album</param> /// <returns>The Updated Genre</returns> /// <remarks> /// <para> /// The update genre is returned so that it can replace the /// genre in the album, which means the navigation properties /// are up to date. /// </para> /// <para> /// Only add an AlbumId if to the AlbumIds collection if it's not already there. /// It won't be if the its is a New Album being created, but there will be when /// reloading the persisted data. /// </para> /// <para> /// Concurrency: /// </para> /// <Para> /// This routine provides a Monitor lock to ensure that only one instance of this /// can modify the Genres collection at a time. /// It locks from the point where the Genre to be updated is retrieved from /// the Genres /// and retains that until the Genre is updated in the Genres collection. /// This type of lock is preferred to ensure that no other updates to the Genres /// is attempted while this update occurs. /// </Para> /// <para> /// This is a very course grained way of locking but is acceptible within this /// application as it is a Windows 8 store app, and not subjected to multiple /// users, only the individual threads within the application. The performance /// hit should not be that noticable here. /// </para> /// </remarks> private Genre UpdateGenre(IUnitOfWork UnitOfWork, Genre Genre, Album Album) { // TODO: Refactor this to use the concurrent collection. var idx = 0; lock (_GenreLock) { // Get the index of the Genre idx = UnitOfWork.Genres.FindIndex(g => g.Name == Genre.Name); // Get the Genre var updatedGenre = UnitOfWork.Genres[idx]; // If Album exists in the retrieved Genre. var al = updatedGenre.Albums.FirstOrDefault(a => a.Id == Album.Id); if (al != null) { // The Album is found, so replace it var alIdx = updatedGenre.Albums.IndexOf(al); updatedGenre.Albums[alIdx] = Album; } else { // Track dosn't exist in the Genres.Tracks collection, so just add it. AddAlbumToGenre(updatedGenre, Album); } // Replace the Genre in the Genres collection UnitOfWork.Genres[idx] = updatedGenre; } // return the updated Genre return UnitOfWork.Genres[idx]; }
/// <summary> /// Adds a new Genre to the Genres collection of the Domain model. It /// returns an instance of the Genre added so that it can be used elsewhere. /// This is standard coding for creating a new object. /// </summary> /// <param name="UnitOfWork">The In-Memory context</param> /// <param name="Genre">the new Genre</param> /// <param name="Album">The album the Genre relates to</param> /// <returns>The updated instance of the new Genre</returns> /// <remarks> /// Only add an AlbumId if to the AlbumIds collection if it's not already there. /// It won't be if the its is a New Album being created, but there will be when /// reloading the persisted data. /// <para> /// Concurrency: /// </para> /// <Para> /// This routine provides a Monitor lock to ensure that only one instance of this /// can modify the Genres collection at a time. /// It locks from the point which a new Id is generated for the Genre being added /// and retains that until the Genre is added to the Genres collection. /// This type of lock is preferred to ensure the generated Id is updated within /// the Genres collection before any other thread can try and Add a Genre which /// would require the Id for this one, which woudl be used in the GenerateId /// methods. This is to avoid duplicate Id's being generated. /// </Para> /// <para> /// This is a very course grained way of locking but is acceptible within this /// application as it is a Windows 8 store app, and not subjected to multiple /// users, only the individual threads within the application. The performance /// hit should not be that noticable here. /// </para> /// </remarks> private Genre AddGenre(IUnitOfWork UnitOfWork, Genre Genre, Album Album) { lock (_GenreLock) { Genre.Albums = new List<Album>(); // Concurrency: Lock between getting the Id and performing the update, as the // this new Id won't be availble for checking purposes by another // thread that is looking to add a Genre. This should avoid the // possiblilties of duplicate Id's // i. Generate GenreId, if there isn't one, will be if reloading persisted data. if (Genre.Id == 0) Genre.Id = GenerateGenreId(UnitOfWork); // ii. Add album ref to Genre AddAlbumToGenre(Genre, Album); // iii.Add Genre to Genres Collection UnitOfWork.Genres.Add(Genre); } return Genre; }
/// <summary> /// Add the Genres to the In-Memory context. The complete collection of update /// genres is returned to update the Album /// </summary> /// <param name="UnitOfWork">In-Memory context</param> /// <param name="Genres">The genres to be updated</param> /// <param name="album">The album reference</param> /// <returns>The updated genres collection</returns> public List<Genre> AddGenresToContext(IUnitOfWork UnitOfWork, List<Genre> Genres, Album album) { var updatedGenres = new List<Genre>(); // 3 Add each genre to the Genres collection foreach (var gn in Genres) { // 3a If exists, Add album reference to the Genre if (GenreExists(UnitOfWork, gn.Name)) { var updateGenre = UpdateGenre(UnitOfWork, gn, album); updatedGenres.Add(updateGenre); } else { // 3b If doesn't exist, generate Genre Id var newGenre = AddGenre(UnitOfWork, gn, album); // iv. Replace Genre to Album.Genres collection updatedGenres.Add(newGenre); } } return updatedGenres; }
/// <summary> /// Updates the Track: New Id and Album, and adds to the Tracks Collection /// </summary> /// <param name="UnitOfWork">The instance of the in-memory context</param> /// <param name="Tracks">The track to be added</param> /// <param name="Album">The album the track belongs to</param> /// <returns>The updated track</returns> /// <remarks> /// This is a synchronous task because there are no waitiable operations either /// needed or so use of List(T) is OK. /// </remarks> public List<Track> AddTracksToContext(IUnitOfWork UnitOfWork, List<Track> Tracks, Album Album) { var updatedTracks = new List<Track>(); foreach (var tr in Tracks) { // Generate the Track Id and update the track, if there isn't one already if (tr.Id == 0) tr.Id = GenerateTrackId(UnitOfWork); // Add the album reference to the track tr.Album = Album; tr.AlbumId = Album.Id; // Process any playlist that might exist (none for a new album import). if (tr.Playlists.Count > 0) { var updatedPlaylists = AddOrUpdatePlaylists(UnitOfWork, tr.Playlists, tr); tr.Playlists = updatedPlaylists; } // Add the track to the Tracks collection UnitOfWork.Tracks.Add(tr); // Add the updated track to the returned list; updatedTracks.Add(tr); } return updatedTracks; }