Упрощенная схема БД
Нужна помощь в организации работы с данными.
Сабжект: десктопное приложение для гостиницы.
В таблице Reservations данные по каждому размещению. Каждое размещение может состоять из одного или нескольких дней (ResDays). На каждый день размещения может быть добавлена доп. услуга (AddServices).
В программе для этого я использую коллекции. Очень упрощенно:
class AddServiceItem
{
string Name;
Decimal Cost;
}
class ResDayItem
{
DateTime Date;
Decimal Cost;
Collection
С созданием размещения проблем нет - пользователь выбирает количество дней, создается экземпляр Reservation, а коллекции из дней и доп. услуг заполняются юзером в интерфейсе программы. После нажатия кнопки "Добавить размещение" все записывается в БД.
Конечно, нужно иметь возможность изменять все данные. При открытии размещения для редактирования, я опять создаю экземпляр Reservation, а коллекции заполняю значениями из БД примерно так
MyReservation = new Reservation();
//создаю коллекцию дней
MyReservation.ResDays = new Collection
Вопрос в следующем: как (в каком порядке) делать апдейт БД при изменении коллекций?
Хотелось бы, чтобы в БД все писалось при нажатии пользователем кнопки "Сохранить". Если количество дней/доп.услуг осталось прежним, то это просто update по всем записям, а если юзер удалил что-то? Как узнать, какие именно элементы коллекций были удалены, чтобы удалить соответствующие записи из БД?
Может быть я вообще применил неверный подход с такой вложенностью коллекций?
Как правильно организовать работу с данными в такой задаче?
Ответ
В более-менее сложных программах реализуют примерно такую структуру
Как пример реализации паттернов Repository и Unit of Work могу предложить вам следующий вариант.
Для Repository мы будем опираться на следующую иллюстрацию
Для Unit of Work мы будем опираться на следующую иллюстрацию
1) Создайте проект Class Library LibDomain, создайте в нем две папки: Repositories и Models.
2) Cоздайте след. классы в папке Models
public class Reservation
{
//ctor
public Reservation() => Days = new HashSet
public long Id { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int Status { get; set; }
public virtual ICollection
``
public class ReservedDay
{
//ctor
public ReservedDay() => Services = new HashSet
public long Id { get; set; }
public DateTime Date { get; set; }
public decimal Cost { get; set; }
//внешний ключ
public long ReservationId { get; set; }
//навигационное св-во
public virtual Reservation Reservation { get; set; }
public ICollection
``
public class AdditionService
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Cost { get; set; }
//внешний ключ
public long ReservedDayId { get; set; }
//навигационное св-во
public virtual ReservedDay Day { get; set; }
}
``
3) в папке репозиториев вам нужно будет создать главный интерфейс репозитория
public interface IRepository
//Commands
Task Add(TEntity entity);
Task AddRange(IEnumerable
и на каждую сущность свой интерфейса репозитория (даю пример только двух)
public interface IReservationRepository : IRepository
Task
Task
``
public interface IReservedDayRepository : IRepository
создайте в корне проекта интерфейс для UnitOfWork
public interface IUnitOfWork : IDisposable
{
IReservationRepository Reservations { get; }
IReservedDayRepository ReservedDays { get; }
IAdditionServiceRepository AdditionServices { get; }
Task
4) Создайте в решении еще один проект библиотеки LibData, в котором мы непосредственно реализуем работу с SQLite через Entity Framework Core.
Добавьте ссылку на LibDomain и установите через NuGet Package Manager пакет Microsoft.EntityFrameworkCore.Sqlite (важно! устанавливайте версию 1.1.2, а не самую последнюю 2.0, которая вышла несколько дней назад, иначе будут проблемы).
5) в проекте создайте класс SqliteDbContext
public class SqliteDbContext : DbContext
{
//ctor
public SqliteDbContext(DbContextOptions
public virtual DbSet
и создайте папку Repositories и в ней класс репозитория
public abstract class Repository
//ctor
public Repository(DbContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
//Queries
public virtual async Task
public virtual async Task
public virtual async Task
//Commands
public virtual Task Add(TEntity entity)
{
return _context.AddAsync(entity);
}
public virtual Task AddRange(IEnumerable
public virtual void Remove(TEntity entity)
{
_context.Remove(entity);
}
public virtual void RemoveRange(IEnumerable
и классы для каждой сущности: ReservationRepository, ReservedDayRepository, AdditionServiceRepository (пример только для первого класса)
public class ReservationRepository : Repository
//ctor
public ReservationRepository(SqliteDbContext context) : base(context)
{
_context = context;
}
//далее реализация методов из IReservationRepository
//или переопределение методов из Repository под Reservation
public Task
public Task
6) создайте класс, кот. реализутет интерфейс IUnitOfWork
public class SqliteDataService : IUnitOfWork
{
private readonly SqliteDbContext _context;
//ctor
public SqliteDataService(SqliteDbContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
Reservations = new ReservationRepository(_context);
ReservedDays = new ReservedDayRepository(_context);
AdditionServices = new AdditionServiceRepository(_context);
}
public IReservationRepository Reservations { get; private set; }
public IReservedDayRepository ReservedDays { get; private set; }
public IAdditionServiceRepository AdditionServices { get; private set; }
public Task
///
var dbBuilder = new DbContextOptionsBuilder
return new SqliteDataService(new SqliteDbContext(dbBuilder.Options));
}
}
7) Пересмотрите еще раз иллюстрации, перечитайте код примеров. Надеюсь вам станет яснее схема взаимодействия с БД. В качестве примера использования привожу пример простого консольного приложения
class Program
{
static void Main(string[] args)
{
string dbPath = @"Filename=D:
epExample.sqlite";
var dbService = SqliteDataService.GetService(dbPath);
//AddReservation(dbService);
//AddServicesToReservation(dbService);
ShowReservation(dbService);
Console.ReadKey();
}
private static async void ShowReservation(SqliteDataService dbService)
{
//находим размещение c его днями и услугами по его id
var res = await dbService.Reservations.GetByIdWithDaysAndServices(1);
Console.WriteLine();
Console.WriteLine($"Размещение с {res.StartDate.ToShortDateString()} по {res.EndDate.ToShortDateString()}");
Console.WriteLine(new string('=', 70));
int dayNumber = 1;
int servNumber = 1;
foreach (var day in res.Days)
{
Console.WriteLine();
Console.WriteLine($"День {dayNumber} - {day.Date.ToShortDateString()}");
foreach (var service in day.Services)
{
Console.WriteLine();
Console.WriteLine($"Услуга {servNumber}:{service.Name} за {service.Cost}руб.");
servNumber++;
}
Console.WriteLine(new string('-', 30));
dayNumber++;
servNumber = 1;
}
}
private static async void AddReservation(SqliteDataService dbService)
{
//создаем дни размещения
ReservedDay day1 = new ReservedDay()
{
Date = new DateTime(year: 2017, month: 8, day: 20),
Cost = 200M
};
ReservedDay day2 = new ReservedDay()
{
Date = new DateTime(year: 2017, month: 8, day: 21),
Cost = 200M
};
ReservedDay day3 = new ReservedDay()
{
Date = new DateTime(year: 2017, month: 8, day: 22),
Cost = 200M
};
//размещение и его дни
Reservation res = new Reservation()
{
StartDate = day1.Date,
EndDate = day3.Date,
Status = 1
};
res.Days.Add(day1);
res.Days.Add(day2);
res.Days.Add(day3);
//добавляем и сохраняем в БД
await dbService.Reservations.Add(res);
await dbService.Commit();
}
private static async void AddServicesToReservation(SqliteDataService dbService)
{
var serv1 = new AdditionService { Name = "Бассейн", Cost = 2M };
var serv2 = new AdditionService { Name = "Парилка", Cost = 1M };
var serv3 = new AdditionService { Name = "Завтрак в номер", Cost = 100M };
var serv4 = new AdditionService { Name = "Чай в номер", Cost = 10M };
//находим размещение по его id
var res = await dbService.Reservations.GetByIdWithDays(1);
//выбираем дни по дате
var dateDay1 = new DateTime(year: 2017, month: 8, day: 20);
var dateDay2 = new DateTime(year: 2017, month: 8, day: 21);
var dateDay3 = new DateTime(year: 2017, month: 8, day: 22);
var day1 = res.Days.Single(d => d.Date == dateDay1);
var day2 = res.Days.Single(d => d.Date == dateDay2);
var day3 = res.Days.Single(d => d.Date == dateDay3);
//добавляем услуги в эти дни
day1.Services.Add(serv1);
day1.Services.Add(serv4);
day2.Services.Add(serv3);
day2.Services.Add(serv1);
day2.Services.Add(serv2);
day3.Services.Add(serv4);
//запоминаем
await dbService.Commit();
}
}
Целиком пример можно скачать здесь
Комментариев нет:
Отправить комментарий