From fbe26898faa7194e7b75049d9288c82830ef4898 Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Mon, 9 Jun 2025 18:36:51 +0300 Subject: [PATCH] add data seeding --- .../Persistence/Configuration.cs | 28 +- src/Persistence/ConfigurationOptions.cs | 4 +- src/Persistence/DbSeeder.cs | 904 ++++++++++++++++-- 3 files changed, 853 insertions(+), 83 deletions(-) diff --git a/src/Configuration/Persistence/Configuration.cs b/src/Configuration/Persistence/Configuration.cs index 4ab16fb..b1a344e 100644 --- a/src/Configuration/Persistence/Configuration.cs +++ b/src/Configuration/Persistence/Configuration.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using cuqmbr.TravelGuide.Persistence.PostgreSql; using cuqmbr.TravelGuide.Persistence.InMemory; using Microsoft.EntityFrameworkCore.Diagnostics; +using cuqmbr.TravelGuide.Application.Common.Services; namespace cuqmbr.TravelGuide.Configuration.Persistence; @@ -76,10 +77,33 @@ public static class Configuration $"{configuration.Type} datastore is not supported."); } - if (configuration.Seed) + if (configuration.SeedIdentity || configuration.SeedData) { using var serviceProvider = services.BuildServiceProvider(); - DbSeeder.Seed(serviceProvider); + + var unitOfWork = + serviceProvider.GetRequiredService(); + var passwordHasher = + serviceProvider.GetRequiredService(); + + using var dbSeeder = new DbSeeder(unitOfWork, passwordHasher); + + if (configuration.SeedIdentity) + { + dbSeeder.SeedIdentity(); + } + + // Data can not be seeded without seeding identity + if (configuration.SeedData && !configuration.SeedIdentity) + { + throw new InvalidOperationException( + "Can not seed data without seeding identity."); + } + + if (configuration.SeedData) + { + dbSeeder.SeedData(); + } } return services; diff --git a/src/Persistence/ConfigurationOptions.cs b/src/Persistence/ConfigurationOptions.cs index a239384..6470a44 100644 --- a/src/Persistence/ConfigurationOptions.cs +++ b/src/Persistence/ConfigurationOptions.cs @@ -12,5 +12,7 @@ public sealed class ConfigurationOptions public bool Migrate { get; set; } = true; - public bool Seed { get; set; } = false; + public bool SeedIdentity { get; set; } = true; + + public bool SeedData { get; set; } = false; } diff --git a/src/Persistence/DbSeeder.cs b/src/Persistence/DbSeeder.cs index 229bc7d..c5f8366 100644 --- a/src/Persistence/DbSeeder.cs +++ b/src/Persistence/DbSeeder.cs @@ -4,103 +4,847 @@ using cuqmbr.TravelGuide.Application.Common.Persistence; using cuqmbr.TravelGuide.Application.Common.Services; using cuqmbr.TravelGuide.Domain.Entities; using cuqmbr.TravelGuide.Domain.Enums; -using Microsoft.Extensions.DependencyInjection; namespace cuqmbr.TravelGuide.Persistence; -public static class DbSeeder +public sealed class DbSeeder : IDisposable { - public static void Seed(IServiceProvider serviceProvider) + private readonly UnitOfWork _unitOfWork; + + private readonly PasswordHasherService _passwordHasher; + + public DbSeeder(UnitOfWork unitOfWork, PasswordHasherService passwordHasher) { - var unitOfWork = - serviceProvider.GetRequiredService(); - - var passwordHasher = - serviceProvider.GetRequiredService(); - + _unitOfWork = unitOfWork; + _passwordHasher = passwordHasher; + } + public void SeedIdentity() + { // Seed Roles + + var datastoreRoles = _unitOfWork.RoleRepository + .GetPageAsync(1, IdentityRole.Enumerations.Count, + CancellationToken.None) + .Result.Items.Select(r => r.Value); + + var identityRoles = IdentityRole.Enumerations.Select(r => r.Value); + + foreach (var role in identityRoles) { - var datastoreRoles = unitOfWork.RoleRepository - .GetPageAsync(1, IdentityRole.Enumerations.Count, - CancellationToken.None) - .Result.Items.Select(r => r.Value); - - var roles = IdentityRole.Enumerations.Select(r => r.Value); - foreach (var role in roles) + if (datastoreRoles.Contains(role)) { - - if (datastoreRoles.Contains(role)) - { - continue; - } - - unitOfWork.RoleRepository.AddOneAsync( - new Role() { Value = role }, - CancellationToken.None).Wait(); + continue; } - unitOfWork.SaveAsync(CancellationToken.None).Wait(); + _unitOfWork.RoleRepository.AddOneAsync( + new Role() { Value = role }, + CancellationToken.None).Wait(); } + _unitOfWork.SaveAsync(CancellationToken.None).Wait(); + + // Seed Accounts + + var isAccountsPresent = + _unitOfWork.AccountRepository.GetPageAsync( + 1, 1, CancellationToken.None) + .Result.Items.Any(); + + if (isAccountsPresent) { - var accounts = - new (string Username, string Email, - string Password, IdentityRole[] Roles)[] - { - ("admin", "admin", "admin", - new [] { IdentityRole.Administrator }), - }; - - var roles = unitOfWork.RoleRepository - .GetPageAsync(1, IdentityRole.Enumerations.Count, - CancellationToken.None) - .Result.Items; - - foreach (var account in accounts) - { - var datastoreAccount = - unitOfWork.AccountRepository.GetOneAsync( - e => e.Email == account.Email, CancellationToken.None) - .Result; - - if (datastoreAccount != null) - { - continue; - } - - var password = Encoding.UTF8.GetBytes(account.Password); - - var salt = RandomNumberGenerator.GetBytes(128 / 8); - var hash = passwordHasher - .HashAsync(password, salt, CancellationToken.None) - .Result; - - var saltBase64 = Convert.ToBase64String(salt); - var hashBase64 = Convert.ToBase64String(hash); - - unitOfWork.AccountRepository.AddOneAsync( - new Account() - { - Username = account.Username, - Email = account.Email, - PasswordHash = hashBase64, - PasswordSalt = saltBase64, - AccountRoles = account.Roles.Select(ar => - new AccountRole() - { - RoleId = roles.Single(dr => dr.Value.Equals(ar)).Id - }) - .ToArray() - }, - CancellationToken.None).Wait(); - } - - unitOfWork.SaveAsync(CancellationToken.None).Wait(); + return; } - unitOfWork.Dispose(); + var accounts = + new (string Username, string Email, IdentityRole[] Roles)[] + { + ("admin", "admin@email.com", + new [] { IdentityRole.Administrator }), + }; + + var roles = _unitOfWork.RoleRepository + .GetPageAsync(1, IdentityRole.Enumerations.Count, + CancellationToken.None) + .Result.Items; + + var datastoreAccounts = + _unitOfWork.AccountRepository.GetPageAsync( + e => accounts.Select(a => a.Email).Contains(e.Email), + 1, accounts.Count(), CancellationToken.None) + .Result.Items; + + foreach (var account in accounts) + { + var datastoreAccount = datastoreAccounts + .SingleOrDefault(a => a.Email == account.Email); + + if (datastoreAccount != null) + { + continue; + } + + var password = Encoding.UTF8.GetBytes(account.Username); + + var salt = RandomNumberGenerator.GetBytes(128 / 8); + var hash = _passwordHasher + .HashAsync(password, salt, CancellationToken.None) + .Result; + + var saltBase64 = Convert.ToBase64String(salt); + var hashBase64 = Convert.ToBase64String(hash); + + _unitOfWork.AccountRepository.AddOneAsync( + new Account() + { + Username = account.Username, + Email = account.Email, + PasswordHash = hashBase64, + PasswordSalt = saltBase64, + AccountRoles = account.Roles.Select(ar => + new AccountRole() + { + RoleId = roles.Single(dr => dr.Value.Equals(ar)).Id + }) + .ToArray() + }, + CancellationToken.None).Wait(); + } + + _unitOfWork.Save(); + } + + public void SeedData() + { + var isAddressesPresent = + _unitOfWork.AddressRepository.GetPageAsync( + 1, 1, CancellationToken.None) + .Result.Items.Any(); + + if (isAddressesPresent) + { + return; + } + + + var countries = new Country[] + { + new Country() + { + Name = "Country 0" + } + }; + + foreach (var country in countries) + { + _unitOfWork.CountryRepository + .AddOneAsync(country, CancellationToken.None) + .Wait(); + } + + + var regions = new Region[] + { + new Region() + { + Name = "Region 0", + Country = countries[0] + }, + new Region() + { + Name = "Region 1", + Country = countries[0] + } + }; + + foreach (var region in regions) + { + _unitOfWork.RegionRepository + .AddOneAsync(region, CancellationToken.None) + .Wait(); + } + + + var cities = new City[] + { + new City() + { + Name = "City 0", + Region = regions[0] + }, + new City() + { + Name = "City 1", + Region = regions[0] + }, + new City() + { + Name = "City 2", + Region = regions[1] + }, + new City() + { + Name = "City 3", + Region = regions[1] + } + }; + + foreach (var city in cities) + { + _unitOfWork.CityRepository + .AddOneAsync(city, CancellationToken.None) + .Wait(); + } + + + var addresses = new Address[] + { + new Address() + { + Name = "Address 0", + Longitude = 0, + Latitude = 0, + VehicleType = VehicleType.Bus, + City = cities[0] + }, + new Address() + { + Name = "Address 1", + Longitude = 1, + Latitude = 1, + VehicleType = VehicleType.Bus, + City = cities[0] + }, + new Address() + { + Name = "Address 2", + Longitude = 2, + Latitude = 2, + VehicleType = VehicleType.Bus, + City = cities[0] + }, + new Address() + { + Name = "Address 3", + Longitude = 3, + Latitude = 3, + VehicleType = VehicleType.Bus, + City = cities[1] + }, + new Address() + { + Name = "Address 4", + Longitude = 4, + Latitude = 4, + VehicleType = VehicleType.Bus, + City = cities[1] + }, + new Address() + { + Name = "Address 5", + Longitude = 5, + Latitude = 5, + VehicleType = VehicleType.Bus, + City = cities[1] + }, + new Address() + { + Name = "Address 6", + Longitude = 6, + Latitude = 6, + VehicleType = VehicleType.Bus, + City = cities[2] + }, + new Address() + { + Name = "Address 7", + Longitude = 7, + Latitude = 7, + VehicleType = VehicleType.Bus, + City = cities[2] + }, + new Address() + { + Name = "Address 8", + Longitude = 8, + Latitude = 8, + VehicleType = VehicleType.Bus, + City = cities[3] + }, + new Address() + { + Name = "Address 9", + Longitude = 9, + Latitude = 9, + VehicleType = VehicleType.Bus, + City = cities[3] + } + }; + + foreach (var address in addresses) + { + _unitOfWork.AddressRepository + .AddOneAsync(address, CancellationToken.None) + .Wait(); + } + + + var routes = new Route[] + { + new Route() + { + Name = "Route 0", + VehicleType = VehicleType.Bus, + RouteAddresses = new RouteAddress[] + { + new RouteAddress() + { + Order = 0, + Address = addresses[3] + }, + new RouteAddress() + { + Order = 1, + Address = addresses[6] + }, + new RouteAddress() + { + Order = 2, + Address = addresses[2] + } + } + }, + new Route() + { + Name = "Route 1", + VehicleType = VehicleType.Bus, + RouteAddresses = new RouteAddress[] + { + new RouteAddress() + { + Order = 0, + Address = addresses[1] + }, + new RouteAddress() + { + Order = 1, + Address = addresses[2] + }, + new RouteAddress() + { + Order = 2, + Address = addresses[7] + }, + new RouteAddress() + { + Order = 3, + Address = addresses[9] + } + } + }, + new Route() + { + Name = "Route 2", + VehicleType = VehicleType.Bus, + RouteAddresses = new RouteAddress[] + { + new RouteAddress() + { + Order = 0, + Address = addresses[0] + }, + new RouteAddress() + { + Order = 1, + Address = addresses[1] + }, + new RouteAddress() + { + Order = 2, + Address = addresses[3] + }, + new RouteAddress() + { + Order = 3, + Address = addresses[4] + }, + new RouteAddress() + { + Order = 4, + Address = addresses[7] + }, + new RouteAddress() + { + Order = 5, + Address = addresses[8] + } + } + } + }; + + foreach (var route in routes) + { + _unitOfWork.RouteRepository + .AddOneAsync(route, CancellationToken.None) + .Wait(); + } + + + var roles = _unitOfWork.RoleRepository + .GetPageAsync(1, IdentityRole.Enumerations.Count, + CancellationToken.None) + .Result.Items; + + var companyOwnerRoleId = roles + .SingleOrDefault( + e => e.Value == IdentityRole.CompanyOwner) + ?.Id; + + if (companyOwnerRoleId == null) + { + throw new InvalidOperationException("Can not find required role."); + } + + var companyOwnerAccountCredentials = + new (string Username, string Email)[] + { + ("company_owner0", "company_owner0@email.com") + }; + + var companyOwnerAccounts = new List(); + + foreach (var accountCredential in companyOwnerAccountCredentials) + { + var password = Encoding.UTF8.GetBytes(accountCredential.Username); + + var salt = RandomNumberGenerator.GetBytes(128 / 8); + var hash = _passwordHasher + .HashAsync(password, salt, CancellationToken.None) + .Result; + + var saltBase64 = Convert.ToBase64String(salt); + var hashBase64 = Convert.ToBase64String(hash); + + companyOwnerAccounts.Add(new Account() + { + Username = accountCredential.Username, + Email = accountCredential.Email, + PasswordHash = hashBase64, + PasswordSalt = saltBase64, + AccountRoles = new AccountRole[] + { + new AccountRole() + { + RoleId = (long)companyOwnerRoleId + } + } + }); + } + + foreach (var account in companyOwnerAccounts) + { + _unitOfWork.AccountRepository + .AddOneAsync(account, CancellationToken.None) + .Wait(); + } + + var companies = new Company[] + { + new Company() + { + Name = "Company 0", + LegalAddress = "Country 0, Region 0, City 0, Address 0", + ContactEmail = "company0@email.com", + ContactPhoneNumber = "+000000000000", + Account = companyOwnerAccounts[0] + } + }; + + foreach (var company in companies) + { + _unitOfWork.CompanyRepository + .AddOneAsync(company, CancellationToken.None) + .Wait(); + } + + + var companyEmployeeRoleId = roles + .SingleOrDefault( + e => e.Value == IdentityRole.CompanyEmployee) + ?.Id; + + if (companyEmployeeRoleId == null) + { + throw new InvalidOperationException("Can not find required role."); + } + + var companyEmployeeAccountCredentials = + new (string Username, string Email)[] + { + ("company_employee0", "company_employee0@email.com"), + ("company_employee1", "company_employee1@email.com"), + ("company_employee2", "company_employee2@email.com") + }; + + var companyEmployeeAccounts = new List(); + var employees = new List(); + + foreach (var accountCredential in companyEmployeeAccountCredentials) + { + var password = Encoding.UTF8.GetBytes(accountCredential.Username); + + var salt = RandomNumberGenerator.GetBytes(128 / 8); + var hash = _passwordHasher + .HashAsync(password, salt, CancellationToken.None) + .Result; + + var saltBase64 = Convert.ToBase64String(salt); + var hashBase64 = Convert.ToBase64String(hash); + + companyEmployeeAccounts.Add(new Account() + { + Username = accountCredential.Username, + Email = accountCredential.Email, + PasswordHash = hashBase64, + PasswordSalt = saltBase64, + AccountRoles = new AccountRole[] + { + new AccountRole() + { + RoleId = (long)companyEmployeeRoleId + } + } + }); + } + + foreach (var account in companyEmployeeAccounts) + { + _unitOfWork.AccountRepository + .AddOneAsync(account, CancellationToken.None) + .Wait(); + + employees.Add(new Employee() + { + FirstName = $"{account.Username}'s fname", + LastName = $"{account.Username}'s lname", + Patronymic = $"{account.Username}'s patronymic", + Sex = + DateTimeOffset.UtcNow.Ticks % 2 == 1 ? + Sex.Male : Sex.Female, + BirthDate = DateOnly.FromDateTime( + DateTimeOffset.UtcNow.Subtract( + TimeSpan.FromDays(365 * 20)).Date), + Company = companies[0], + Documents = new EmployeeDocument[] + { + new EmployeeDocument() + { + DocumentType = DocumentType.Passport, + Information = $"{account.Username}'s passport" + } + }, + Account = account + }); + } + + foreach (var employee in employees) + { + _unitOfWork.EmployeeRepository + .AddOneAsync(employee, CancellationToken.None) + .Wait(); + } + + + var vehicles = new Vehicle[] + { + new Bus() + { + Number = "XX0000XX", + Model = "Model 0", + Capacity = 30, + Company = companies[0] + }, + new Bus() + { + Number = "XX1111XX", + Model = "Model 1", + Capacity = 30, + Company = companies[0] + }, + new Bus() + { + Number = "XX2222XX", + Model = "Model 2", + Capacity = 30, + Company = companies[0] + } + }; + + foreach (var vehicle in vehicles) + { + _unitOfWork.VehicleRepository + .AddOneAsync(vehicle, CancellationToken.None) + .Wait(); + } + + + // Vehicle Enrollments (binding between a vehicle and a route, + // specifying departure date and time) + // and + // Route Address Details (for earch address in the route specifying + // cost and time of travel from current to next address + // and current address stop duration) + + // VE1: 4 -> 7 -> 3 + // \ + // VE2: 2 -> 3 -> 8 -> 10 + // \ + // VE3: 1 -> 2 -> 4 -> 5 -> 8 -> 9 + // ---------------------------------> time + + // Vehicle Enrollment 1 + // + // Route: 4 -> 7 -> 3 + // Departure Time: 2025-01-01 07:30:00.000 + // Time to Next Address: + // 4: P0000-00-00T00:30:00.000 + // 7: P0000-00-00T00:30:00.000 + // 3: P0000-00-00T00:00:00.000 + // Total time moving: 60 minutes + // Cost to Next Address: + // 4: 5 + // 7: 11.50 + // 3: 0 + // Total: 16.50 + // Address Stop duration: + // 4: P0000-00-00T00:00:00.000 + // 7: P0000-00-00T00:10:00.000 + // 3: P0000-00-00T00:00:00.000 + // Total time in stops: 10 minutes + // Total time on road: 70 minutes + // + // Arriave to 3 at 8:40 + + // Vehicle Enrollment 2 + // + // Route: 2 -> 3 -> 8 -> 10 + // Departure Time: 2025-01-01 08:30:00.000 + // Time to Next Address: + // 2: P0000-00-00T00:20:00.000 + // 3: P0000-00-00T00:30:00.000 + // 8: P0000-00-00T00:20:00.000 + // 10: P0000-00-00T00:00:00.000 + // Total time moving: 70 minutes + // Cost to Next Address: + // 2: 4.30 + // 3: 14.71 + // 8: 12.10 + // 10: 0 + // Total: 21.11 + // Address Stop duration: + // 2: P0000-00-00T00:00:00.000 + // 3: P0000-00-00T00:10:00.000 + // 8: P0000-00-00T00:10:00.000 + // 10: P0000-00-00T00:00:00.000 + // Total time in stops: 20 minutes + // Total time on road: 90 minutes + // + // Arrive to 3 at 8:50 + // Arrive to 8 at 9:30 + + // Vehicle Enrollment 3 + // + // Route: 1 -> 2 -> 4 -> 5 -> 8 -> 9 + // Departure Time: 2025-01-01 07:30:00.000 + // Time to Next Address: + // 1: P0000-00-00T00:30:00.000 + // 2: P0000-00-00T00:20:00.000 + // 4: P0000-00-00T00:30:00.000 + // 5: P0000-00-00T00:30:00.000 + // 8: P0000-00-00T00:30:00.000 + // 9: P0000-00-00T00:00:00.000 + // Total time moving: 140 minutes + // Cost to Next Address: + // 1: 5 + // 2: 10 + // 4: 11.2 + // 5: 5.23 + // 8: 5.2 + // 9: 0 + // Total: 36.63 + // Address Stop duration: + // 1: P0000-00-00T00:00:00.000 + // 2: P0000-00-00T00:10:00.000 + // 4: P0000-00-00T00:10:00.000 + // 5: P0000-00-00T00:10:00.000 + // 8: P0000-00-00T00:10:00.000 + // 9: P0000-00-00T00:00:00.000 + // Total time in stops: 40 minutes + // Total time on road: 180 minutes + // + // Arrive to 8 at 9:40 + + var vehicleEnrollments = new VehicleEnrollment[] + { + new VehicleEnrollment() + { + DepartureTime = DateTimeOffset.UtcNow.AddDays(1), + Currency = Currency.EUR, + Vehicle = vehicles[0], + Route = routes[0], + RouteAddressDetails = new RouteAddressDetail[] + { + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 5, + CurrentAddressStopTime = TimeSpan.Zero, + RouteAddress = routes[0].RouteAddresses.ElementAt(0) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 11.5M, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[0].RouteAddresses.ElementAt(1) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.Zero, + CostToNextAddress = 0, + CurrentAddressStopTime = TimeSpan.Zero, + RouteAddress = routes[0].RouteAddresses.ElementAt(2) + } + }, + VehicleEnrollmentEmployees = new VehicleEnrollmentEmployee[] + { + new VehicleEnrollmentEmployee() + { + Employee = employees[0] + } + } + }, + new VehicleEnrollment() + { + DepartureTime = DateTimeOffset.UtcNow.AddDays(1).AddHours(1), + Currency = Currency.UAH, + Vehicle = vehicles[1], + Route = routes[1], + RouteAddressDetails = new RouteAddressDetail[] + { + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(20), + CostToNextAddress = 4.3M, + CurrentAddressStopTime = TimeSpan.Zero, + RouteAddress = routes[1].RouteAddresses.ElementAt(0) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 14.71M, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[1].RouteAddresses.ElementAt(1) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(20), + CostToNextAddress = 12.1M, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[1].RouteAddresses.ElementAt(2) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.Zero, + CostToNextAddress = 0, + CurrentAddressStopTime = TimeSpan.Zero, + RouteAddress = routes[1].RouteAddresses.ElementAt(3) + } + }, + VehicleEnrollmentEmployees = new VehicleEnrollmentEmployee[] + { + new VehicleEnrollmentEmployee() + { + Employee = employees[1] + } + } + }, + new VehicleEnrollment() + { + DepartureTime = DateTimeOffset.UtcNow.AddDays(1), + Currency = Currency.USD, + Vehicle = vehicles[2], + Route = routes[2], + RouteAddressDetails = new RouteAddressDetail[] + { + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 5, + CurrentAddressStopTime = TimeSpan.Zero, + RouteAddress = routes[2].RouteAddresses.ElementAt(0) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(20), + CostToNextAddress = 10, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[2].RouteAddresses.ElementAt(1) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 11.2M, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[2].RouteAddresses.ElementAt(2) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 5.23M, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[2].RouteAddresses.ElementAt(3) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.FromMinutes(30), + CostToNextAddress = 5.2M, + CurrentAddressStopTime = TimeSpan.FromMinutes(10), + RouteAddress = routes[2].RouteAddresses.ElementAt(4) + }, + new RouteAddressDetail() + { + TimeToNextAddress = TimeSpan.Zero, + CostToNextAddress = 0, + CurrentAddressStopTime = TimeSpan.Zero, + RouteAddress = routes[2].RouteAddresses.ElementAt(5) + } + }, + VehicleEnrollmentEmployees = new VehicleEnrollmentEmployee[] + { + new VehicleEnrollmentEmployee() + { + Employee = employees[2] + } + } + }, + }; + + foreach (var enrollment in vehicleEnrollments) + { + _unitOfWork.VehicleEnrollmentRepository + .AddOneAsync(enrollment, CancellationToken.None) + .Wait(); + } + + + _unitOfWork.Save(); + } + + public void Dispose() + { + _unitOfWork.Dispose(); } }