Created
September 13, 2023 15:40
-
-
Save EdCharbeneau/4a4c038320557cd81e8917fed459c2f3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Configuration; | |
using System.Data.Entity; | |
using System.Globalization; | |
using System.IO; | |
using System.IO.Compression; | |
using System.Linq; | |
using System.Text.RegularExpressions; | |
namespace eShopLegacyWebForms.Models.Infrastructure | |
{ | |
public class CatalogDBInitializer : CreateDatabaseIfNotExists<CatalogDBContext> | |
{ | |
private readonly bool useCustomizationData; | |
private readonly string contentRootPath; | |
private const string DBCatalogSequenceName = "catalog_type_hilo"; | |
private const string DBBrandSequenceName = "catalog_brand_hilo"; | |
private const string CatalogItemHiLoSequenceScript = @"Models\Infrastructure\dbo.catalog_hilo.Sequence.sql"; | |
private const string CatalogBrandHiLoSequenceScript = @"Models\Infrastructure\dbo.catalog_brand_hilo.Sequence.sql"; | |
private const string CatalogTypeHiLoSequenceScript = @"Models\Infrastructure\dbo.catalog_type_hilo.Sequence.sql"; | |
private CatalogItemHiLoGenerator indexGenerator; | |
public CatalogDBInitializer(CatalogItemHiLoGenerator indexGenerator, IConfiguration configuration, IWebHostEnvironment hostingEnvironment) | |
{ | |
this.indexGenerator = indexGenerator; | |
//useCustomizationData = bool.Parse(ConfigurationManager.AppSettings["UseCustomizationData"]); | |
useCustomizationData = bool.Parse(configuration["UseCustomizationData"]!); | |
contentRootPath = hostingEnvironment.ContentRootPath; | |
} | |
protected override void Seed(CatalogDBContext context) | |
{ | |
ExecuteScript(context, CatalogItemHiLoSequenceScript); | |
ExecuteScript(context, CatalogBrandHiLoSequenceScript); | |
ExecuteScript(context, CatalogTypeHiLoSequenceScript); | |
AddCatalogTypes(context); | |
AddCatalogBrands(context); | |
AddCatalogItems(context); | |
AddCatalogItemPictures(); | |
} | |
private void AddCatalogTypes(CatalogDBContext context) | |
{ | |
var preconfiguredTypes = useCustomizationData | |
? GetCatalogTypesFromFile() | |
: PreconfiguredData.GetPreconfiguredCatalogTypes(); | |
int sequenceId = GetSequenceIdFromSelectedDBSequence(context, DBCatalogSequenceName); | |
foreach (var type in preconfiguredTypes) | |
{ | |
type.Id = sequenceId; | |
context.CatalogTypes.Add(type); | |
sequenceId++; | |
} | |
context.SaveChanges(); | |
} | |
private void AddCatalogBrands(CatalogDBContext context) | |
{ | |
var preconfiguredBrands = useCustomizationData | |
? GetCatalogBrandsFromFile(contentRootPath) | |
: PreconfiguredData.GetPreconfiguredCatalogBrands(); | |
int sequenceId = GetSequenceIdFromSelectedDBSequence(context, DBBrandSequenceName); | |
foreach (var brand in preconfiguredBrands) | |
{ | |
brand.Id = sequenceId; | |
context.CatalogBrands.Add(brand); | |
sequenceId++; | |
} | |
context.SaveChanges(); | |
} | |
private void AddCatalogItems(CatalogDBContext context) | |
{ | |
var preconfiguredItems = useCustomizationData | |
? GetCatalogItemsFromFile(context, contentRootPath) | |
: PreconfiguredData.GetPreconfiguredCatalogItems(); | |
foreach (var item in preconfiguredItems) | |
{ | |
var sequenceId = indexGenerator.GetNextSequenceValue(context); | |
item.Id = sequenceId; | |
context.CatalogItems.Add(item); | |
} | |
context.SaveChanges(); | |
} | |
private IEnumerable<CatalogType> GetCatalogTypesFromFile() | |
{ | |
string csvFileCatalogTypes = Path.Combine(contentRootPath, "Setup", "CatalogTypes.csv"); | |
if (!File.Exists(csvFileCatalogTypes)) | |
{ | |
return PreconfiguredData.GetPreconfiguredCatalogTypes(); | |
} | |
string[] csvheaders; | |
string[] requiredHeaders = { "catalogtype" }; | |
csvheaders = GetHeaders(csvFileCatalogTypes, requiredHeaders); | |
return File.ReadAllLines(csvFileCatalogTypes) | |
.Skip(1) // skip header row | |
.Select(x => CreateCatalogType(x)) | |
.Where(x => x != null); | |
} | |
static CatalogType CreateCatalogType(string type) | |
{ | |
type = type.Trim('"').Trim(); | |
if (String.IsNullOrEmpty(type)) | |
{ | |
throw new Exception("catalog Type Name is empty"); | |
} | |
return new CatalogType | |
{ | |
Type = type, | |
}; | |
} | |
static IEnumerable<CatalogBrand> GetCatalogBrandsFromFile(string contentRootPath) | |
{ | |
string csvFileCatalogBrands = Path.Combine(contentRootPath, "Setup", "CatalogBrands.csv"); | |
if (!File.Exists(csvFileCatalogBrands)) | |
{ | |
return PreconfiguredData.GetPreconfiguredCatalogBrands(); | |
} | |
string[] csvheaders; | |
string[] requiredHeaders = { "catalogbrand" }; | |
csvheaders = GetHeaders(csvFileCatalogBrands, requiredHeaders); | |
return File.ReadAllLines(csvFileCatalogBrands) | |
.Skip(1) // skip header row | |
.Select(x => CreateCatalogBrand(x)) | |
.Where(x => x != null); | |
} | |
static CatalogBrand CreateCatalogBrand(string brand) | |
{ | |
brand = brand.Trim('"').Trim(); | |
if (String.IsNullOrEmpty(brand)) | |
{ | |
throw new Exception("catalog Brand Name is empty"); | |
} | |
return new CatalogBrand | |
{ | |
Brand = brand, | |
}; | |
} | |
static IEnumerable<CatalogItem> GetCatalogItemsFromFile(CatalogDBContext context, string contentRootPath) | |
{ | |
string csvFileCatalogItems = Path.Combine(contentRootPath, "Setup", "CatalogItems.csv"); | |
if (!File.Exists(csvFileCatalogItems)) | |
{ | |
return PreconfiguredData.GetPreconfiguredCatalogItems(); | |
} | |
string[] csvheaders; | |
string[] requiredHeaders = { "catalogtypename", "catalogbrandname", "description", "name", "price", "pictureFileName" }; | |
string[] optionalheaders = { "availablestock", "restockthreshold", "maxstockthreshold", "onreorder" }; | |
csvheaders = GetHeaders(csvFileCatalogItems, requiredHeaders, optionalheaders); | |
var catalogTypeIdLookup = context.CatalogTypes.ToDictionary(ct => ct.Type, ct => ct.Id); | |
var catalogBrandIdLookup = context.CatalogBrands.ToDictionary(ct => ct.Brand, ct => ct.Id); | |
return File.ReadAllLines(csvFileCatalogItems) | |
.Skip(1) // skip header row | |
.Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)")) | |
.Select(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup)) | |
.Where(x => x != null); | |
} | |
static CatalogItem CreateCatalogItem(string[] column, string[] headers, Dictionary<String, int> catalogTypeIdLookup, Dictionary<String, int> catalogBrandIdLookup) | |
{ | |
if (column.Count() != headers.Count()) | |
{ | |
throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); | |
} | |
string catalogTypeName = column[Array.IndexOf(headers, "catalogtypename")].Trim('"').Trim(); | |
if (!catalogTypeIdLookup.ContainsKey(catalogTypeName)) | |
{ | |
throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); | |
} | |
string catalogBrandName = column[Array.IndexOf(headers, "catalogbrandname")].Trim('"').Trim(); | |
if (!catalogBrandIdLookup.ContainsKey(catalogBrandName)) | |
{ | |
throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); | |
} | |
string priceString = column[Array.IndexOf(headers, "price")].Trim('"').Trim(); | |
if (!Decimal.TryParse(priceString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out Decimal price)) | |
{ | |
throw new Exception($"price={priceString}is not a valid decimal number"); | |
} | |
var catalogItem = new CatalogItem() | |
{ | |
CatalogTypeId = catalogTypeIdLookup[catalogTypeName], | |
CatalogBrandId = catalogBrandIdLookup[catalogBrandName], | |
Description = column[Array.IndexOf(headers, "description")].Trim('"').Trim(), | |
Name = column[Array.IndexOf(headers, "name")].Trim('"').Trim(), | |
Price = price, | |
PictureFileName = column[Array.IndexOf(headers, "picturefilename")].Trim('"').Trim(), | |
}; | |
int availableStockIndex = Array.IndexOf(headers, "availablestock"); | |
if (availableStockIndex != -1) | |
{ | |
string availableStockString = column[availableStockIndex].Trim('"').Trim(); | |
if (!String.IsNullOrEmpty(availableStockString)) | |
{ | |
if (int.TryParse(availableStockString, out int availableStock)) | |
{ | |
catalogItem.AvailableStock = availableStock; | |
} | |
else | |
{ | |
throw new Exception($"availableStock={availableStockString} is not a valid integer"); | |
} | |
} | |
} | |
int restockThresholdIndex = Array.IndexOf(headers, "restockthreshold"); | |
if (restockThresholdIndex != -1) | |
{ | |
string restockThresholdString = column[restockThresholdIndex].Trim('"').Trim(); | |
if (!String.IsNullOrEmpty(restockThresholdString)) | |
{ | |
if (int.TryParse(restockThresholdString, out int restockThreshold)) | |
{ | |
catalogItem.RestockThreshold = restockThreshold; | |
} | |
else | |
{ | |
throw new Exception($"restockThreshold={restockThreshold} is not a valid integer"); | |
} | |
} | |
} | |
int maxStockThresholdIndex = Array.IndexOf(headers, "maxstockthreshold"); | |
if (maxStockThresholdIndex != -1) | |
{ | |
string maxStockThresholdString = column[maxStockThresholdIndex].Trim('"').Trim(); | |
if (!String.IsNullOrEmpty(maxStockThresholdString)) | |
{ | |
if (int.TryParse(maxStockThresholdString, out int maxStockThreshold)) | |
{ | |
catalogItem.MaxStockThreshold = maxStockThreshold; | |
} | |
else | |
{ | |
throw new Exception($"maxStockThreshold={maxStockThreshold} is not a valid integer"); | |
} | |
} | |
} | |
int onReorderIndex = Array.IndexOf(headers, "onreorder"); | |
if (onReorderIndex != -1) | |
{ | |
string onReorderString = column[onReorderIndex].Trim('"').Trim(); | |
if (!String.IsNullOrEmpty(onReorderString)) | |
{ | |
if (bool.TryParse(onReorderString, out bool onReorder)) | |
{ | |
catalogItem.OnReorder = onReorder; | |
} | |
else | |
{ | |
throw new Exception($"onReorder={onReorderString} is not a valid boolean"); | |
} | |
} | |
} | |
return catalogItem; | |
} | |
static string[] GetHeaders(string csvfile, string[] requiredHeaders, string[] optionalHeaders = null) | |
{ | |
string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); | |
if (csvheaders.Count() < requiredHeaders.Count()) | |
{ | |
throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is bigger then csv header count '{csvheaders.Count()}' "); | |
} | |
if (optionalHeaders != null) | |
{ | |
if (csvheaders.Count() > (requiredHeaders.Count() + optionalHeaders.Count())) | |
{ | |
throw new Exception($"csv header count '{csvheaders.Count()}' is larger then required '{requiredHeaders.Count()}' and optional '{optionalHeaders.Count()}' headers count"); | |
} | |
} | |
foreach (var requiredHeader in requiredHeaders) | |
{ | |
if (!csvheaders.Contains(requiredHeader.ToLowerInvariant())) | |
{ | |
throw new Exception($"does not contain required header '{requiredHeader}'"); | |
} | |
} | |
return csvheaders; | |
} | |
private static int GetSequenceIdFromSelectedDBSequence(CatalogDBContext context, string dBSequenceName) | |
{ | |
var rawQuery = context.Database.SqlQuery<Int64>($"SELECT NEXT VALUE FOR {dBSequenceName}"); | |
var sequenceId = (int)rawQuery.Single(); | |
return sequenceId; | |
} | |
private void ExecuteScript(CatalogDBContext context, string scriptFile) | |
{ | |
var scriptFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, scriptFile); | |
context.Database.ExecuteSqlCommand(File.ReadAllText(scriptFilePath)); | |
} | |
private void AddCatalogItemPictures() | |
{ | |
if (!useCustomizationData) | |
{ | |
return; | |
} | |
DirectoryInfo picturePath = new DirectoryInfo(Path.Combine(contentRootPath, "Pics")); | |
foreach (FileInfo file in picturePath.GetFiles()) | |
{ | |
file.Delete(); | |
} | |
string zipFileCatalogItemPictures = Path.Combine(contentRootPath, "Setup", "CatalogItems.zip"); | |
ZipFile.ExtractToDirectory(zipFileCatalogItemPictures, picturePath.ToString()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment