/// <summary> /// Import an item from a <see cref="TModel"/>. /// </summary> /// <param name="item">The model to be imported.</param> /// <param name="silent">Whether the user should be notified fo the import.</param> /// <param name="archive">An optional archive to use for model population.</param> public TModel Import(TModel item, bool silent = false, ArchiveReader archive = null) { delayEvents(); try { Logger.Log($"Importing {item}...", LoggingTarget.Database); using (var write = ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { try { if (!write.IsTransactionLeader) { throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}"); } var existing = CheckForExisting(item); if (existing != null) { Undelete(existing); Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); handleEvent(() => ItemAdded?.Invoke(existing, true, silent)); return(existing); } if (archive != null) { item.Files = createFileInfos(archive, Files); } Populate(item, archive); // import to store ModelStore.Add(item, silent); } catch (Exception e) { write.Errors.Add(e); throw; } } Logger.Log($"Import of {item} successfully completed!", LoggingTarget.Database); } catch (Exception e) { Logger.Error(e, $"Import of {item} failed and has been rolled back.", LoggingTarget.Database); item = null; } finally { // we only want to flush events after we've confirmed the write context didn't have any errors. flushEvents(item != null); } return(item); }
/// <summary> /// Import a beatmap from an <see cref="ArchiveReader"/>. /// </summary> /// <param name="archive">The beatmap to be imported.</param> public BeatmapSetInfo Import(ArchiveReader archive) { using (contextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { // create a new set info (don't yet add to database) var beatmapSet = createBeatmapSetInfo(archive); // check if this beatmap has already been imported and exit early if so var existingHashMatch = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == beatmapSet.Hash); if (existingHashMatch != null) { Undelete(existingHashMatch); return(existingHashMatch); } // check if a set already exists with the same online id if (beatmapSet.OnlineBeatmapSetID != null) { var existingOnlineId = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); if (existingOnlineId != null) { Delete(existingOnlineId); beatmaps.Cleanup(s => s.ID == existingOnlineId.ID); } } beatmapSet.Files = createFileInfos(archive, files); beatmapSet.Beatmaps = createBeatmapDifficulties(archive); // remove metadata from difficulties where it matches the set foreach (BeatmapInfo b in beatmapSet.Beatmaps) { if (beatmapSet.Metadata.Equals(b.Metadata)) { b.Metadata = null; } } // import to beatmap store Import(beatmapSet); return(beatmapSet); } }
/// <summary> /// Import an item from an <see cref="ArchiveReader"/>. /// </summary> /// <param name="archive">The archive to be imported.</param> public TModel Import(ArchiveReader archive) { using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. { // create a new model (don't yet add to database) var item = CreateModel(archive); var existing = CheckForExisting(item); if (existing != null) { return(existing); } item.Files = createFileInfos(archive, Files); Populate(item, archive); // import to store ModelStore.Add(item); return(item); } }
/// <summary> /// Refresh an instance potentially from a different thread with a local context-tracked instance. /// </summary> /// <param name="obj">The object to use as a reference when negotiating a local instance.</param> /// <param name="lookupSource">An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes.</param> /// <typeparam name="T">A valid EF-stored type.</typeparam> protected virtual void Refresh <T>(ref T obj, IQueryable <T> lookupSource = null) where T : class, IHasPrimaryKey { using (var usage = ContextFactory.GetForWrite()) { var context = usage.Context; if (context.Entry(obj).State != EntityState.Detached) { return; } int id = obj.ID; var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find <T>(id); if (foundObject != null) { obj = foundObject; } else { context.Add(obj); } } }