entity-framework-core - 如何在 Entity Framework Core 中控制内存中生成的键值?
问题描述
背景
我正在开发一个基于 ASP.NET Core 2.1 和 EntityFrameworkCore 2.1.1 的 Web 应用程序。除了其他类型的测试,我正在使用InMemory Web API 测试对 Web.Api 进行某种集成测试。
由于测试数据相当复杂,我已经基于迁移的数据库的一部分生成了一个 JSON 文件,并且当内存测试开始时,测试正在从这个 JSON 文件中导入数据。
问题
只读测试工作得很好。但是,当我为具有标识 ( ValueGenerated == ValueGenerated.OnAdd
) 的实体插入内容时,它会自动获得值 1(所有 PK 都是int
s)。这是有道理的,因为任何生成器 EF 在幕后使用来生成这些值都没有被指示从某个值生成。
但是,对于生成现有键值的插入,这显然不能正常工作。
我尝试过的事情
[当前工作解决方案]移动键值- 在反序列化所有对象后,我对所有相关实体执行 ids 移动操作(它们获得“大”值)。这可以正常工作,但容易出错(例如,某些实体具有静态 ID,我必须确保正确定义所有外键/导航属性,而且现在有点慢,因为我依靠反射来正确识别键 /需要移动的导航属性)
测试启动.cs
services.AddSingleton<IValueGeneratorCache, ValueGeneratorCache>();
services.AddSingleton<ValueGeneratorCacheDependencies, ValueGeneratorCacheDependencies>();
ID 生成功能
public const int StartValue = 100000000;
class ResettableValueGenerator : ValueGenerator<int>
{
private int _current;
public override bool GeneratesTemporaryValues => false;
public override int Next(EntityEntry entry)
{
return Interlocked.Increment(ref _current);
}
public void Reset() => _current = StartValue;
}
public static void ResetValueGenerators(CustomApiContext context, IValueGeneratorCache cache, int startValue = StartValue)
{
var allKeyProps = context.Model.GetEntityTypes()
.Select(e => e.FindPrimaryKey().Properties[0])
.Where(p => p.ClrType == typeof(int));
var keyProps = allKeyProps.Where(p => p.ValueGenerated == ValueGenerated.OnAdd);
foreach (var keyProperty in keyProps)
{
var generator = (ResettableValueGenerator)cache.GetOrAdd(
keyProperty,
keyProperty.DeclaringEntityType,
(p, e) => new ResettableValueGenerator());
generator.Reset();
}
}
调试时,我可以看到我的实体正在迭代,因此应用了重置。
将数据推送到内存数据库中
private void InitializeSeeding(IServiceScope scope)
{
using (scope)
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<CustomApiContext>();
// this pushes deserialized data + static data into the database
InitDbContext(context);
var valueGeneratorService = services.GetRequiredService<IValueGeneratorCache>();
ResetValueGenerators(context, valueGeneratorService);
}
catch (Exception ex)
{
var logger = services.GetService<ILogger<Startup>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
}
实际插入
这是使用通用存储库完成的,但归结为:
Context.Set<T>().Add(entity);
Context.SaveChanges();
问题:如何在 Entity Framework Core 中控制内存中生成的键值?
解决方案
推荐阅读
- swiftui - SwiftUI - 我的混合图像视图在 iPhone 上失败。预览中的作品
- halide - 卤化物数学是否以“浮点”(32 位)执行?
- c# - 如何为自包含控制台应用程序 Visual Studio C# 创建 .msi 安装程序
- spotfire - 如何根据日期指定的行数在 Spotfire 中显示可视化?
- reactjs - 如何在 Cypress 中访问组件道具
- javascript - 根据一个属性的唯一性过滤数组对象
- pygame - JetBrains PyCharm 找不到任何外部模块
- r - 如何添加控制线 - 不同地块不同?
- postgresql - 在postgres中将日期差异转换为真实
- r - 计算向量 x 中等于向量 y 中任何向量元素的元素数