add data seeding
Some checks failed
/ build-docker (push) Has been skipped
/ tests (push) Failing after 33s
/ build (push) Successful in 5m24s

This commit is contained in:
cuqmbr 2025-06-09 18:36:51 +03:00
parent 44dceee7b8
commit fbe26898fa
Signed by: cuqmbr
GPG Key ID: 0AA446880C766199
3 changed files with 853 additions and 83 deletions

View File

@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore;
using cuqmbr.TravelGuide.Persistence.PostgreSql; using cuqmbr.TravelGuide.Persistence.PostgreSql;
using cuqmbr.TravelGuide.Persistence.InMemory; using cuqmbr.TravelGuide.Persistence.InMemory;
using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Diagnostics;
using cuqmbr.TravelGuide.Application.Common.Services;
namespace cuqmbr.TravelGuide.Configuration.Persistence; namespace cuqmbr.TravelGuide.Configuration.Persistence;
@ -76,10 +77,33 @@ public static class Configuration
$"{configuration.Type} datastore is not supported."); $"{configuration.Type} datastore is not supported.");
} }
if (configuration.Seed) if (configuration.SeedIdentity || configuration.SeedData)
{ {
using var serviceProvider = services.BuildServiceProvider(); using var serviceProvider = services.BuildServiceProvider();
DbSeeder.Seed(serviceProvider);
var unitOfWork =
serviceProvider.GetRequiredService<UnitOfWork>();
var passwordHasher =
serviceProvider.GetRequiredService<PasswordHasherService>();
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; return services;

View File

@ -12,5 +12,7 @@ public sealed class ConfigurationOptions
public bool Migrate { get; set; } = true; public bool Migrate { get; set; } = true;
public bool Seed { get; set; } = false; public bool SeedIdentity { get; set; } = true;
public bool SeedData { get; set; } = false;
} }

View File

@ -4,103 +4,847 @@ using cuqmbr.TravelGuide.Application.Common.Persistence;
using cuqmbr.TravelGuide.Application.Common.Services; using cuqmbr.TravelGuide.Application.Common.Services;
using cuqmbr.TravelGuide.Domain.Entities; using cuqmbr.TravelGuide.Domain.Entities;
using cuqmbr.TravelGuide.Domain.Enums; using cuqmbr.TravelGuide.Domain.Enums;
using Microsoft.Extensions.DependencyInjection;
namespace cuqmbr.TravelGuide.Persistence; 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 = _unitOfWork = unitOfWork;
serviceProvider.GetRequiredService<UnitOfWork>(); _passwordHasher = passwordHasher;
}
var passwordHasher =
serviceProvider.GetRequiredService<PasswordHasherService>();
public void SeedIdentity()
{
// Seed Roles // 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))
{ {
continue;
if (datastoreRoles.Contains(role))
{
continue;
}
unitOfWork.RoleRepository.AddOneAsync(
new Role() { Value = role },
CancellationToken.None).Wait();
} }
unitOfWork.SaveAsync(CancellationToken.None).Wait(); _unitOfWork.RoleRepository.AddOneAsync(
new Role() { Value = role },
CancellationToken.None).Wait();
} }
_unitOfWork.SaveAsync(CancellationToken.None).Wait();
// Seed Accounts // Seed Accounts
var isAccountsPresent =
_unitOfWork.AccountRepository.GetPageAsync(
1, 1, CancellationToken.None)
.Result.Items.Any();
if (isAccountsPresent)
{ {
var accounts = return;
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();
} }
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<Account>();
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<Account>();
var employees = new List<Employee>();
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();
} }
} }