Если каждый CSV-файл представляет собой одну таблицу вашей базы данных, подумайте о том, чтобы сделать что-то похожее на структуру сущностей.
Вместо IQueryable<...>
пусть ваш DbSets
агрегат IEnumerable<...>
Если вам нужно только получить данные, это будет довольно просто. Если вы также хотите обновить, вам нужно внедрить (или повторно использовать) DbChangeTracker
public DbSet<T> : IEnumerable<T> where T: class
{
public FileInfo CsvFile {get; set;}
public IEnumerator<T> GetEnumerator()
{
return this.ReadCsvFile().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
protected IEnumerable<T> ReadCsvFile()
{
// open the CsvFile, read the lines and convert to objects of type T
// consider using Nuget package CsvHelper
...
foreach (var csvLine in csvLines)
{
T item = Create<T>(csvLine); // TODO: write how to convert a line into T
yield return T;
}
}
}
Вам также понадобится DbContext, который содержит все ваши DbSets:
class DbContext
{
public DbSet<School> Schools {get; } = new DbSet<School>{CsvFile = ...};
public DbSet<Teacher> Teachers {get; } = new DbSet<Teacher> {CsvFile = ...};
public DbSet<Student> Students {get; } = new DbSet<Student> {CsvFile = ...};
}
Вы можете улучшить производительность, запомнив уже извлеченные предметы. Поместите их в словарь, используйте первичный ключ в качестве ключа. Также добавьте функцию Find
к DbSet:
class DbSet<T> : IEnumerable<T>
{
private readonly Dictionary<int, T> fetchedItems = new Dictionary<int, T>();
public T Find(int id)
{
if (!fetchedItems.TryGetValue(id, out T fetchedItem))
{
// fetch elements using ReadCsvFile and put them in the Dictionary
// until you found the item with the requested primary key
// or until the end of your sequence
}
return fetchedItem;
}
}
Проще всего, если у каждого элемента таблицы одинаковый первичный ключ:
interface IPrimaryKey
{
int Id {get;}
}
class DbSet<T> : IEnumerable<T> where T : IPrimaryKey {...}
Если нет, вам нужно сообщить DbSet тип первичного ключа:
class DbSet<T, TKey> : IEnumerable<T> where T : class
{
private readonly Dictinary<TKey, T> fetchedItems = ...
}
Если вы решили хранить свои элементы в Словаре, то пусть ваш GetEnumerator сначала возвращает уже fetchedItems, прежде чем извлекать новые строки из вашего CSV-файла.
Добавить / обновить / удалить элементы
Для этого вам нужно иметь возможность добавлять / обновлять / удалять элементы из вашего CsVFile. Я предполагаю, что уже есть функции для этого.
Для эффективного обновления вам потребуется нечто похожее на DbContext.SaveChanges. Пусть каждый DbSet запоминает, какие элементы добавлять / удалять / обновлять, используя ChangeTracker:
class Entity<T> where T : IPrimaryKey
{
public T Value {get; set;}
public T OriginalValue {get; set;}
}
class ChangeTracker<T, TKey> where T: ICloneable
{
readonly Dictionary<int, Entity<T, TKey>> fetchedEntities = new Dictionary<int, Entity<T, TKey>>
readonly List<T> itemsToAdd = new List<T>();
public T Add(T item)
{
// TODO: check for not NULL, and Id == 0
this.ItemsToAdd.Add(itemToAdd);
return item;
}
public void Remove(T item)
{
// TODO: check not null, and primary key != 0
Entity<T> entityToRemove = Find(item.Id);
// TODO: decide what to do if there is no such item
entityToRemove.Value = null;
// null indicates it is about to be removed
}
Вам понадобится Find, который запоминает исходное значение:
public Entity<T> Find(TKey primaryKey)
{
// is it already in the Dictionary (found before)?
// if not: get it from the CsvDatabase and put it in the dictionary
if (!fetchedItems.TryGetValue(primaryKey, out Entity<T> fetchedEntity))
{
// not fetched yet, fetch if from your Csv File
T fetchedItem = ...
// what to do if does not exist?
// add to the dictionary:
fetchedEntities.Add(new Entity<T>
{
value = fetchedItem,
originalValue = (T)fetchedItem.Clone(),
// so if value changes, original does not change
});
}
return fetchedItem;
}
Наконец, ваши SaveChanges ()
void SaveChanges()
{
// your CsvFile database has functions to add / update / remove items
foreach (var itemToAdd in itemsToAdd)
{
csvDatabase.Add(itemToAdd);
}
// update or remove fetched items with OriginalValue unequal to Value
var itemsToUpdate = this.fetchedItems
.Where(fetchedItem => !ValueComparer.Equals(fetchedItem.OriginalValue, fetchedItem.Value)
.ToList();
foreach (Entity<T> itemToUpdate in itemsToUpdate)
{
if (itemToUpdate.Value == null)
{ // remove
csvFile.Remove(itemToUpdate.OriginalValue);
}
else
{ // update
csvFile.Update(...);
}
}
}
Очевидно, что если вы хотите иметь возможность обновлять элементы в вашей базе данных, вы должны иметь возможность проверить, изменились ли элементы. Вам понадобится IEqualityComparer<T>
, который проверяет по значению
class DbChangeTracker<T, TKey> : IEnumerable<T> where T : class
{
public IEqualityComparer<T> ValueComparer {get; set;}
...
}
DbSet SaveChanges:
void SaveChanges()
{
this.ChangeTracker.SaveChanges();
}
DbContext SaveChanges:
Students.SaveChanges()
Teachers.SaveChanges();
Schools.SaveChanges();