EF Core 从具有多重性零或一的模型中获取实体的导航属性
EF Core get Navigation Properties of an entity from Model with multiplicity Zero or One
我正在创建一个通用的 class 来为数据库植入集成测试所需的实体。
我可以创建单独的实体,但是当一个实体依赖于另一个实体时,我必须手动指示我的 class 先创建父实体,然后再继续。
我正在尝试自动进行此检测,从模型定义中获取多重性为 0 或 1 的导航属性列表(参考导航属性),一旦完成,我的 class 将递归地调用自身来创建父实体首先(循环依赖超出了这里的范围)。
我以前在 EF for .net Framework 中这样做,但 EF Core 已经发生了很大变化。
我在 EF Core 中缺少的是 RelationshipMultiplicity,我在官方文档中找不到任何对 Multiplicity 的引用,甚至艰难的 hacky 解决方案是检查导航 属性是一个集合,我想有更多的控制权,让事情变得简单。
到目前为止,我正在使用以下方法探索模型定义:
var modelData = _context.Model.GetEntityTypes()
.Select(t => new
{
t.ClrType.Name,
DerivedNavigationProperties = t.FindDerivedNavigations(t.ClrType.Name),
DefiningNavigationProperties = t.FindDefiningNavigation(),
DeclaredForeignKeys = t.GetDeclaredForeignKeys(),
DeclaredNavigations = t.GetDeclaredNavigations(),
DerivedNavigations = t.GetDerivedNavigations(),
DerivedNavigationsInclusive = t.GetDerivedNavigationsInclusive(),
Navigations = t.GetNavigations() // This returns all Navigation Properties (INavigation)
});
在检查 GitHub 中的源代码后,我可以自信地说,EF Core 中没有多重性。
我创建了一个类似于 .net Framework 3.5+ 的枚举(参见:Official documentation):
public enum RelationshipMultiplicity
{
Many = 2,
One = 1,
ZeroOrOne = 0
}
然后是允许使用枚举作为过滤器获取所有导航属性的扩展方法。
我使用的关键是:
- 名为 INavigation 的扩展方法 IsCollection
- ForeignKey.IsRequired 属性
该方法允许按关系类型获取所有导航属性
public static class ModelExtensions
{
/// <summary>
/// Extension method used to get from the entity all navigation properties by multiplicity
/// </summary>
/// <typeparam name="T">Entity from where the navigation properties are taken</typeparam>
/// <param name="model">Context Model</param>
/// <param name="multiplicity">Type of multiplicity to use</param>
/// <returns>List of PropertyInfo of Navigation Properties</returns>
public static IEnumerable<PropertyInfo> GetNavigationProperties<T>(this IModel model, RelationshipMultiplicity multiplicity)
{
var navigations = model.GetEntityTypes().FirstOrDefault(m => m.ClrType == typeof(T))?.GetNavigations();
var properties = new List<PropertyInfo>();
switch (multiplicity)
{
case RelationshipMultiplicity.Many | RelationshipMultiplicity.ZeroOrOne:
return navigations?
.Select(nav => nav.PropertyInfo);
case RelationshipMultiplicity.Many:
return navigations?
.Where(nav => nav.IsCollection())
.Select(nav => nav.PropertyInfo);
case RelationshipMultiplicity.One:
return navigations?
.Where(nav => !nav.IsCollection() && nav.ForeignKey.IsRequired)
.Select(nav => nav.PropertyInfo);
case RelationshipMultiplicity.ZeroOrOne:
return navigations?
.Where(nav => !nav.IsCollection())
.Select(nav => nav.PropertyInfo);
default:
return null;
}
return properties;
}
}
用法示例:
var oneToManyRelations = _context.Model.GetNavigationProperties<Transaction>(
RelationshipMultiplicity.ZeroOrOne);
var manyToOneRelations = _context.Model.GetNavigationProperties<Transaction>(
RelationshipMultiplicity.Many);
var allRelations = _context.Model.GetNavigationProperties<Transaction>(
RelationshipMultiplicity.Many |
RelationshipMultiplicity.ZeroOrOne);
我正在创建一个通用的 class 来为数据库植入集成测试所需的实体。 我可以创建单独的实体,但是当一个实体依赖于另一个实体时,我必须手动指示我的 class 先创建父实体,然后再继续。 我正在尝试自动进行此检测,从模型定义中获取多重性为 0 或 1 的导航属性列表(参考导航属性),一旦完成,我的 class 将递归地调用自身来创建父实体首先(循环依赖超出了这里的范围)。
我以前在 EF for .net Framework 中这样做,但 EF Core 已经发生了很大变化。 我在 EF Core 中缺少的是 RelationshipMultiplicity,我在官方文档中找不到任何对 Multiplicity 的引用,甚至艰难的 hacky 解决方案是检查导航 属性是一个集合,我想有更多的控制权,让事情变得简单。
到目前为止,我正在使用以下方法探索模型定义:
var modelData = _context.Model.GetEntityTypes()
.Select(t => new
{
t.ClrType.Name,
DerivedNavigationProperties = t.FindDerivedNavigations(t.ClrType.Name),
DefiningNavigationProperties = t.FindDefiningNavigation(),
DeclaredForeignKeys = t.GetDeclaredForeignKeys(),
DeclaredNavigations = t.GetDeclaredNavigations(),
DerivedNavigations = t.GetDerivedNavigations(),
DerivedNavigationsInclusive = t.GetDerivedNavigationsInclusive(),
Navigations = t.GetNavigations() // This returns all Navigation Properties (INavigation)
});
在检查 GitHub 中的源代码后,我可以自信地说,EF Core 中没有多重性。
我创建了一个类似于 .net Framework 3.5+ 的枚举(参见:Official documentation):
public enum RelationshipMultiplicity
{
Many = 2,
One = 1,
ZeroOrOne = 0
}
然后是允许使用枚举作为过滤器获取所有导航属性的扩展方法。 我使用的关键是:
- 名为 INavigation 的扩展方法 IsCollection
- ForeignKey.IsRequired 属性
该方法允许按关系类型获取所有导航属性
public static class ModelExtensions
{
/// <summary>
/// Extension method used to get from the entity all navigation properties by multiplicity
/// </summary>
/// <typeparam name="T">Entity from where the navigation properties are taken</typeparam>
/// <param name="model">Context Model</param>
/// <param name="multiplicity">Type of multiplicity to use</param>
/// <returns>List of PropertyInfo of Navigation Properties</returns>
public static IEnumerable<PropertyInfo> GetNavigationProperties<T>(this IModel model, RelationshipMultiplicity multiplicity)
{
var navigations = model.GetEntityTypes().FirstOrDefault(m => m.ClrType == typeof(T))?.GetNavigations();
var properties = new List<PropertyInfo>();
switch (multiplicity)
{
case RelationshipMultiplicity.Many | RelationshipMultiplicity.ZeroOrOne:
return navigations?
.Select(nav => nav.PropertyInfo);
case RelationshipMultiplicity.Many:
return navigations?
.Where(nav => nav.IsCollection())
.Select(nav => nav.PropertyInfo);
case RelationshipMultiplicity.One:
return navigations?
.Where(nav => !nav.IsCollection() && nav.ForeignKey.IsRequired)
.Select(nav => nav.PropertyInfo);
case RelationshipMultiplicity.ZeroOrOne:
return navigations?
.Where(nav => !nav.IsCollection())
.Select(nav => nav.PropertyInfo);
default:
return null;
}
return properties;
}
}
用法示例:
var oneToManyRelations = _context.Model.GetNavigationProperties<Transaction>(
RelationshipMultiplicity.ZeroOrOne);
var manyToOneRelations = _context.Model.GetNavigationProperties<Transaction>(
RelationshipMultiplicity.Many);
var allRelations = _context.Model.GetNavigationProperties<Transaction>(
RelationshipMultiplicity.Many |
RelationshipMultiplicity.ZeroOrOne);