Skip to content

Instantly share code, notes, and snippets.

@GW-FUB
Last active September 11, 2024 12:24
Show Gist options
  • Save GW-FUB/d7cfa866023e82f6538bbf83047c331e to your computer and use it in GitHub Desktop.
Save GW-FUB/d7cfa866023e82f6538bbf83047c331e to your computer and use it in GitHub Desktop.
LiteDB Replace With CUD
public class DBContext
{
private LiteDatabase _userDB;
public static DBContext Instance { get; } = new DBContext(DefaultDatabaseFolderPath);
...
string userDataDBFilePath = Path.Combine(DatabaseFolderPath, "UserData.lite");
_userDB = new LiteDatabase(GetConnectionString(userDataDBFilePath))
{
UtcDate = true
};
...
private static ConnectionString GetConnectionString(string databasePath) => new ConnectionString
{
Filename = databasePath,
Upgrade = true,
};
...
/// <summary>
/// Gets a collection with the name of the calling property.
/// </summary>
/// <remarks> Accesses the user database. May only be called when logged in. </remarks>
/// <exception cref="NullReferenceException"> When no user is logged in. </exception>
public ILiteCollection<T> GetNamedCollection<T>([CallerMemberName] string name = null) where T : new() => _userDB.GetCollection<T>(name);
}
public static class DBContextExtensions
{
...
public static ILiteCollection<MyValueDto> MyValueDtos(this DBContext context)
{
var collection = context.GetNamedCollection<MyValueDto>();
collection.EnsureIndex(x => x.Value);
collection.EnsureIndex(x => x.Table);
return collection;
}
}
// Get values by extension collection
var existing = DBContext.Instance.MyValueDtos().FindAll().ToList();
var current = newItems.ToList();
var (creates, updates, deletes) = existing.GetCUD(current, x => x.Id);
/// <summary>
/// Compares a source sequence to another by id and returns the creates, updates and deletes that are necessary for the source sequence.<br/>
/// <br/>
/// Creates: Missing in the source. Updates: Exist in both. Deletes: Just present in the source.
/// </summary>
/// <typeparam name="TId"> The type of the id to compare. </typeparam>
/// <param name="source"> The source sequence. Contains the currently present items. </param>
/// <param name="compare"> The compare sequence. Contains the currently available items. The source sequence should get modified to include the same items. </param>
/// <returns> The necessary creates, updates and deletes for the source sequence. </returns>
public static (IEnumerable<T> creates, IEnumerable<T> updates, IEnumerable<T> deletes) GetCUD<T, TId>(this IEnumerable<T> source, IEnumerable<T> compare, Func<T, TId> getId)
{
List<(T item, TId id)> sourceItems = source.Select(x => (x, getId(x))).ToList();
List<(T item, TId id)> compareItems = compare.Select(x => (x, getId(x))).ToList();
var sourceIds = new HashSet<TId>(sourceItems.Select(x => x.id));
var compareIds = new HashSet<TId>(compareItems.Select(x => x.id));
// Exists only in the second => Create
var createIds = compareIds.Except(sourceIds);
var creates = compareItems.Where(x => createIds.Contains(x.id)).Select(x => x.item);
// Exists in both => Update
var updateIds = sourceIds.Intersect(compareIds);
var updates = updateIds.Select(updateId => compareItems.Find(item => item.id.Equals(updateId)).item);
// Exists only in the first => Delete
var deleteIds = sourceIds.Except(compareIds);
var deletes = sourceItems.Where(x => deleteIds.Contains(x.id)).Select(x => x.item);
return (creates, updates, deletes);
}
/// <summary>
/// Compares a source sequence to another by id and returns the creates, updates and deletes that are necessary for the source sequence.<br/>
/// <br/>
/// Creates: Missing in the source. Updates: Exist in both. Deletes: Just present in the source.
/// </summary>
/// <typeparam name="TId"> The type of the id to compare. </typeparam>
/// <param name="source"> The source sequence. Contains the currently present items. </param>
/// <param name="compare"> The compare sequence. Contains the currently available items. The source sequence should get modified to include the same items. </param>
/// <returns> The necessary creates, updates and deletes for the source sequence. </returns>
public static (IEnumerable<T2> creates, IEnumerable<(T, T2)> updates, IEnumerable<T> deletes) GetCUD<T, T2, TId>(this IEnumerable<T> source, Func<T, TId> getIdOfSource, IEnumerable<T2> compare, Func<T2, TId> getIdOfCompare)
{
List<(T item, TId id)> sourceItems = source.Select(x => (x, getIdOfSource(x))).ToList();
List<(T2 item, TId id)> compareItems = compare.Select(x => (x, getIdOfCompare(x))).ToList();
var sourceIds = new HashSet<TId>(sourceItems.Select(x => x.id).Distinct());
var compareIds = new HashSet<TId>(compareItems.Select(x => x.id).Distinct());
// Exists only in the second => Create
var createIds = compareIds.Except(sourceIds);
var creates = compareItems.Where(x => createIds.Contains(x.id)).Select(x => x.item);
// Exists in both => Update
var updateIds = sourceIds.Intersect(compareIds);
var updates = updateIds.Select(updateId => (sourceItems.Find(item => item.id.Equals(updateId)).item, compareItems.Find(item => item.id.Equals(updateId)).item));
// Exists only in the first => Delete
var deleteIds = sourceIds.Except(compareIds);
var deletes = sourceItems.Where(x => deleteIds.Contains(x.id)).Select(x => x.item);
return (creates, updates, deletes);
}
public static void Replace<T>(this ILiteCollection<T> collection, IEnumerable<T> items)
{
var mapper = BsonMapper.Global;
var entityMapper = collection.EntityMapper;
var idField = entityMapper.Id;
var idType = idField.DataType;
IEnumerable<(BsonValue id, T element)> existingIds = collection.FindAll().Select(x => (GetBsonValue(x), x));
IEnumerable<(BsonValue id, T element)> currentIds = items.Select(x => (GetBsonValue(x), x));
var (creates, updates, deletes) = existingIds.GetCUD(currentIds, x => x.id);
foreach (var (id, element) in deletes.ToList())
{
collection.Delete(id);
}
foreach (var (id, element) in updates.ToList())
{
collection.Update(element);
}
foreach (var (id, element) in creates.ToList())
{
collection.Insert(element);
}
BsonValue GetBsonValue(T item)
{
var value = idField.Getter(item);
return mapper.Serialize(idType, value);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment