c# - 级联删除在运行时使用 EF Core 配置的模型
问题描述
为了实现一个基本的修订历史系统,我定义了一些Action
实体,这些实体配置了反射并包含在 EF Core 的模型构建器中。每个动作都被视为一个单独的实体,并包含对应实体的外键。
例如:
public interface IAction
{
public long Id { get; }
public long DateTime Date { get; }
public long CreatorId { get; }
public User Creator { get; }
}
public interface IPostAction
{
long PostId { get; }
Post Post { get; }
}
public interface IUserAction
{
long UserId { get; }
User User { get; }
}
public abstract class ActionBase : IAction
{
public long Id { get; set; }
public DateTime Date { get; set; }
public long CreatorId { get; set; }
[ForeignKey( nameof( CreatorId ) )]
public User Creator { get; set; }
}
public abstract class PostAction : ActionBase, IPostAction
{
public long PostId { get; set; }
[ForeignKey( nameof( PostId ) )]
public Post Post { get; set; }
}
public class AddPostFollowerAction : PostAction, IUserAction
{
public long UserId { get; set; }
[ForeignKey( nameof( UserId ) )]
public User User { get; set; }
}
然后通过创建DynamicAssembly
为每种类型定义 IEntityTypeConfiguration 的方法来配置这些模型。
/// <inheritdoc />
public abstract class ActionConfiguration<TAction> : IEntityTypeConfiguration<TAction>
where TAction : class, IAction
{
/// <inheritdoc />
/// <summary>
/// Configures the <see cref="IAction" />.
/// </summary>
/// <param name="builder">The configuration builder.</param>
public void Configure( EntityTypeBuilder<TAction> builder )
{
builder.HasKey( a => a.Id );
builder.ToTable( $"Actions_{typeof(TAction).Name}" );
builder.HasOne( a => a.Creator )
.WithMany()
.HasForeignKey( a => a.CreatorId );
}
/// <summary>
/// Configures the <see cref="IEntity" /> implementation.
/// </summary>
/// <param name="builder">
/// The configuration builder.
/// </param>
public virtual void Configuring( EntityTypeBuilder<TAction> builder )
{
}
}
/// <summary>
/// A static cache that auto-generates entity configurations for
/// concrete <see cref="IAction"/> classes
/// </summary>
public static class ActionConfigurationCache
{
#region Data Members
/// <summary>
/// The dynamic assembly that stores the auto-generated
/// <see cref="ActionConfiguration{TAction}"/>s.
/// </summary>
public static readonly Assembly Assembly;
#endregion
#region Constructor
/// <summary>
/// Initializes the cache.
/// </summary>
static ActionConfigurationCache()
{
Assembly = CreateConfigurationAssembly();
}
#endregion
#region Private Methods
/// <summary>
/// Creates a dynamic assembly that stores auto-generated
/// <see cref="ActionConfiguration{TAction}"/>s.
/// </summary>
/// <returns>
/// The dynamic assembly.
/// </returns>
private static Assembly CreateConfigurationAssembly()
{
// Gather concrete Action types
var actionTypes = typeof( IAction ).GetConcreteTypes();
// Create Dynamic Assembly
var assemblyName = new AssemblyName( "ActionConfigurationAssembly" );
var assembly = AssemblyBuilder.DefineDynamicAssembly( assemblyName, AssemblyBuilderAccess.Run );
// Create Dynamic Module
var module = assembly.DefineDynamicModule( "ActionConfigurationCache" );
// Create ActionConfiguration types
foreach( var actionType in actionTypes )
CreateConfigurationClass( module, actionType );
// Build Assembly
return assembly;
}
/// <summary>
/// Creates an entity configuration for the specified <see cref="IAction"/> type
/// </summary>
/// <param name="module">
/// The <see cref="Module"/> to define the entity configuration in.
/// </param>
/// <param name="type">
/// A concrete <see cref="IAction"/> implementation to generate an
/// entity configuration for.
/// </param>
/// <returns>
/// The <see cref="TypeInfo"/> of the generated entity configuration.
/// </returns>
private static TypeInfo CreateConfigurationClass( ModuleBuilder module, Type type )
{
// Define concrete ActionConfiguration type
var configGeneric = typeof( ActionConfiguration<> ).MakeGenericType( type );
var configType = module.DefineType(
$"{type.Name}Configuration",
TypeAttributes.Class,
configGeneric );
// Create the type.
configType.CreateTypeInfo();
return configType;
}
#endregion
}
在删除实体之前,这种方法可以正常工作。如果帖子被删除,由于导航实体在类中的设置方式,它会引发外键错误Action
。由于他们使用ForeignKeyAttribute
,AddPostFollowerAction
被指定为从属实体,并被Post
指定为主体实体。因此,当尝试级联删除时,由于角色本质上是切换的,因此级联删除不会删除任何Action
实体并导致违反约束。
由于Action
将要定义的实体的数量(以及由于特性蠕变而在未来添加的更多实体)以及 EntityFramework 不以这种方式支持多态性的事实,因此Action
在其对应的实体中为每个个体定义导航属性并不是真的可行的。
有没有办法可以配置级联删除以自动删除这些操作?
解决方案
推荐阅读
- flutter - 在 showModalBottomSheet 外部单击时,如何控制传递给 Navigator.pop() 的内容?
- matlab - 在循环matlab外的while循环上操作
- android - Android 版 Google Chrome 上的字体大小显示问题
- laravel - PosrgteSQL:根据步骤(日、月、年)的时间段列值的总和
- wordpress - 移除边框
- asp.net-core - “Microsoft.AspNetCore.SpaServices”中都存在“PrerenderTagHelper”类型
- angular - 每次调用 child 时,Angular 子路由都会重新加载父组件
- c# - Windows Form C# - 如何使用以编程方式创建的文本框?
- html - 使用伪元素(及其高度)的视差
- asp.net - 在后面的代码上禁用用户控件 telerik:RadButton inside telerik:RadListView ItemTemplate