Code-complete v2 vehicle API

This commit is contained in:
2020-07-26 21:52:36 -04:00
parent 48dc190027
commit f2ca3419b0
28 changed files with 1292 additions and 1001 deletions

View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "3.1.6",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace PartSource.Api.Controllers
{
public class BaseApiController : ControllerBase
{
public ActionResult ApiResponse<T>(T response)
{
switch (response)
{
case IList list:
if (list.Count == 0)
{
return NoContent();
}
break;
default:
if (response == null)
{
return NotFound();
}
break;
}
return Ok(response);
}
}
}

View File

@@ -0,0 +1,152 @@
using Microsoft.AspNetCore.Mvc;
using PartSource.Data.Models;
using PartSource.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using NexpartMake = PartSource.Data.Nexpart.Make;
using NexpartModel = PartSource.Data.Nexpart.Model;
using NexpartEngine = PartSource.Data.Nexpart.Engine;
using NexpartSubmodel = PartSource.Data.Nexpart.SubModel;
namespace PartSource.Api.Controllers
{
/// <summary>
/// This controller exists to enable legacy support for the old Nexpart-based vehicle lookup.
/// </summary>
[Obsolete]
[Route("vehicles")]
[ApiController]
public class LegacyVehiclesController : BaseNexpartController
{
private readonly VehicleService _vehicleService;
public LegacyVehiclesController(VehicleService vehicleService)
{
_vehicleService = vehicleService;
}
[HttpGet]
[Route("makes")]
public async Task<ActionResult> GetMakes()
{
IList<VehicleMake> vehicleMakes = await _vehicleService.GetMakes();
IList<NexpartMake> nexpartMakes = new List<NexpartMake>();
foreach (VehicleMake make in vehicleMakes)
{
nexpartMakes.Add(new NexpartMake
{
Id = make.MakeId,
Value = make.Name.Trim()
});
}
return Ok(new { data = new { make = nexpartMakes } });
}
[HttpGet]
[Route("models/makeid/{makeId}/modelyear/{year}")]
public async Task<ActionResult> GetModels(int makeId, int year)
{
IList<VehicleModel> vehicleModels = await _vehicleService.GetModels(makeId, year);
IList<NexpartModel> nexpartModels = new List<NexpartModel>();
foreach (VehicleModel model in vehicleModels)
{
nexpartModels.Add(new NexpartModel
{
Id = model.ModelId,
Value = model.Name.Trim()
});
}
return Ok(new { data = new[] { new { model = nexpartModels } } });
}
[HttpGet]
[Route("basevehicle/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
[Route("basevehicles/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
public async Task<ActionResult> GetBaseVehicle(int makeId, int modelId, int year)
{
BaseVehicle baseVehicle = await _vehicleService.GetBaseVehicle(makeId, modelId, year);
return Ok(new { data = baseVehicle });
}
[HttpGet]
[Route("engines/basevehicleid/{baseVehicleId}")]
[Route("engines/basevehicleid/{baseVehicleId}/submodelid/{subModelId}")]
public async Task<ActionResult> GetEngines(int baseVehicleId, int? submodelId = null)
{
IList<NexpartEngine> nexpartEngines = new List<NexpartEngine>();
IList<Engine> engines = submodelId == null
? await _vehicleService.GetEngines(baseVehicleId)
: await _vehicleService.GetEngines(baseVehicleId, (int)submodelId);
foreach(Engine engine in engines)
{
nexpartEngines.Add(new NexpartEngine
{
Id = engine.EngineConfigId,
Value = engine.Description.Trim()
});
}
return Ok(new { data = new { engine = nexpartEngines } });
}
[HttpGet]
[Route("trim/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
[Route("submodels/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
public async Task<ActionResult> GetSubmodels(int makeId, int modelId, int year)
{
IList<Submodel> submodels = await _vehicleService.GetSubmodels(makeId, modelId, year);
IList<NexpartSubmodel> nexpartSubmodels = new List<NexpartSubmodel>();
foreach (Submodel submodel in submodels)
{
nexpartSubmodels.Add(new NexpartSubmodel
{
Id = submodel.SubmodelId.ToString(),
Description = submodel.Name.Trim()
});
}
return Ok(new { data = new { subModel = submodels } });
}
[HttpGet]
[Route("detail/basevehicleid/{baseVehicleId}/submodelid/{submodelId}/engineconfigid/{engineConfigId}")]
public async Task<ActionResult> GetVehicleDetail(int baseVehicleId, int submodelId, int engineConfigId)
{
Vehicle vehicle = await _vehicleService.GetVehicle(baseVehicleId, engineConfigId, submodelId);
return Ok(new { data = vehicle });
}
[HttpGet]
[Route("types")]
public ActionResult GetVehicleTypes()
{
return new StatusCodeResult((int)HttpStatusCode.Gone);
}
[HttpGet]
[Route("makes/vehicletypeid/{vehicleTypeId}")]
public ActionResult GetMakes(int vehicleTypeId)
{
return new StatusCodeResult((int)HttpStatusCode.Gone);
}
[HttpGet]
[Route("models/makeid/{makeId}/modelyear/{year}/vehicletypeid/{vehicleTypeId}")]
public ActionResult GetModels(int makeId, int year, int vehicleTypeId)
{
return new StatusCodeResult((int)HttpStatusCode.Gone);
}
}
}

View File

@@ -1,101 +1,132 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using PartSource.Data.Dtos;
using PartSource.Data.Models;
using PartSource.Services;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace PartSource.Api.Controllers
{
[Route("[controller]")]
[ApiController]
public class VehiclesController : BaseNexpartController
{
private readonly VehicleService _vehicleService;
[Route("v2/[controller]")]
[ApiController]
public class VehiclesController : BaseApiController
{
private readonly VehicleService _vehicleService;
public VehiclesController(VehicleService vehicleService)
{
this._vehicleService = vehicleService;
}
public VehiclesController(VehicleService vehicleService)
{
_vehicleService = vehicleService;
}
[HttpGet]
[Route("makes")]
public async Task<ActionResult> GetMakes()
{
IList<VehicleMake> makes = await _vehicleService.GetAllMakes();
[HttpGet]
[Route("")]
public async Task<ActionResult> GetVehicles([FromQuery] Vehicle vehicleQuery)
{
IList<Vehicle> vehicles = await _vehicleService.GetVehicles(vehicleQuery);
return Ok(makes);
}
return ApiResponse(vehicles);
}
[HttpGet]
[Route("models/makeid/{makeId}/modelyear/{year}")]
public async Task<ActionResult> GetModels(int makeId, int year)
{
IList<VehicleData> models = await _vehicleService.GetModels(makeId, year);
[HttpGet]
[Route("{id}")]
public async Task<ActionResult> GetVehicleById(int id)
{
Vehicle vehicle = await _vehicleService.GetVehicleById(id);
return Ok(models);
}
return ApiResponse(vehicle);
}
[HttpGet]
[Route("basevehicle/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
[Route("basevehicles/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
public async Task<ActionResult> GetBaseVehicle(int makeId, int modelId, int year)
{
IList<BaseVehicle> baseVehicles = await _vehicleService.GetBaseVehicles(makeId, modelId, year);
[HttpGet]
[Route("makes")]
public async Task<ActionResult> GetMakes([FromQuery] VehicleMake vehicleMake)
{
IList<VehicleMake> makes = await _vehicleService.GetMakes(vehicleMake);
return Ok(baseVehicles);
}
return ApiResponse(makes);
}
[HttpGet]
[Route("engines/basevehicleid/{baseVehicleId}")]
[Route("engines/basevehicleid/{baseVehicleId}/submodelid/{subModelId}")]
public async Task<ActionResult> GetEngines(int baseVehicleId, int? submodelId = null)
{
IList<Engine> engines = submodelId == null
? await _vehicleService.GetEngines(baseVehicleId)
: await _vehicleService.GetEngines(baseVehicleId, (int)submodelId);
[HttpGet]
[Route("makes/{id}")]
public async Task<ActionResult> GetMakeById(int id)
{
VehicleMake make = await _vehicleService.GetMakeById(id);
return Ok(engines);
}
return ApiResponse(make);
}
[HttpGet]
[Route("trim/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
[Route("submodels/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
public async Task<ActionResult> GetSubmodels(int makeId, int modelId, int year)
{
IList<Submodel> submodels = await _vehicleService.GetSubmodels(makeId, modelId, year);
[HttpGet]
[Route("models")]
public async Task<ActionResult> GetModels([FromQuery] VehicleModel vehicleModel)
{
IList<VehicleModel> models = await _vehicleService.GetModels(vehicleModel);
return Ok(submodels);
}
return ApiResponse(models);
}
[HttpGet]
[Route("/detail/basevehicleid/{baseVehicleId}/submodelid/{submodelId}/engineconfigid/{engineConfigId}")]
public async Task<ActionResult> GetVehicleDetail(int baseVehicleId, int submodelId, int engineConfigId)
{
VehicleData vehicle = await _vehicleService.GetVehicle(baseVehicleId, engineConfigId, submodelId);
[HttpGet]
[Route("models/{id}")]
public async Task<ActionResult> GetModelById(int id)
{
VehicleModel model = await _vehicleService.GetModelById(id);
return Ok(vehicle);
}
return ApiResponse(model);
}
[HttpGet]
[Route("types")]
public ActionResult GetVehicleTypes()
{
return new StatusCodeResult((int)HttpStatusCode.Gone);
}
[HttpGet]
[Route("basevehicles")]
public async Task<ActionResult> GetBaseVehicles([FromQuery] BaseVehicle baseVehicle)
{
IList<BaseVehicle> baseVehicles = await _vehicleService.GetBaseVehicles(baseVehicle);
[HttpGet]
[Route("makes/vehicletypeid/{vehicleTypeId}")]
public ActionResult GetMakes(int vehicleTypeId)
{
return new StatusCodeResult((int)HttpStatusCode.Gone);
}
return ApiResponse(baseVehicles);
}
[HttpGet]
[Route("models/makeid/{makeId}/modelyear/{year}/vehicletypeid/{vehicleTypeId}")]
public ActionResult GetModels(int makeId, int year, int vehicleTypeId)
{
return new StatusCodeResult((int)HttpStatusCode.Gone);
}
}
[HttpGet]
[Route("basevehicles/{id}")]
public async Task<ActionResult> GetBaseVehicleById(int id)
{
BaseVehicle baseVehicle = await _vehicleService.GetBaseVehicleById(id);
return ApiResponse(baseVehicle);
}
[HttpGet]
[Route("engines")]
public async Task<ActionResult> GetEngines([FromQuery] Engine engine)
{
IList<Engine> engines = await _vehicleService.GetEngines(engine);
return ApiResponse(engines);
}
[HttpGet]
[Route("engines/{id}")]
public async Task<ActionResult> GetEngineById(int id)
{
EngineDto engine = await _vehicleService.GetEngineById(id);
return ApiResponse(engine);
}
[HttpGet]
[Route("submodels")]
public async Task<ActionResult> GetSubmodels([FromQuery] Submodel submodelQuery)
{
IList<Submodel> submodels = await _vehicleService.GetSubmodels(submodelQuery);
return ApiResponse(submodels);
}
[HttpGet]
[Route("submodels/{id}")]
public async Task<ActionResult> GetSubmodels(int id)
{
SubmodelDto submodel = await _vehicleService.GetSubmodelById(id);
return ApiResponse(submodel);
}
}
}

View File

@@ -23,6 +23,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -24,11 +24,13 @@ namespace PartSource.Api
services.AddMvc(options =>
{
options.OutputFormatters.Add(new LiquidTemplateOutputFormatter());
options.EnableEndpointRouting = false;
});
services.AddTransient<PartService>();
services.AddTransient<NexpartService>();
services.AddTransient<SecurityService>();
services.AddTransient<VehicleService>();
services.AddCors(o => o.AddPolicy("Default", builder =>
{
@@ -45,10 +47,10 @@ namespace PartSource.Api
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//if (env.IsDevelopment())
//{
// app.UseDeveloperExceptionPage();
//}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//else
//{
// app.UseHsts();
@@ -56,7 +58,7 @@ namespace PartSource.Api
app.UseCors("Default");
app.UseExceptionHandler("/Error");
// app.UseExceptionHandler("/Error");
// app.UseHttpsRedirection();
app.UseMvc();
}

View File

@@ -1,6 +1,6 @@
{
"ConnectionStrings": {
"PartSourceDatabase": "Server=(localdb)\\mssqllocaldb;Database=PartSource;Trusted_Connection=True;"
}
"ConnectionStrings": {
"PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
}
}

View File

@@ -23,8 +23,8 @@ namespace PartSource.Automation.Factories
{
switch (jobName)
{
case nameof(AddProducts):
return _serviceProvider.GetService<AddProducts>();
case nameof(AddAndUpdateProducts):
return _serviceProvider.GetService<AddAndUpdateProducts>();
case nameof(DeleteProducts):
return _serviceProvider.GetService<DeleteProducts>();

View File

@@ -0,0 +1,320 @@
using Microsoft.EntityFrameworkCore;
using PartSource.Automation.Jobs.Interfaces;
using PartSource.Automation.Models;
using PartSource.Data;
using PartSource.Data.Models;
using PartSource.Services.Integrations;
using Ratermania.Shopify.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace PartSource.Automation.Jobs
{
public class AddAndUpdateProducts : IAutomationJob
{
private readonly PartSourceContext _partSourceContext;
private readonly ShopifyClient _shopifyClient;
public AddAndUpdateProducts(PartSourceContext partSourceContext, ShopifyClient shopifyClient)
{
_partSourceContext = partSourceContext;
_shopifyClient = shopifyClient;
}
public async Task<AutomationJobResult> Run()
{
//await SyncronizeIdsAndSkus();
//await AddSkus();
await AddVariants();
return new AutomationJobResult
{
IsSuccess = true
};
}
/// <summary>
/// Ensures syncronization between Shopify IDs and Partsource SKUs
/// </summary>
private async Task SyncronizeIdsAndSkus()
{
IEnumerable<Product> products = await _shopifyClient.Products.Get();
_partSourceContext.Database.ExecuteSqlCommand("UPDATE ImportData SET ShopifyId = NULL");
while (products != null && products.Any())
{
foreach (Product product in products)
{
foreach (ProductVariant variant in product.Variants)
{
ImportData importData = _partSourceContext.ImportData.FirstOrDefault(i => i.VariantSku == variant.Sku);
if (importData != null)
{
importData.ShopifyId = product.Id;
}
}
}
await _partSourceContext.SaveChangesAsync();
try
{
products = await _shopifyClient.Products.GetNext();
}
catch
{
products = await _shopifyClient.Products.GetPrevious();
}
}
}
public async Task AddSkus()
{
IList<ImportData> items = _partSourceContext.ImportData
.Where(i => i.IsVariant == null && i.ShopifyId == null)
// .OrderBy(i => i.Title)
.ToList();
// items = items.Where(i => i.Title == items.First().Title).ToList();
//
foreach (ImportData importData in items)
{
try
{
// Images
IList<ProductImage> productImages = new List<ProductImage>();
if (!string.IsNullOrEmpty(importData.ImageSrc))
{
foreach (string src in importData?.ImageSrc.Split(','))
{
productImages.Add(new ProductImage
{
Src = src,
Alt = importData.ImageAltText
});
}
if (productImages.Count > 0)
{
productImages.Add(new ProductImage
{
Src = "https://cdn.shopify.com/s/files/1/2239/4255/files/No_Image_Found.jpg",
Alt = "No Image Found"
});
}
}
// Product Tags
List<string> productTags = new List<string>
{
items[0].LineCode,
items[0].PartNumber,
};
List<ProductVariant> productVariants = new List<ProductVariant>();
//foreach (ImportData itemVariant in items)
//{
productVariants.Add(new ProductVariant
{
InventoryPolicy = "Deny",
CompareAtPrice = importData.CompareAt,
Price = importData.Price,
Sku = importData.VariantSku,
Title = importData.VariantTitle ?? importData.Title,
Option1 = importData.VariantTitle,
RequiresShipping = false,
});
//}
Product requestData = new Product
{
BodyHtml = importData.BodyHtml,
Title = importData.Title,
Vendor = importData.Vendor,
Tags = string.Join(",", productTags),
Published = true,
//ProductType = importData.FINELINE_NM,
Images = productImages.ToArray(),
Variants = productVariants.ToArray(),
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
requestData = await _shopifyClient.Products.Add(requestData);
if (requestData.Id > 0)
{
//foreach (ImportData variant in items)
//{
// variant.ShopifyId = requestData.Id;
//}
importData.ShopifyId = requestData.Id;
_partSourceContext.SaveChanges();
Console.WriteLine($"{importData.VariantSku}");
}
else
{
Console.WriteLine($"SHOPIFY ID WAS 0 - {importData.VariantSku}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Failed to add SKU {importData.VariantSku}: {ex.StackTrace}");
}
//items = _partSourceContext.ImportData
// .Where(i => i.Title == _partSourceContext.ImportData.First(d => d.ShopifyId == null).Title)
// .ToList();
}
}
public async Task AddVariants()
{
IList<ImportData> items = _partSourceContext.ImportData
.Where(i => i.IsVariant.Value && i.ShopifyId == null)
.OrderBy(i => i.Title)
.ToList();
items = items.Where(i => i.Title == items.First().Title).ToList();
while (items != null && items.Count > 0)
{
// Images
IList<ProductImage> productImages = new List<ProductImage>();
if (!string.IsNullOrEmpty(items[0].ImageSrc))
{
productImages = items.SelectMany(v =>
{
IList<ProductImage> images = new List<ProductImage>();
foreach (string src in v.ImageSrc?.Split(','))
{
images.Add(new ProductImage
{
Src = src,
Alt = v.ImageAltText
});
}
return images;
}).ToList();
if (productImages.Count > 0)
{
productImages.Add(new ProductImage
{
Src = "https://cdn.shopify.com/s/files/1/2239/4255/files/No_Image_Found.jpg",
Alt = "No Image Found"
});
}
}
// Product Tags
List<string> productTags = new List<string>
{
items[0].LineCode,
items[0].PartNumber,
};
List<ProductVariant> productVariants = new List<ProductVariant>();
foreach (ImportData itemVariant in items)
{
productVariants.Add(new ProductVariant
{
InventoryPolicy = "Deny",
CompareAtPrice = itemVariant.CompareAt,
Price = itemVariant.Price,
Sku = itemVariant.VariantSku,
Title = itemVariant.VariantTitle,
Option1 = itemVariant.VariantTitle,
RequiresShipping = false,
});
}
Product requestData = new Product
{
BodyHtml = items[0].BodyHtml,
Title = items[0].Title,
Vendor = items[0].Vendor,
Tags = string.Join(",", productTags),
Published = true,
//ProductType = importData.FINELINE_NM,
Images = productImages.ToArray(),
Variants = productVariants.ToArray(),
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
requestData = await _shopifyClient.Products.Add(requestData);
if (requestData.Id > 0)
{
foreach (ImportData variant in items)
{
variant.ShopifyId = requestData.Id;
}
_partSourceContext.SaveChanges();
Console.WriteLine($"{items[0].VariantSku}");
}
else
{
Console.WriteLine($"SHOPIFY ID WAS 0 - {items[0].VariantSku}");
}
ImportData next = _partSourceContext.ImportData.FirstOrDefault(i => i.IsVariant != null && i.ShopifyId == null);
if (next != null)
{
items = _partSourceContext.ImportData
.Where(i => i.Title == next.Title)
.ToList();
}
else
{
items = null;
}
}
}
}
//private void Log(string message)
//{
// try
// {
// using (FileStream fileStream = File.OpenWrite(@"C:\users\tommy\desktop\log.txt"))
// {
// fileStream.Write(Encoding.UTF8.GetBytes(message + "\n"));
// }
// }
// catch
// {
// // LOL Fix this
// Log(message);
// }
//}
}

View File

@@ -1,152 +0,0 @@
using PartSource.Automation.Jobs.Interfaces;
using PartSource.Automation.Models;
using PartSource.Data;
using PartSource.Data.Models;
using PartSource.Services.Integrations;
using Ratermania.Shopify.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace PartSource.Automation.Jobs
{
public class AddProducts : IAutomationJob
{
private readonly PartSourceContext _partSourceContext;
private readonly ShopifyClient _shopifyClient;
public AddProducts(PartSourceContext partSourceContext, ShopifyClient shopifyClient)
{
_partSourceContext = partSourceContext;
_shopifyClient = shopifyClient;
}
public async Task<AutomationJobResult> Run()
{
await AddSkus();
return new AutomationJobResult
{
IsSuccess = true
};
}
public async Task AddSkus()
{
IList<ImportData> items = _partSourceContext.ImportData
.Where(i => i.IsVariant.Value)
.OrderBy(i => i.Title)
.ToList();
items = items.Where(i => i.Title == items.First().Title).ToList();
while (items != null && items.Count > 0)
{
// Images
IList<ProductImage> productImages = items.SelectMany(v => {
IList<ProductImage> images = new List<ProductImage>();
foreach (string src in v.ImageSrc?.Split(','))
{
images.Add(new ProductImage
{
Src = src,
Alt = v.ImageAltText
});
}
return images;
}).ToList();
if (productImages.Count > 0)
{
productImages.Add(new ProductImage
{
Src = "https://cdn.shopify.com/s/files/1/2239/4255/files/No_Image_Found.jpg",
Alt = "No Image Found"
});
}
// Product Tags
List<string> productTags = new List<string>
{
items[0].LineCode,
items[0].PartNumber,
};
List<ProductVariant> productVariants = new List<ProductVariant>();
foreach (ImportData itemVariant in items)
{
productVariants.Add(new ProductVariant
{
InventoryPolicy = "Deny",
CompareAtPrice = itemVariant.CompareAt,
Price = itemVariant.Price,
Sku = itemVariant.VariantSku,
Title = itemVariant.VariantTitle,
Option1 = itemVariant.VariantTitle,
RequiresShipping = false,
});
}
Product requestData = new Product
{
BodyHtml = items[0].BodyHtml,
Title = items[0].Title,
Vendor = items[0].Vendor,
Tags = string.Join(",", productTags),
Published = true,
//ProductType = importData.FINELINE_NM,
Images = productImages.ToArray(),
Variants = productVariants.ToArray(),
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
requestData = await _shopifyClient.Products.Add(requestData);
if (requestData.Id > 0)
{
foreach (ImportData variant in items)
{
variant.ShopifyId = requestData.Id;
}
_partSourceContext.SaveChanges();
Console.WriteLine($"{items[0].VariantSku}");
}
else
{
Console.WriteLine($"SHOPIFY ID WAS 0 - {items[0].VariantSku}");
}
items = _partSourceContext.ImportData
.Where(i => i.Title == _partSourceContext.ImportData.First(d => d.ShopifyId == null).Title)
.ToList();
}
}
}
//private void Log(string message)
//{
// try
// {
// using (FileStream fileStream = File.OpenWrite(@"C:\users\tommy\desktop\log.txt"))
// {
// fileStream.Write(Encoding.UTF8.GetBytes(message + "\n"));
// }
// }
// catch
// {
// // LOL Fix this
// Log(message);
// }
//}
}

View File

@@ -1,535 +1,192 @@
//using Microsoft.EntityFrameworkCore;
//using PartSource.Automation.Jobs.Interfaces;
//using PartSource.Automation.Services;
//using PartSource.Data;
//using PartSource.Data.Models;
//using PartSource.Data.Nexpart;
//using PartSource.Data.Shopify;
//using PartSource.Services;
//using System;
//using System.Collections.Concurrent;
//using System.Collections.Generic;
//using System.Data;
//using System.Data.SqlClient;
//using System.IO;
//using System.Linq;
//using System.Text;
//using System.Text.RegularExpressions;
//using System.Threading.Tasks;
//namespace PartSource.Automation.Jobs
//{
// public class UpdateFitmentCopy : AddProducts, IAutomationJob
// {
// private readonly IServiceProvider _serviceProvider;
// private readonly SuperOldShopifyService _shopifyService;
// private readonly PartSourceContext _partSourceContext;
// private readonly NexpartService _nexpartService;
// private ConcurrentQueue<long> _shopifyIdQueue;
// private string _connectionString = "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=3600;";
// private readonly int _threadCount = 1;
// public UpdateFitmentCopy(IServiceProvider serviceProvider, PartSourceContext partSourceContext, SuperOldShopifyService shopifyService, NexpartService nexpartService) : base(serviceProvider, partSourceContext, nexpartService, shopifyService)
// {
// _serviceProvider = serviceProvider;
// _nexpartService = nexpartService;
// _shopifyService = shopifyService;
// _partSourceContext = partSourceContext;
// }
// new public void Run()
// {
// Console.WriteLine("");
// UpdateMetafields(26);
// //UpdatePublished();
// //FitmentReport();
// // List<long> shopifyIds = _partSourceContext.ImportData.Where(s => s.IsFitment.Value && s.ShopifyId != null).Select(s => (long)s.ShopifyId).ToList();
// //List<long> shopifyIds = _partSourceContext.ImportData.Where(s => s.ShopifyId != null).Select(s => (long)s.ShopifyId).ToList();
// //_shopifyIdQueue = new ConcurrentQueue<long>(shopifyIds);
// //Parallel.For(0, _threadCount, i =>
// // {
// // //Categorize();
// // });
// // });
// }
// private IList<string> GetYmmFitmentTags(BuyersGuideSearchResponse response)
// {
// BuyersGuideMake[] makes = response?.ResponseBody?.Apps?.Make;
// if (makes == null)
// {
// return new List<string>();
// }
// IList<string> fitmentTags = new List<string>();
// foreach (BuyersGuideMake make in makes)
// {
// foreach (BuyersGuideModel model in make.Model)
// {
// IList<int> years = new List<int>();
// for (int year = model.FromYear; year <= model.ToYear; year++)
// {
// years.Add(year);
// }
// string tag = $"{string.Join('-', years)} {make.Name} {model.Name}";
// fitmentTags.Add(tag);
// }
// }
// return fitmentTags;
// }
// //private IList<string> GetVehicleIdFitmentTags(BuyersGuideSearchResponse response)
// //{
// // BuyersGuideMake[] makes = response?.ResponseBody?.Apps?.Make;
// // if (makes == null)
// // {
// // return new List<string>();
// // }
// // IList<string> fitmentTags = new List<string>();
// // foreach (BuyersGuideMake make in makes)
// // {
// // foreach (BuyersGuideModel model in make.Model)
// // {
// // foreach (BuyersGuideEngine engine in model.Engine)
// // {
// // IList<int> vehicleIds = _partSourceContext.VehicleData.Where(d =>
// // d.EngineDescription == engine.Desc
// // && d.Year == engine.Year
// // && d.MakeName == make.Name
// // && d.ModelName == model.Name
// // )
// // .Select(d => d.VehicleToEngineConfigId)
// // .ToList();
// // foreach (int id in vehicleIds)
// // {
// // string tag = $"v{id}";
// // fitmentTags.Add(tag);
// // }
// // }
// // }
// // }
// // return fitmentTags;
// //}
// /* public void UpdateFitmentViaBuyersGuideSearch()
// {
// int i = 0;
// IList<Product> products = _shopifyService.GetManyProducts(250, i).Result;
// while (products.Count > 0)
// {
// foreach (Product product in products)
// {
// try
// {
// //if (product.Tags.Contains("-v") || product.Tags.ToLowerInvariant().Contains("zzzisfitment=false"))
// //{
// // continue;
// //}
// ImportData importData = _partSourceContext.ImportData.FirstOrDefault(p => p.ShopifyId == product.Id);
// if (importData == null || !importData.IsFitment.Value)
// {
// continue;
// }
// importData.DcfMappings = _partSourceContext.DcfMappings.Where(d => d.LineCode == importData.LineCode).ToList();
// BuyersGuideSearch buyersGuideSearch = new BuyersGuideSearch();
// List<string> ymmFitmentTags = new List<string>();
// List<string> vehicleIdFitmentTags = new List<string>();
// foreach (DcfMapping mapping in importData.DcfMappings)
// {
// buyersGuideSearch = new BuyersGuideSearch
// {
// Part = new BuyersGuidePart
// {
// PartNumber = importData.PartNumber,
// MfrCode = mapping.WhiCode
// }
// };
// BuyersGuideSearchResponse response = _nexpartService.SendRequest<BuyersGuideSearch, BuyersGuideSearchResponse>(buyersGuideSearch).Result;
// if (response.ResponseBody != null)
// {
// ymmFitmentTags.AddRange(GetYmmFitmentTags(response));
// //vehicleIdFitmentTags.AddRange(GetVehicleIdFitmentTags(response));
// }
// }
// bool published = true;
// List<string> productTags = new List<string>
// {
// importData.LineCode,
// importData.PartNumber
// };
// published = (ymmFitmentTags.Count > 0 || vehicleIdFitmentTags.Count > 0);
// productTags.Add($"zzzisFitment={importData.IsFitment}");
// productTags.AddRange(vehicleIdFitmentTags);
// productTags.AddRange(ymmFitmentTags);
// if (productTags.Count > 249)
// {
// string message = $"Truncating {importData.VariantSku} - {productTags.Count} product tags";
// // Console.WriteLine(message);
// productTags = productTags.Take(249).ToList();
// }
// published = (ymmFitmentTags.Count > 0 || vehicleIdFitmentTags.Count > 0);
// product.Tags = string.Join(",", productTags);
// product.PublishedAt = published ? (DateTime?)DateTime.Now : null;
// bool updateResult = _shopifyService.UpdateProduct(product).Result;
// if (updateResult)
// {
// if (published)
// {
// Console.WriteLine($"{product.Id}");
// }
// }
// else
// {
// Console.WriteLine($"Failed to update product {product.Id}");
// }
// }
// catch (Exception ex)
// {
// Console.WriteLine($"{ex.Message}");
// }
// }
// i++;
// products = _shopifyService.GetManyProducts(250, i).Result;
// }
// } */
// /* private void UpdatePublished()
// {
// DbContextOptionsBuilder<PartSourceContext> optionsBuilder = new DbContextOptionsBuilder<PartSourceContext>();
// optionsBuilder.UseSqlServer(_connectionString);
// PartSourceContext threadDbContext = new PartSourceContext(optionsBuilder.Options);
// int i = 0;
// int updated = 0;
// IList<Product> products = _shopifyService.GetManyProducts(250, i).Result;
// while (products.Count > 0)
// {
// foreach (Product product in products)
// {
// try
// {
// ImportData importData = threadDbContext.ImportData.FirstOrDefault(p => p.ShopifyId == product.Id);
// if (importData == null || !importData.IsFitment.Value)
// {
// continue;
// }
// if (product.PublishedAt == null)
// {
// if (product.Tags.Contains("-v"))
// {
// IList<string> tags = product.Tags.Split(',');
// string fitmentTag = tags.Where(t => t.ToLowerInvariant().Contains("zzzisfitment")).FirstOrDefault();
// tags.Remove(fitmentTag);
// tags.Add("zzzisFitment=True");
// product.Tags = string.Join(',', tags);
// product.PublishedAt = DateTime.Now;
// bool result = _shopifyService.UpdateProduct(product).Result;
// updated++;
// }
// }
// }
// catch (Exception ex)
// {
// continue;
// }
// }
// i++;
// products = _shopifyService.GetManyProducts(250, i).Result;
// Console.Write($"\ri={i}");
// }
// } */
// private void FitmentReport()
// {
// IList<ImportData> importCache = _partSourceContext.ImportData.Where(s => s.IsFitment.Value && s.ShopifyId != null).ToList();
// IList<DcfMapping> mappingCache = _partSourceContext.DcfMappings.ToList();
// Regex regex = new Regex("v[0-9]{6}");
// using (StreamWriter writer = File.AppendText("c:\\users\\tommy\\desktop\\fitment report.csv"))
// {
// writer.WriteLine("Line Code,Part Number,WHI Match");
// int i = 0;
// IList<Product> products = _shopifyService.GetManyProducts(250, i).Result;
// while (products.Count > 0)
// {
// try
// {
// foreach (Product product in products)
// {
// ImportData importData = importCache.Join(
// mappingCache,
// d => d.LineCode,
// m => m.LineCode,
// (d, m) => d
// )
// .FirstOrDefault(p => p.ShopifyId == product.Id);
// if (importData == null || !importData.IsFitment.Value || product.Variants.Length == 0)
// {
// continue;
// }
// bool tagged = regex.IsMatch(product.Tags);
// if (!tagged)
// {
// // IList<string> whiCodes = mappingCache.Where(d => d.LineCode == importData.LineCode).Select(d => d.WhiCode).ToList();
// writer.WriteLine($"{importData.LineCode},{importData.PartNumber},{tagged}");
// }
// }
// i++;
// products = _shopifyService.GetManyProducts(250, i).Result;
// }
// catch (Exception ex)
// {
// Console.WriteLine("whoops");
// }
// }
// }
// }
// private void UpdateMetafields(int i)
// {
// DbContextOptionsBuilder<PartSourceContext> optionsBuilder = new DbContextOptionsBuilder<PartSourceContext>();
// optionsBuilder.UseSqlServer(_connectionString);
// ShopifyClient shopifyClient = new ShopifyClient(new Ratermania.Shopify.ShopifyOptionsBuilder
// {
// ShopDomain = "partsource.myshopify.com",
// ApiVersion = "2020-01",
// ApiKey = "88f931933b566ade1fc92c6a39f04b34",
// ApiSecret = "527a3b4213c2c7ecb214728a899052df"
// });
// PartSourceContext threadDbContext = new PartSourceContext(optionsBuilder.Options);
// threadDbContext.Database.SetCommandTimeout(1440);
// IList<Product> products = _shopifyService.GetManyProducts(250, i).Result;
// while (products.Count > 0)
// {
// try
// {
// foreach (Product product in products)
// {
// try
// {
// if (product.Tags.Contains("-v") || product.Tags.ToLowerInvariant().Contains("zzzisfitment=false"))
// {
// continue;
// }
// ImportData test = threadDbContext.ImportData.First();
// ImportData importData = threadDbContext.ImportData.FirstOrDefault(p => p.ShopifyId == product.Id);
// if (importData == null)
// {
// continue;
// }
// bool published = true;
// List<string> productTags = new List<string>
// {
// importData.LineCode,
// importData.PartNumber
// };
// IDictionary<string, IList<string>> positions = new Dictionary<string, IList<string>>();
// string partNumber = Regex.Replace(importData.PartNumber, "[^a-zA-Z0-9]", string.Empty);
// string sql = $"select distinct BaseVehicleId, EngineConfigId, LineCode, PartNumber, Position from dbo.Fitment where LineCode in (select WhiCode from DcfMapping where LineCode='{importData.LineCode}') and PartNumber = '{partNumber}'";
// IList<Fitment> fitments = _partSourceContext.Fitments.FromSql(sql).ToList();
// foreach (Fitment fitment in fitments)
// {
// fitment.Position = fitment.Position.Replace("\"", string.Empty);
// if (string.IsNullOrEmpty(fitment.Position))
// {
// continue;
// }
// VehicleData vehicle = _partSourceContext.VehicleData.FirstOrDefault(v => v.BaseVehicleId == fitment.BaseVehicleId && v.EngineConfigId == fitment.EngineConfigId);
// if (vehicle == null)
// {
// continue;
// }
// if (positions.ContainsKey(fitment.Position))
// {
// positions[fitment.Position].Add(vehicle.VehicleToEngineConfigId.ToString());
// }
// else
// {
// positions.Add(fitment.Position, new List<string>
// {
// vehicle.VehicleToEngineConfigId.ToString()
// });
// }
// }
// foreach (KeyValuePair<string, IList<string>> position in positions)
// {
// Metafield metafield = new Metafield
// {
// OwnerId = product.Id,
// OwnerResource = "product",
// Namespace = "partsource",
// Key = position.Key,
// Value = string.Join(',', position.Value),
// ValueType = "string"
// };
// var x = shopifyClient.Metafields.Add(metafield).Result;
// Console.WriteLine(product.Id);
// }
// }
// catch (Exception ex)
// {
// Console.WriteLine($"{ex.Message}");
// }
// }
// i += _threadCount;
// products = _shopifyService.GetManyProducts(250, i).Result;
// Console.WriteLine(i);
// }
// catch (Exception ex)
// {
// Console.WriteLine($"{ex.Message}");
// }
// }
// }
// private MenuNode[] GetMenuNodes(int menuId, int numberOfLevels, int? parentMenuNodeId = null)
// {
// MenuNodesLookup request = new MenuNodesLookup
// {
// MenuId = menuId,
// NumberOfLevels = numberOfLevels,
// ParentMenuNodeId = parentMenuNodeId
// };
// MenuNodesLookupResponse response = _nexpartService.SendRequest<MenuNodesLookup, MenuNodesLookupResponse>(request).Result;
// return response.ResponseBody.MenuNode;
// }
// //private bool IsFitmentLineCode(string lineCode)
// //{
// // string sql = $"select WhiCode from DcfMapping where PartSourceCode='{importData.LineCode}'";
// // using (DataTable dataTable = new DataTable())
// // {
// // using (SqlConnection connection = new SqlConnection(_connectionString))
// // {
// // connection.Open();
// // using (SqlCommand command = new SqlCommand(sql, connection))
// // {
// // SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
// // dataAdapter.Fill(dataTable);
// // }
// // }
// // if (dataTable.Rows.Count == 0)
// // {
// // return false;
// // }
// // lineCode = dataTable.Rows[0]["WhiCode"].ToString();
// // }
// //}
// }
//}
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using PartSource.Automation.Jobs.Interfaces;
using PartSource.Automation.Models;
using PartSource.Automation.Services;
using PartSource.Data;
using PartSource.Data.Models;
using PartSource.Data.Nexpart;
using PartSource.Services;
using PartSource.Services.Integrations;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Ratermania.Shopify.Entities;
namespace PartSource.Automation.Jobs
{
public class UpdateFitment : IAutomationJob
{
private readonly IServiceProvider _serviceProvider;
private readonly ShopifyClient _shopifyClient;
private readonly PartSourceContext _partSourceContext;
private readonly NexpartService _nexpartService;
private readonly VehicleService _vehicleService;
public UpdateFitment(IServiceProvider serviceProvider, PartSourceContext partSourceContext, ShopifyClient shopifyClient, NexpartService nexpartService, VehicleService vehicleService)
{
_partSourceContext = partSourceContext;
_shopifyClient = shopifyClient;
_vehicleService = vehicleService;
}
public async Task<AutomationJobResult> Run()
{
IList<ImportData> parts = _partSourceContext.ImportData.Where(i => new[] { "Raybestos Brakes", "MotoMaster", "Pro Series OE Plus Brake Pads" }.Contains(i.Vendor)).ToList();
foreach (ImportData importData in parts)
{
try
{
Product product = await _shopifyClient.Products.GetById((long)importData.ShopifyId);
if (product == null)
{
continue;
}
bool isFitment = false;
IList<Vehicle> vehicles = _vehicleService.GetVehiclesForPart(importData.PartNumber, importData.LineCode);
IList<int> vehicleIdFitment = _vehicleService.GetVehicleIdFitment(vehicles);
if (vehicleIdFitment.Count > 0)
{
isFitment = true;
string json = JsonConvert.SerializeObject(vehicleIdFitment);
if (json.Length >= 100000)
{
continue;
}
Metafield vehicleMetafield = new Metafield
{
Namespace = "fitment",
Key = "ids",
Value = json,
ValueType = "json_string",
OwnerResource = "product",
OwnerId = product.Id
};
await _shopifyClient.Metafields.Add(vehicleMetafield);
}
IList<string> ymmFitment = _vehicleService.GetYmmFitment(vehicles);
if (ymmFitment.Count > 0)
{
isFitment = true;
string json = JsonConvert.SerializeObject(ymmFitment);
if (json.Length >= 100000)
{
continue;
}
Metafield ymmMetafield = new Metafield
{
Namespace = "fitment",
Key = "seo",
Value = json,
ValueType = "json_string",
OwnerResource = "product",
OwnerId = product.Id
};
await _shopifyClient.Metafields.Add(ymmMetafield);
}
Metafield isFitmentMetafield = new Metafield
{
Namespace = "Flags",
Key = "IsFitment",
Value = isFitment.ToString(),
ValueType = "string",
OwnerResource = "product",
OwnerId = product.Id
};
await _shopifyClient.Metafields.Add(isFitmentMetafield);
List<string> tags = new List<string>
{
importData.LineCode,
importData.PartNumber
};
for (int i = 0; i < vehicleIdFitment.Count; i += 25)
{
tags.Add(string.Join('-', vehicleIdFitment.Skip(i).Take(25).Select(i => $"v{i}")));
}
tags.AddRange(ymmFitment);
if (tags.Count > 250)
{
tags = tags.Take(250).ToList();
}
product.Tags = string.Join(',', tags);
await _shopifyClient.Products.Update(product);
}
catch (Exception ex)
{
Console.WriteLine($"{importData.VariantSku}: {ex.Message}");
}
}
return new AutomationJobResult
{
IsSuccess = true
};
}
private async Task DeleteFitmentMetafields(long shopifyId)
{
IDictionary<string, object> parameters = new Dictionary<string, object>
{
{ "metafield[owner_id]", shopifyId},
{ "metafield[owner_resource]", "product" },
{ "namespace", "fitment" },
};
IEnumerable<Metafield> metafields = await _shopifyClient.Metafields.Get(parameters);
foreach (Metafield metafield in metafields)
{
await _shopifyClient.Metafields.Delete(metafield);
}
}
public IList<Vehicle> GetVehicles(string partNumber, string lineCode)
{
partNumber = Regex.Replace(partNumber, "[^a-zA-Z0-9]", string.Empty);
//string sql = $"select distinct BaseVehicleId, EngineConfigId from dbo.Fitment where ManufacturerCode in (select WhiCode from DcfMapping where PartSourceCode='{lineCode}') and (partNumber = '{partNumber}' or partNumber = '{partNumber.Replace("-", string.Empty)}')";
string sql = $"with FitmentIds (BaseVehicleId, EngineConfigId) as (select distinct BaseVehicleId, EngineConfigId from dbo.Fitment where LineCode in (select WhiCode from DcfMapping where LineCode='{lineCode}') and PartNumber = '{partNumber}') select v.* from VehicleData v join FitmentIds f on v.BaseVehicleId = f.BaseVehicleId and v.EngineConfigId = f.EngineConfigId;";
#pragma warning disable EF1000 // Possible SQL injection vulnerability.
IList<Vehicle> vehicles = _partSourceContext.Vehicles.FromSql(sql).ToList();
#pragma warning restore EF1000 // Possible SQL injection vulnerability.
return vehicles;
}
private void Update()
{
}
}
}

View File

@@ -1,200 +1,200 @@
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using PartSource.Automation.Jobs.Interfaces;
using PartSource.Automation.Models;
using PartSource.Automation.Services;
using PartSource.Data;
using PartSource.Data.Models;
using PartSource.Data.Nexpart;
using PartSource.Services;
using PartSource.Services.Integrations;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Ratermania.Shopify.Entities;
//using Microsoft.EntityFrameworkCore;
//using Newtonsoft.Json;
//using PartSource.Automation.Jobs.Interfaces;
//using PartSource.Automation.Models;
//using PartSource.Automation.Services;
//using PartSource.Data;
//using PartSource.Data.Models;
//using PartSource.Data.Nexpart;
//using PartSource.Services;
//using PartSource.Services.Integrations;
//using System;
//using System.Collections.Concurrent;
//using System.Collections.Generic;
//using System.Data;
//using System.Data.SqlClient;
//using System.IO;
//using System.Linq;
//using System.Text;
//using System.Text.RegularExpressions;
//using System.Threading.Tasks;
//using Ratermania.Shopify.Entities;
namespace PartSource.Automation.Jobs
{
public class UpdateFitment : IAutomationJob
{
private readonly IServiceProvider _serviceProvider;
private readonly ShopifyClient _shopifyClient;
private readonly PartSourceContext _partSourceContext;
private readonly NexpartService _nexpartService;
//namespace PartSource.Automation.Jobs
//{
// public class UpdateFitment : IAutomationJob
// {
// private readonly IServiceProvider _serviceProvider;
// private readonly ShopifyClient _shopifyClient;
// private readonly PartSourceContext _partSourceContext;
// private readonly NexpartService _nexpartService;
private readonly VehicleService _vehicleService;
// private readonly VehicleService _vehicleService;
public UpdateFitment(IServiceProvider serviceProvider, PartSourceContext partSourceContext, ShopifyClient shopifyClient, NexpartService nexpartService, VehicleService vehicleService)
{
_partSourceContext = partSourceContext;
_shopifyClient = shopifyClient;
_vehicleService = vehicleService;
}
// public UpdateFitment(IServiceProvider serviceProvider, PartSourceContext partSourceContext, ShopifyClient shopifyClient, NexpartService nexpartService, VehicleService vehicleService)
// {
// _partSourceContext = partSourceContext;
// _shopifyClient = shopifyClient;
// _vehicleService = vehicleService;
// }
public async Task<AutomationJobResult> Run()
{
IEnumerable<Product> products = await _shopifyClient.Products.Get();
// public async Task<AutomationJobResult> Run()
// {
// IEnumerable<Product> products = await _shopifyClient.Products.Get();
int i = 0;
// int i = 0;
while (products != null && products.Any())
{
foreach (Product product in products)
{
try
{
ImportData importData = _partSourceContext.ImportData.FirstOrDefault(i => i.VariantSku == product.Variants[0].Sku && i.UpdatedAt == null);
IList<VehicleData> vehicles = _vehicleService.GetVehiclesForPart(importData?.PartNumber, importData?.LineCode);
// while (products != null && products.Any())
// {
// foreach (Product product in products)
// {
// try
// {
// ImportData importData = _partSourceContext.ImportData.FirstOrDefault(i => i.VariantSku == product.Variants[0].Sku && i.UpdatedAt == null);
// IList<VehicleData> vehicles = _vehicleService.GetVehiclesForPart(importData?.PartNumber, importData?.LineCode);
if (vehicles == null || vehicles.Count == 0)
{
continue;
}
// if (vehicles == null || vehicles.Count == 0)
// {
// continue;
// }
await DeleteFitmentMetafields(product.Id);
// await DeleteFitmentMetafields(product.Id);
bool isFitment = false;
// bool isFitment = false;
IList<int> vehicleIdFitment = _vehicleService.GetVehicleIdFitment(vehicles);
if (vehicleIdFitment.Count > 0)
{
isFitment = true;
// IList<int> vehicleIdFitment = _vehicleService.GetVehicleIdFitment(vehicles);
// if (vehicleIdFitment.Count > 0)
// {
// isFitment = true;
string json = JsonConvert.SerializeObject(vehicleIdFitment);
if (json.Length >= 100000)
{
continue;
}
// string json = JsonConvert.SerializeObject(vehicleIdFitment);
// if (json.Length >= 100000)
// {
// continue;
// }
Metafield vehicleMetafield = new Metafield
{
Namespace = "fitment",
Key = "ids",
Value = json,
ValueType = "json_string",
OwnerResource = "product",
OwnerId = product.Id
};
// Metafield vehicleMetafield = new Metafield
// {
// Namespace = "fitment",
// Key = "ids",
// Value = json,
// ValueType = "json_string",
// OwnerResource = "product",
// OwnerId = product.Id
// };
await _shopifyClient.Metafields.Add(vehicleMetafield);
}
// await _shopifyClient.Metafields.Add(vehicleMetafield);
// }
IList<string> ymmFitment = _vehicleService.GetYmmFitment(vehicles);
if (ymmFitment.Count > 0)
{
isFitment = true;
// IList<string> ymmFitment = _vehicleService.GetYmmFitment(vehicles);
// if (ymmFitment.Count > 0)
// {
// isFitment = true;
string json = JsonConvert.SerializeObject(ymmFitment);
if (json.Length >= 100000)
{
continue;
}
// string json = JsonConvert.SerializeObject(ymmFitment);
// if (json.Length >= 100000)
// {
// continue;
// }
Metafield ymmMetafield = new Metafield
{
Namespace = "fitment",
Key = "seo",
Value = json,
ValueType = "json_string",
OwnerResource = "product",
OwnerId = product.Id
};
// Metafield ymmMetafield = new Metafield
// {
// Namespace = "fitment",
// Key = "seo",
// Value = json,
// ValueType = "json_string",
// OwnerResource = "product",
// OwnerId = product.Id
// };
await _shopifyClient.Metafields.Add(ymmMetafield);
}
// await _shopifyClient.Metafields.Add(ymmMetafield);
// }
Metafield isFitmentMetafield = new Metafield
{
Namespace = "Flags",
Key = "IsFitment",
Value = isFitment.ToString(),
ValueType = "string",
OwnerResource = "product",
OwnerId = product.Id
};
// Metafield isFitmentMetafield = new Metafield
// {
// Namespace = "Flags",
// Key = "IsFitment",
// Value = isFitment.ToString(),
// ValueType = "string",
// OwnerResource = "product",
// OwnerId = product.Id
// };
await _shopifyClient.Metafields.Add(isFitmentMetafield);
// await _shopifyClient.Metafields.Add(isFitmentMetafield);
importData.UpdatedAt = DateTime.Now;
importData.UpdateType = "Fitment";
}
// importData.UpdatedAt = DateTime.Now;
// importData.UpdateType = "Fitment";
// }
catch (Exception ex)
{
Console.WriteLine($"{product.Id}: {ex.Message}");
}
}
// catch (Exception ex)
// {
// Console.WriteLine($"{product.Id}: {ex.Message}");
// }
// }
try
{
i++;
Console.WriteLine(i);
// try
// {
// i++;
// Console.WriteLine(i);
await _partSourceContext.SaveChangesAsync();
// await _partSourceContext.SaveChangesAsync();
products = await _shopifyClient.Products.GetNext();
}
// products = await _shopifyClient.Products.GetNext();
// }
catch (Exception ex)
{
i++;
Console.WriteLine(i);
// catch (Exception ex)
// {
// i++;
// Console.WriteLine(i);
Console.WriteLine($"Retrying request: {ex.Message}");
// Console.WriteLine($"Retrying request: {ex.Message}");
await _partSourceContext.SaveChangesAsync();
// await _partSourceContext.SaveChangesAsync();
products = await _shopifyClient.Products.GetNext();
}
}
// products = await _shopifyClient.Products.GetNext();
// }
// }
return new AutomationJobResult
{
IsSuccess = true
};
}
// return new AutomationJobResult
// {
// IsSuccess = true
// };
// }
private async Task DeleteFitmentMetafields(long shopifyId)
{
IDictionary<string, object> parameters = new Dictionary<string, object>
{
{ "metafield[owner_id]", shopifyId},
{ "metafield[owner_resource]", "product" },
{ "namespace", "fitment" },
};
// private async Task DeleteFitmentMetafields(long shopifyId)
// {
// IDictionary<string, object> parameters = new Dictionary<string, object>
// {
// { "metafield[owner_id]", shopifyId},
// { "metafield[owner_resource]", "product" },
// { "namespace", "fitment" },
// };
IEnumerable<Metafield> metafields = await _shopifyClient.Metafields.Get(parameters);
// IEnumerable<Metafield> metafields = await _shopifyClient.Metafields.Get(parameters);
foreach (Metafield metafield in metafields)
{
await _shopifyClient.Metafields.Delete(metafield);
}
}
// foreach (Metafield metafield in metafields)
// {
// await _shopifyClient.Metafields.Delete(metafield);
// }
// }
public IList<VehicleData> GetVehicles(string partNumber, string lineCode)
{
partNumber = Regex.Replace(partNumber, "[^a-zA-Z0-9]", string.Empty);
// public IList<VehicleData> GetVehicles(string partNumber, string lineCode)
// {
// partNumber = Regex.Replace(partNumber, "[^a-zA-Z0-9]", string.Empty);
//string sql = $"select distinct BaseVehicleId, EngineConfigId from dbo.Fitment where ManufacturerCode in (select WhiCode from DcfMapping where PartSourceCode='{lineCode}') and (partNumber = '{partNumber}' or partNumber = '{partNumber.Replace("-", string.Empty)}')";
string sql = $"with FitmentIds (BaseVehicleId, EngineConfigId) as (select distinct BaseVehicleId, EngineConfigId from dbo.Fitment where LineCode in (select WhiCode from DcfMapping where LineCode='{lineCode}') and PartNumber = '{partNumber}') select v.* from VehicleData v join FitmentIds f on v.BaseVehicleId = f.BaseVehicleId and v.EngineConfigId = f.EngineConfigId;";
// //string sql = $"select distinct BaseVehicleId, EngineConfigId from dbo.Fitment where ManufacturerCode in (select WhiCode from DcfMapping where PartSourceCode='{lineCode}') and (partNumber = '{partNumber}' or partNumber = '{partNumber.Replace("-", string.Empty)}')";
// string sql = $"with FitmentIds (BaseVehicleId, EngineConfigId) as (select distinct BaseVehicleId, EngineConfigId from dbo.Fitment where LineCode in (select WhiCode from DcfMapping where LineCode='{lineCode}') and PartNumber = '{partNumber}') select v.* from VehicleData v join FitmentIds f on v.BaseVehicleId = f.BaseVehicleId and v.EngineConfigId = f.EngineConfigId;";
#pragma warning disable EF1000 // Possible SQL injection vulnerability.
IList<VehicleData> vehicles = _partSourceContext.VehicleData.FromSql(sql).ToList();
#pragma warning restore EF1000 // Possible SQL injection vulnerability.
//#pragma warning disable EF1000 // Possible SQL injection vulnerability.
// IList<VehicleData> vehicles = _partSourceContext.VehicleData.FromSql(sql).ToList();
//#pragma warning restore EF1000 // Possible SQL injection vulnerability.
return vehicles;
}
private void Update()
{
// return vehicles;
// }
// private void Update()
// {
}
// }
}
}
// }
//}

View File

@@ -53,7 +53,7 @@ namespace PartSource.Automation.Jobs
}
IList<Fitment> fitments = GetPositionOrderedFitments(importData?.PartNumber, importData?.LineCode);
IList<VehicleData> vehicles = _vehicleService.GetVehiclesForPart(importData?.PartNumber, importData?.LineCode);
IList<Vehicle> vehicles = _vehicleService.GetVehiclesForPart(importData?.PartNumber, importData?.LineCode);
if (fitments.Count == 0 || vehicles.Count == 0)
{

View File

@@ -102,7 +102,7 @@ namespace PartSource.Automation
.AddSingleton<SsisService>()
.AddSingleton<FtpService>()
.AddSingleton<EmailService>()
.AddSingleton<AddProducts>()
.AddSingleton<AddAndUpdateProducts>()
//.AddSingleton<BuildCategories>()
//.AddSingleton<BackupProducts>()
//.AddSingleton<BuildVehicleCache>()

View File

@@ -2,7 +2,7 @@
"profiles": {
"PartSource.Automation": {
"commandName": "Project",
"commandLineArgs": "UpdatePricing",
"commandLineArgs": "UpdatePositioning",
"environmentVariables": {
"PS_AUTOMATION_ENVIRONMENT": "development"
}

View File

@@ -1,7 +1,7 @@
{
"ConnectionStrings": {
//"PartSourceDatabase": "Server=localhost;Database=ps-whi-stage;Integrated Security=True;"
"PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
"PartSourceDatabase": "Server=localhost;Database=ps-whi-stage;Integrated Security=True;"
//"PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
},
"emailConfiguration": {

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PartSource.Data.Dtos
{
public class EngineDto
{
public int EngineConfigId { get; set; }
public string Description { get; set; }
public string Make { get; set; }
public int MakeId { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PartSource.Data.Dtos
{
public class SubmodelDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Make { get; set; }
public int MakeId { get; set; }
}
}

View File

@@ -5,14 +5,18 @@ using System.Text;
namespace PartSource.Data.Models
{
public class BaseVehicle
{
public int BaseVehicleId { get; set; }
public class BaseVehicle
{
public int BaseVehicleId { get; set; }
public int Year { get; set; }
public string Make { get; set; }
public int MakeId { get; set; }
public int MakeId { get; set; }
public int ModelId { get; set; }
}
public string Model { get; set; }
public int ModelId { get; set; }
public int Year { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Security.Cryptography;
using System.Text;
namespace PartSource.Data.Models
@@ -14,5 +15,13 @@ namespace PartSource.Data.Models
public int BaseVehicleId { get; set; }
public int SubmodelId { get; set; }
public string Make { get; set; }
public int MakeId { get; set; }
public string Model { get; set; }
public int ModelId { get; set; }
}
}

View File

@@ -10,9 +10,9 @@ namespace PartSource.Data.Models
public string LineCode { get; set; }
public int? BaseVehicleId { get; set; }
public int BaseVehicleId { get; set; }
public int? EngineConfigId { get; set; }
public int EngineConfigId { get; set; }
public string Position { get; set; }
}

View File

@@ -7,20 +7,20 @@ using System.Text;
namespace PartSource.Data.Models
{
[Table("Submodel", Schema = "Vehicle")]
public class Submodel
{
public int SubmodelId { get; set; }
public string Name { get; set; }
[JsonIgnore]
public int Year { get; set; }
[JsonIgnore]
public string Make { get; set; }
public int MakeId { get; set; }
[JsonIgnore]
public string Model { get; set; }
public int ModelId { get; set; }
}
}

View File

@@ -6,32 +6,29 @@ using System.Text;
namespace PartSource.Data.Models
{
public class VehicleData
public class Vehicle
{
public int? MakeId { get; set; }
public int MakeId { get; set; }
public string MakeName { get; set; }
public int? ModelId { get; set; }
public int ModelId { get; set; }
public string ModelName { get; set; }
public int? EngineConfigId { get; set; }
public int EngineConfigId { get; set; }
public string EngineDescription { get; set; }
public int? BaseVehicleId { get; set; }
public int BaseVehicleId { get; set; }
public int? Year { get; set; }
public int Year { get; set; }
public int? SubmodelId { get; set; }
public int SubmodelId { get; set; }
public string SubmodelName { get; set; }
[Key]
public int VehicleToEngineConfigId { get; set; }
[NotMapped]
public string Position { get; set; }
}
}

View File

@@ -8,7 +8,7 @@ namespace PartSource.Data.Models
public class VehicleMake
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int MakeId { get; set; }
public string Name { get; set; }
}

View File

@@ -9,11 +9,14 @@ namespace PartSource.Data.Models
public class VehicleModel
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public int ModelId { get; set; }
public int Year { get; set; }
public string Name { get; set; }
public int VehicleMakeId { get; set; }
public string Make { get; set; }
public int MakeId { get; set; }
}
}

View File

@@ -33,12 +33,7 @@ namespace PartSource.Data
public DbSet<Part> Parts { get; set; }
public DbSet<VehicleMake> VehicleMakes { get; set; }
public DbSet<VehicleModel> VehicleModels { get; set; }
public DbSet<VehicleData> VehicleData { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<PartsAvailability> PartAvailabilities { get; set; }
@@ -48,6 +43,10 @@ namespace PartSource.Data
public DbQuery<Submodel> Submodels { get; set; }
public DbQuery<VehicleMake> VehicleMakes { get; set; }
public DbQuery<VehicleModel> VehicleModels { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
@@ -55,6 +54,8 @@ namespace PartSource.Data
modelBuilder.Query<BaseVehicle>().ToView(nameof(BaseVehicle));
modelBuilder.Query<Engine>().ToView(nameof(Engine));
modelBuilder.Query<Submodel>().ToView(nameof(Submodel));
modelBuilder.Query<VehicleMake>().ToView(nameof(VehicleMake));
modelBuilder.Query<VehicleModel>().ToView(nameof(VehicleModel));
modelBuilder.Entity<PartsAvailability>().HasKey(p => new { p.Store, p.SKU });
modelBuilder.Entity<DcfMapping>().HasKey(d => new { d.LineCode, d.WhiCode });
@@ -70,10 +71,9 @@ namespace PartSource.Data
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=PartSource;Trusted_Connection=True;");
optionsBuilder.UseSqlServer("Server=localhost;Database=ps-whi-stage;Trusted_Connection=True;");
}
}
}

View File

@@ -0,0 +1,83 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using static System.Linq.Expressions.Expression;
namespace PartSource.Services.Extensions
{
public static class IQueryableExtensions
{
public static IQueryable<T> ApplyQueryDto<T>(this IQueryable<T> queryable, T dto)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
object value = property.GetValue(dto);
if (IsFilterableValue(value))
{
queryable = queryable.Where(property, value);
}
}
return queryable;
}
private static IQueryable<T> Where<T>(this IQueryable<T> queryable, PropertyInfo property, object value)
{
if (property == null || value == null)
{
return queryable;
}
MemberExpression functions = Property(null, typeof(EF).GetProperty(nameof(EF.Functions)));
MethodInfo likeMethodInfo = typeof(DbFunctionsExtensions).GetMethod(nameof(DbFunctionsExtensions.Like), new Type[] { functions.Type, typeof(string), typeof(string) });
ParameterExpression lambdaParameter = Parameter(typeof(T), "x");
Expression expressionProperty = Property(lambdaParameter, property.Name);
if (property.PropertyType != typeof(string))
{
expressionProperty = Call(expressionProperty, typeof(object).GetMethod(nameof(object.ToString), new Type[0]));
}
MethodCallExpression methodCall = Call(
null,
likeMethodInfo,
functions,
expressionProperty,
Constant(value.ToString()));
return queryable.Where(Lambda<Func<T, bool>>(methodCall, lambdaParameter));
}
private static bool IsFilterableValue(object value)
{
switch (value)
{
case int i:
if (i == default) return false;
break;
case DateTime d:
if (d == default) return false;
break;
case string s:
if (string.IsNullOrEmpty(s)) return false;
break;
default:
if (value == null) return false;
break;
}
return true;
}
}
}

View File

@@ -1,13 +1,15 @@
using Microsoft.EntityFrameworkCore;
using PartSource.Data;
using PartSource.Data.Dtos;
using PartSource.Data.Models;
using PartSource.Services.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace PartSource.Services
{
public class VehicleService
@@ -19,28 +21,102 @@ namespace PartSource.Services
_partSourceContext = partSourceContext;
}
public async Task<IList<VehicleMake>> GetAllMakes()
public async Task<IList<Vehicle>> GetVehicles(Vehicle vehicleQuery)
{
return await _partSourceContext.VehicleMakes.ToListAsync();
return await _partSourceContext.Vehicles
.ApplyQueryDto(vehicleQuery)
.ToListAsync();
}
public async Task<IList<VehicleData>> GetModels(int makeId, int year)
public async Task<Vehicle> GetVehicleById(int vehicleToEngineConfigId)
{
return await _partSourceContext.VehicleData.Where(d =>
d.MakeId == makeId
&& d.Year == year
)
.ToListAsync();
return await _partSourceContext.Vehicles
.FirstOrDefaultAsync(v => v.VehicleToEngineConfigId == vehicleToEngineConfigId);
}
public async Task<IList<BaseVehicle>> GetBaseVehicles(int makeId, int modelId, int year)
public async Task<Vehicle> GetVehicle(int baseVehicleId, int engineConfigId, int submodelId)
{
return await _partSourceContext.BaseVehicles.Where(d =>
return await _partSourceContext.Vehicles.FirstOrDefaultAsync(d =>
d.BaseVehicleId == baseVehicleId
&& d.EngineConfigId == engineConfigId
&& d.SubmodelId == submodelId
);
}
public async Task<IList<VehicleMake>> GetMakes()
{
return await _partSourceContext.VehicleMakes
.OrderBy(m => m.Name)
.ToListAsync();
}
public async Task<IList<VehicleMake>> GetMakes(VehicleMake criteria)
{
return await _partSourceContext.VehicleMakes
.ApplyQueryDto(criteria)
.OrderBy(m => m.Name)
.ToListAsync();
}
public async Task<VehicleMake> GetMakeById(int makeId)
{
return await _partSourceContext.VehicleMakes
.FirstOrDefaultAsync(m => m.MakeId == makeId);
}
public async Task<IList<VehicleModel>> GetModels(VehicleModel criteria)
{
return await _partSourceContext.VehicleModels
.ApplyQueryDto(criteria)
.OrderBy(m => m.Name)
.ToListAsync();
}
public async Task<IList<VehicleModel>> GetModels(int makeId, int year)
{
return await _partSourceContext.VehicleModels
.Where(m =>
m.MakeId == makeId
&& m.Year == year
)
.OrderBy(d => d.Name)
.ToListAsync();
}
public async Task<VehicleModel> GetModelById(int modelId)
{
return await _partSourceContext.VehicleModels
.FirstOrDefaultAsync(m => m.ModelId == modelId);
}
public async Task<IList<BaseVehicle>> GetBaseVehicles(BaseVehicle criteria)
{
return await _partSourceContext.BaseVehicles
.ApplyQueryDto(criteria)
.ToListAsync();
}
public async Task<BaseVehicle> GetBaseVehicle(int makeId, int modelId, int year)
{
return await _partSourceContext.BaseVehicles.FirstOrDefaultAsync(d =>
d.MakeId == makeId
&& d.ModelId == modelId
&& d.Year == year
)
.ToListAsync();
);
}
public async Task<BaseVehicle> GetBaseVehicleById(int baseVehicleId)
{
return await _partSourceContext.BaseVehicles
.FirstOrDefaultAsync(b => b.BaseVehicleId == baseVehicleId);
}
public async Task<IList<Engine>> GetEngines(Engine criteria)
{
return await _partSourceContext.Engines
.ApplyQueryDto(criteria)
.ToListAsync();
}
public async Task<IList<Engine>> GetEngines(int baseVehicleId)
@@ -57,30 +133,57 @@ namespace PartSource.Services
.ToListAsync();
}
public async Task<EngineDto> GetEngineById(int engineConfigId)
{
return await _partSourceContext.Engines
.Where(e => e.EngineConfigId == engineConfigId)
.Select(e => new EngineDto
{
EngineConfigId = e.EngineConfigId,
Description = e.Description,
Make = e.Make,
MakeId = e.MakeId
})
.FirstOrDefaultAsync();
}
public async Task<IList<Submodel>> GetSubmodels(Submodel submodelQuery)
{
return await _partSourceContext.Submodels
.ApplyQueryDto(submodelQuery)
.ToListAsync();
}
public async Task<IList<Submodel>> GetSubmodels(int makeId, int modelId, int year)
{
return await _partSourceContext.Submodels.Where(s =>
s.MakeId == makeId
&& s.ModelId == modelId
&& s.Year == modelId
&& s.Year == year
)
.ToListAsync();
}
public async Task<VehicleData> GetVehicle(int baseVehicleId, int engineConfigId, int submodelId)
public async Task<SubmodelDto> GetSubmodelById(int submodelId)
{
return await _partSourceContext.VehicleData.FirstOrDefaultAsync(d =>
d.BaseVehicleId == baseVehicleId
&& d.EngineConfigId == engineConfigId
&& d.SubmodelId == submodelId
);
return await _partSourceContext.Submodels
.Where(s => s.SubmodelId == submodelId)
.Select(s => new SubmodelDto
{
Id = s.SubmodelId,
Name = s.Name,
Make = s.Make,
MakeId = s.MakeId
})
.FirstOrDefaultAsync();
}
public IList<string> GetYmmFitment(IList<VehicleData> vehicles)
public IList<string> GetYmmFitment(IList<Vehicle> vehicles)
{
if (vehicles.Count == 0)
{
return null;
return new string[0];
}
IList<string> fitmentTags = new List<string>();
@@ -95,7 +198,7 @@ namespace PartSource.Services
List<string> years = vehicles
.Where(v => v.MakeName == make && v.ModelName == model)
.OrderBy(v => v.Year)
.Select(v => v.Year.HasValue ? v.Year.Value.ToString().Trim() : string.Empty)
.Select(v => v.Year.ToString().Trim())
.Distinct()
.ToList();
@@ -109,12 +212,12 @@ namespace PartSource.Services
return fitmentTags;
}
public IList<int> GetVehicleIdFitment(IList<VehicleData> vehicles)
public IList<int> GetVehicleIdFitment(IList<Vehicle> vehicles)
{
return vehicles.Select(v => v.VehicleToEngineConfigId).ToArray();
}
public IList<VehicleData> GetVehiclesForPart(string partNumber, string lineCode)
public IList<Vehicle> GetVehiclesForPart(string partNumber, string lineCode)
{
if (string.IsNullOrEmpty(partNumber) || string.IsNullOrEmpty(lineCode))
{
@@ -127,13 +230,13 @@ namespace PartSource.Services
.Where(d => d.LineCode == lineCode)
.Select(d => d.WhiCode);
IQueryable<VehicleData> vehicles = _partSourceContext.Fitments
IQueryable<Vehicle> vehicles = _partSourceContext.Fitments
.Where(f => f.PartNumber == partNumber && whiCodes.Contains(f.LineCode))
.Join(_partSourceContext.VehicleData,
.Join(_partSourceContext.Vehicles,
f => new { f.BaseVehicleId, f.EngineConfigId },
v => new { v.BaseVehicleId, v.EngineConfigId },
(f, v) => v);
return vehicles.ToList();
}
}