Removed strongly typed inventory objects
Added EAV inventory types Added EAV object handling philosophy Added controllers Added EF Core migration integration
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WyvernInventory.Core.Models;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Data;
|
||||
|
||||
public class DBContext : DbContext
|
||||
{
|
||||
|
||||
public DBContext(DbContextOptions<DBContext> options) : base(options) {}
|
||||
|
||||
public DbSet<InventoryItem> InventoryItems => Set<InventoryItem>();
|
||||
public DbSet<InventoryAttributeDefinition> InventoryAttributeDefinitions => Set<InventoryAttributeDefinition>();
|
||||
public DbSet<InventoryAttributeValue> InventoryAttributeValues => Set<InventoryAttributeValue>();
|
||||
public DbSet<InventoryType> InventoryTypes => Set<InventoryType>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<InventoryItem>()
|
||||
.HasOne(_ => _.Type)
|
||||
.WithMany()
|
||||
.HasForeignKey(_ => _.TypeId);
|
||||
|
||||
modelBuilder.Entity<InventoryAttributeValue>()
|
||||
.HasOne(_ => _.Item)
|
||||
.WithMany(_ => _.AttributeValues)
|
||||
.HasForeignKey(_ => _.ItemId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<InventoryAttributeValue>()
|
||||
.HasOne(_ => _.AttributeDefinition)
|
||||
.WithMany()
|
||||
.HasForeignKey(_ => _.AttributeDefinitionId)
|
||||
.OnDelete(DeleteBehavior.NoAction);
|
||||
|
||||
modelBuilder.Entity<InventoryAttributeDefinition>()
|
||||
.HasOne(_ => _.Type)
|
||||
.WithMany(_ => _.AttributeDefinitions)
|
||||
.HasForeignKey(_ => _.TypeId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<InventoryAttributeValue>()
|
||||
.HasIndex(_ => new { _.ItemId, _.AttributeDefinitionId})
|
||||
.IsUnique();
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using WyvernInventory.Core.Interfaces.Repos;
|
||||
using WyvernInventory.Core.Models;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Repos;
|
||||
|
||||
public class GenericInventoryItemRepo : IDbObjectRepo<GenericInventoryItem>
|
||||
{
|
||||
private List<GenericInventoryItem> _genericItemList = [];
|
||||
|
||||
public async Task<List<GenericInventoryItem>> GetAsync(Predicate<GenericInventoryItem>? filter = null)
|
||||
{
|
||||
List<GenericInventoryItem> result = [];
|
||||
|
||||
if (filter is null) return _genericItemList;
|
||||
|
||||
result.AddRange(_genericItemList.Where(item => filter(item)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<(int created, int updated)> UpsertAsync(List<GenericInventoryItem> items)
|
||||
{
|
||||
int created = 0;
|
||||
int updated = 0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Id is not null && _genericItemList.Any(_ => _.Id == item.Id))
|
||||
{
|
||||
int index = _genericItemList.IndexOf(_genericItemList.Find(_ => _.Id == item.Id));
|
||||
_genericItemList[index] = item;
|
||||
|
||||
updated++;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Id = _genericItemList.Count;
|
||||
_genericItemList.Add(item);
|
||||
|
||||
created++;
|
||||
}
|
||||
}
|
||||
|
||||
return (created, updated);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(List<GenericInventoryItem> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
_genericItemList.Remove(_genericItemList.Find(_ => _.Id == item.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WyvernInventory.Core.Interfaces.Repos;
|
||||
using WyvernInventory.Core.Models;
|
||||
using WyvernInventory.Infrastructure.Data;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Repos;
|
||||
|
||||
public class InventoryItemRepo(DBContext dbContext) : IInventoryItemRepo
|
||||
{
|
||||
private DBContext _dbContext = dbContext;
|
||||
|
||||
public async Task<List<InventoryItemDto>> GetAsync(List<InventoryItemRequest>? filterList = null)
|
||||
{
|
||||
return _dbContext.InventoryItems
|
||||
.Include(_ => _.AttributeValues)
|
||||
.Include(_ => _.Type)
|
||||
.Select(_ => new InventoryItemDto
|
||||
{
|
||||
Id = (int)_.Id!,
|
||||
Name = _.Name,
|
||||
AttributeValues = _.AttributeValues.Select(x => new InventoryAttributeValueDto
|
||||
{
|
||||
Id = x.Id,
|
||||
AttributeDefinitionId = (int)x.AttributeDefinitionId!,
|
||||
AttributeDefinition = new InventoryAttributeDefinitionDto()
|
||||
{
|
||||
Id = x.AttributeDefinitionId,
|
||||
Name = x.AttributeDefinition.Name,
|
||||
DataType = x.AttributeDefinition.DataType
|
||||
},
|
||||
StringValue = x.StringValue,
|
||||
IntValue = x.IntValue,
|
||||
DecimalValue = x.DecimalValue,
|
||||
BoolValue = x.BoolValue,
|
||||
DateTimeValue = x.DateTimeValue
|
||||
}).ToList(),
|
||||
Type = new InventoryTypeDto()
|
||||
{
|
||||
Id = _.TypeId,
|
||||
Name = _.Type.Name,
|
||||
}
|
||||
})
|
||||
.ToList<InventoryItemDto>();
|
||||
}
|
||||
|
||||
public async Task<(int created, int updated)> UpsertAsync(List<InventoryItemRequest> items)
|
||||
{
|
||||
var created = 0;
|
||||
var updated = 0;
|
||||
|
||||
var itemsToCreate = items
|
||||
.Where(_ => _.Id is null or 0)
|
||||
.Select(_ => new InventoryItem
|
||||
{
|
||||
Name = _.Name,
|
||||
AttributeValues = _.AttributeValues?.Select(x => new InventoryAttributeValue
|
||||
{
|
||||
Id = x.Id,
|
||||
AttributeDefinitionId = x.AttributeDefinitionId,
|
||||
BoolValue = x.BoolValue,
|
||||
DateTimeValue = x.DateTimeValue,
|
||||
DecimalValue = x.DecimalValue,
|
||||
IntValue = x.IntValue,
|
||||
StringValue = x.StringValue
|
||||
}).ToList(),
|
||||
TypeId = _.TypeId
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var itemsToUpdate = items
|
||||
.Where(_ => _.Id is not null and not 0)
|
||||
.Select(_ => new InventoryItem
|
||||
{
|
||||
Id = _.Id,
|
||||
Name = _.Name,
|
||||
AttributeValues = _.AttributeValues?.Select(x => new InventoryAttributeValue
|
||||
{
|
||||
Id = x.Id,
|
||||
AttributeDefinitionId = x.AttributeDefinitionId,
|
||||
BoolValue = x.BoolValue,
|
||||
DateTimeValue = x.DateTimeValue,
|
||||
DecimalValue = x.DecimalValue,
|
||||
IntValue = x.IntValue,
|
||||
StringValue = x.StringValue
|
||||
}).ToList(),
|
||||
TypeId = _.TypeId
|
||||
})
|
||||
.ToList();
|
||||
|
||||
if (itemsToCreate.Count > 0)
|
||||
{
|
||||
_dbContext.InventoryItems.AddRange(itemsToCreate);
|
||||
created = itemsToCreate.Count;
|
||||
}
|
||||
|
||||
if (itemsToUpdate.Count > 0)
|
||||
{
|
||||
List<int> idList = itemsToUpdate.Select(_ =>
|
||||
{
|
||||
if (_.Id != null) return (int)_.Id;
|
||||
throw new NullReferenceException();
|
||||
}).ToList<int>();
|
||||
|
||||
var existingItems = _dbContext.InventoryItems.ToList()
|
||||
.Where(_ => idList.Contains((int)_.Id!))
|
||||
.ToDictionary(_ => (int)_.Id!);
|
||||
|
||||
foreach (var incoming in itemsToUpdate)
|
||||
{
|
||||
if (!existingItems.TryGetValue(incoming.Id!.Value, out var existing))
|
||||
continue;
|
||||
|
||||
existing.Name = incoming.Name;
|
||||
existing.AttributeValues = incoming.AttributeValues;
|
||||
|
||||
updated++;
|
||||
}
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
return (created, updated);
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAsync(List<InventoryItemRequest> items)
|
||||
{
|
||||
var itemsToDelete = _dbContext.InventoryItems.Where(_ => items.Select(_ => _.Id).Contains(_.Id)).ToList();
|
||||
|
||||
_dbContext.InventoryItems.RemoveRange(itemsToDelete);
|
||||
|
||||
return await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WyvernInventory.Core.Interfaces.Repos;
|
||||
using WyvernInventory.Core.Models;
|
||||
using WyvernInventory.Infrastructure.Data;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Repos;
|
||||
|
||||
public class InventoryTypeRepo(DBContext dbContext) : IDbObjectRepo<InventoryType, InventoryTypeDto>
|
||||
{
|
||||
private readonly DBContext _dbContext = dbContext;
|
||||
|
||||
public async Task<List<InventoryTypeDto>> GetAsync(List<InventoryType>? filterList = null)
|
||||
{
|
||||
return _dbContext.InventoryTypes
|
||||
.Include(_ => _.AttributeDefinitions)
|
||||
.Select(_ => new InventoryTypeDto
|
||||
{
|
||||
Id = _.Id,
|
||||
Name = _.Name,
|
||||
AttributeDefinitions = _.AttributeDefinitions.Select(x => new InventoryAttributeDefinitionDto
|
||||
{
|
||||
Id = x.Id,
|
||||
Name = x.Name,
|
||||
DataType = x.DataType
|
||||
}).ToList()
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<(int created, int updated)> UpsertAsync(List<InventoryType> items)
|
||||
{
|
||||
var created = 0;
|
||||
var updated = 0;
|
||||
|
||||
var itemsToCreate = items
|
||||
.Where(_ => _.Id is null or 0)
|
||||
.ToList();
|
||||
|
||||
var itemsToUpdate = items
|
||||
.Where(_ => _.Id is not null and not 0)
|
||||
.ToList();
|
||||
|
||||
if (itemsToCreate.Count > 0)
|
||||
{
|
||||
_dbContext.InventoryTypes.AddRange(itemsToCreate);
|
||||
created = itemsToCreate.Count;
|
||||
}
|
||||
|
||||
if (itemsToUpdate.Count > 0)
|
||||
{
|
||||
List<int> idList = itemsToUpdate.Select(_ =>
|
||||
{
|
||||
if (_.Id != null) return (int)_.Id;
|
||||
throw new NullReferenceException();
|
||||
}).ToList<int>();
|
||||
|
||||
var existingItems = _dbContext.InventoryTypes.ToList()
|
||||
.Where(_ => idList.Contains((int)_.Id!))
|
||||
.ToDictionary(_ => (int)_.Id!);
|
||||
|
||||
foreach (var incoming in itemsToUpdate)
|
||||
{
|
||||
if (!existingItems.TryGetValue(incoming.Id!.Value, out var existing))
|
||||
continue;
|
||||
|
||||
existing.Name = incoming.Name;
|
||||
existing.AttributeDefinitions = incoming.AttributeDefinitions;
|
||||
|
||||
updated++;
|
||||
}
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
return (created, updated);
|
||||
}
|
||||
|
||||
public async Task<int> DeleteAsync(List<InventoryType> items)
|
||||
{
|
||||
var itemsToDelete = _dbContext.InventoryTypes.Where(_ => items.Select(_ => _.Id).Contains(_.Id)).ToList();
|
||||
|
||||
_dbContext.InventoryTypes.RemoveRange(itemsToDelete);
|
||||
|
||||
return await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using WyvernInventory.Core.Interfaces.Repos;
|
||||
using WyvernInventory.Core.Interfaces.Services;
|
||||
using WyvernInventory.Core.Models;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Services;
|
||||
|
||||
public class GenericInventoryItemService(IDbObjectRepo<GenericInventoryItem> dbRepo)
|
||||
: IDataObjectService<GenericInventoryItem>
|
||||
{
|
||||
private readonly IDbObjectRepo<GenericInventoryItem> _dbRepo = dbRepo;
|
||||
|
||||
public async Task<List<GenericInventoryItem>> GetAsync(Predicate<GenericInventoryItem>? filter = null) => await _dbRepo.GetAsync(filter);
|
||||
|
||||
public async Task<(int created, int updated)> UpsertAsync(List<GenericInventoryItem> items) => await _dbRepo.UpsertAsync(items);
|
||||
|
||||
public async Task DeleteAsync(List<GenericInventoryItem> items) => await _dbRepo.DeleteAsync(items);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using WyvernInventory.Core.Interfaces.Repos;
|
||||
using WyvernInventory.Core.Interfaces.Services;
|
||||
using WyvernInventory.Core.Models;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Services;
|
||||
|
||||
public class InventoryItemService(IInventoryItemRepo repo) : IInventoryItemService
|
||||
{
|
||||
private IInventoryItemRepo _repo = repo;
|
||||
|
||||
public async Task<List<InventoryItemDto>> GetAsync(List<InventoryItemRequest>? filter = null) => await _repo.GetAsync(filter);
|
||||
|
||||
public async Task<(int created, int updated)> UpsertAsync(List<InventoryItemRequest> items) => await _repo.UpsertAsync(items);
|
||||
|
||||
public async Task<int> DeleteAsync(List<InventoryItemRequest> items) => await _repo.DeleteAsync(items);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using WyvernInventory.Core.Interfaces.Repos;
|
||||
using WyvernInventory.Core.Interfaces.Services;
|
||||
using WyvernInventory.Core.Models;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Services;
|
||||
|
||||
public class InventoryTypeService(IDbObjectRepo<InventoryType, InventoryTypeDto> repo) : IDataObjectService<InventoryType, InventoryTypeDto>
|
||||
{
|
||||
private IDbObjectRepo<InventoryType, InventoryTypeDto> _repo = repo;
|
||||
|
||||
public async Task<List<InventoryTypeDto>> GetAsync(List<InventoryType>? filter = null) => await _repo.GetAsync(filter);
|
||||
|
||||
public async Task<(int created, int updated)> UpsertAsync(List<InventoryType> items) => await _repo.UpsertAsync(items);
|
||||
|
||||
public async Task<int> DeleteAsync(List<InventoryType> items) => await _repo.DeleteAsync(items);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog.Core;
|
||||
using WyvernInventory.Infrastructure.Data;
|
||||
|
||||
namespace WyvernInventory.Infrastructure.Utils;
|
||||
|
||||
public static class DbUtils
|
||||
{
|
||||
public static bool CanConnectToDatabase(DBContext db, ILogger<Logger> logger)
|
||||
{
|
||||
if (db.Database.CanConnect())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.LogCritical("Unable to connect to DB");
|
||||
|
||||
Environment.Exit(1);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,12 @@
|
||||
<ProjectReference Include="..\WyvernInventory.Core\WyvernInventory.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user