- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试限制几个 generic
只允许使用的方法 Entities
那inherit
来自 IParentOf<TChildEntity>
interface
,以及访问 Entity's
Foreign Key
(父 ID)Generically
.
演示;
public void AdoptAll<TParentEntity, TChildEntity>(TParentEntity parent,
TParentEntity adoptee)
where TParentEntity : DataEntity, IParentOf<TChildEntity>
where TChildEntity : DataEntity, IChildOf<TParentEntity>
{
foreach (TChildEntity child in (IParentOf<TChildEntity>)parent.Children)
{
(IChildOf<TParentEntity)child.ParentId = adoptee.Id;
}
}
子实体类模型看起来像这样,
public class Account : DataEntity, IChildOf<AccountType>, IChildOf<AccountData>
{
public string Name { get; set; }
public string Balance { get; set; }
// Foreign Key and Navigation Property for AccountType
int IChildOf<AccountType>.ParentId{ get; set; }
public virtual AccountType AccountType { get; set; }
// Foreign Key and Navigation Property for AccountData
int IChildOf<AccountData>.ParentId{ get; set; }
public virtual AccountData AccountData { get; set; }
}
首先,这可能吗?或者它会在 EF 中崩溃吗?
其次,由于外键不遵循约定(而且有多个),我该如何通过 Fluent Api 设置它们?我可以在数据注释中看到如何执行此操作。
我希望这是清楚的,我已经考虑了一段时间并试图解决它,所以我可以遵循我的论点,但它可能没有被清楚地传达,所以如果需要请寻求澄清。我想要这样做的原因是为了使代码安全以及自动化大量手动更改添加新关联和实体所需的类。
谢谢。
编辑
我决定创建一些基础类来实现这个想法并进行测试,我的代码如下。
public abstract class ChildEntity : DataEntity
{
public T GetParent<T>() where T : ParentEntity
{
foreach (var item in GetType().GetProperties())
{
if (item.GetValue(this) is T entity)
return entity;
}
return null;
}
}
public abstract class ParentEntity : DataEntity
{
public ICollection<T> GetChildren<T>() where T : ChildEntity
{
foreach (var item in GetType().GetProperties())
{
if (item.GetValue(this) is ICollection<T> collection)
return collection;
}
return null;
}
}
public interface IParent<TEntity> where TEntity : ChildEntity
{
ICollection<T> GetChildren<T>() where T : ChildEntity;
}
public interface IChild<TEntity> where TEntity : ParentEntity
{
int ForeignKey { get; set; }
T GetParent<T>() where T : ParentEntity;
}
public class ParentOne : ParentEntity, IParent<ChildOne>
{
public string Name { get; set; }
public decimal Amount { get; set; }
public virtual ICollection<ChildOne> ChildOnes { get; set; }
}
public class ParentTwo : ParentEntity, IParent<ChildOne>
{
public string Name { get; set; }
public decimal Value { get; set; }
public virtual ICollection<ChildOne> ChildOnes { get; set; }
}
public class ChildOne : ChildEntity, IChild<ParentOne>, IChild<ParentTwo>
{
public string Name { get; set; }
public decimal Balance { get; set; }
int IChild<ParentOne>.ForeignKey { get; set; }
public virtual ParentOne ParentOne { get; set; }
int IChild<ParentTwo>.ForeignKey { get; set; }
public virtual ParentTwo ParentTwo { get; set; }
}
Data Entity
简单地给出每个 entity
一个Id
property
.
我设置了标准的通用存储库,其中包含用于中介的工作单元类。 AdoptAll 方法在我的程序中看起来像这样。
public void AdoptAll<TParentEntity, TChildEntity>(TParentEntity parent,
TParentEntity adoptee, UoW uoW)
where TParentEntity : DataEntity, IParent<TChildEntity>
where TChildEntity : DataEntity, IChild<TParentEntity>
{
var currentParent = uoW.GetRepository<TParentEntity>().Get(parent.Id);
foreach (TChildEntity child in currentParent.GetChildren<TChildEntity>())
{
child.ForeignKey = adoptee.Id;
}
}
这似乎工作正常并且没有错误(最小测试)这样做有什么重大缺陷吗?
谢谢。
编辑二
这是DbContext中的OnModelCreating方法,为每个实体设置外键。这有问题吗?
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ChildOne>()
.HasOne(p => p.ParentOne)
.WithMany(c => c.ChildOnes)
.HasForeignKey(fk => ((IChild<ParentOne>)fk).ForeignKey);
modelBuilder.Entity<ChildOne>()
.HasOne(p => p.ParentTwo)
.WithMany(c => c.ChildOnes)
.HasForeignKey(fk => ((IChild<ParentTwo>)fk).ForeignKey);
}
最佳答案
根据更新的示例,您希望从实体类公共(public)接口(interface)中隐藏显式 FK,但仍然让它对 EF Core 可见并映射到数据库中的 FK 列。
第一个问题是显式实现的接口(interface)成员不能直接被 EF 发现。此外,它没有好的名称,因此默认约定不适用。
例如,没有流畅配置的 EF Core 将正确地在 Parent
之间创建一对多关联。和 Child
实体,但因为它不会发现 int IChild<Parent>.ForeignKey { get; set; }
属性,它将通过 ParentOneId
维护 FK 属性值/ParentTwoId
shadow properties而不是通过接口(interface)显式属性。换句话说,这些属性不会由 EF Core 填充,也不会被更改跟踪器考虑。
要让 EF Core 使用它们,您需要分别使用 HasForeignKey
映射 FK 属性和数据库列名。和 HasColumnName
流畅的 API 方法重载接受 string
属性名称。请注意,字符串属性名称必须完全符合命名空间。同时 Type.FullName
为非泛型类型提供该字符串,没有像 IChild<ParentOne>
这样的泛型类型的属性/方法(结果必须是 "Namespace.IChild<Namespace.ParentOne>"
),所以让我们首先为此创建一些助手:
static string ChildForeignKeyPropertyName<TParent>() where TParent : ParentEntity
=> $"{typeof(IChild<>).Namespace}.IChild<{typeof(TParent).FullName}>.{nameof(IChild<TParent>.ForeignKey)}";
static string ChildForeignKeyColumnName<TParent>() where TParent : ParentEntity
=> $"{typeof(TParent).Name}Id";
下一步是创建一个辅助方法来执行必要的配置:
static void ConfigureRelationship<TChild, TParent>(ModelBuilder modelBuilder)
where TChild : ChildEntity, IChild<TParent>
where TParent : ParentEntity, IParent<TChild>
{
var childEntity = modelBuilder.Entity<TChild>();
var foreignKeyPropertyName = ChildForeignKeyPropertyName<TParent>();
var foreignKeyColumnName = ChildForeignKeyColumnName<TParent>();
var foreignKey = childEntity.Metadata.GetForeignKeys()
.Single(fk => fk.PrincipalEntityType.ClrType == typeof(TParent));
// Configure FK column name
childEntity
.Property<int>(foreignKeyPropertyName)
.HasColumnName(foreignKeyColumnName);
// Configure FK property
childEntity
.HasOne<TParent>(foreignKey.DependentToPrincipal.Name)
.WithMany(foreignKey.PrincipalToDependent.Name)
.HasForeignKey(foreignKeyPropertyName);
}
如您所见,我正在使用 EF Core 提供的元数据服务来查找相应导航属性的名称。
但这种泛型方法其实也显示了这种设计的局限性。通用约束允许我们使用
childEntity.Property(c => c.ForeignKey)
编译很好,但在运行时不起作用。它不仅适用于流畅的 API 方法,而且基本上适用于任何涉及表达式树的通用方法(如 LINQ to Entities 查询)。当使用公共(public)属性隐式实现接口(interface)属性时,不存在此问题。
我们稍后会回到这个限制。要完成映射,请将以下内容添加到您的 OnModelCreating
覆盖:
ConfigureRelationship<ChildOne, ParentOne>(modelBuilder);
ConfigureRelationship<ChildOne, ParentTwo>(modelBuilder);
现在 EF Core 将正确加载/考虑您明确实现的 FK 属性。
现在回到限制。使用像您的 AdoptAll
这样的通用对象服务没有问题。方法或 LINQ to Objects。但是您不能在用于访问 EF Core 元数据的表达式中或在 LINQ to Entities 查询中访问这些属性。在后一种情况下,您应该通过导航属性访问它,或者在这两种情况下,您都应该通过从 ChildForeignKeyPropertyName<TParent>()
返回的名称访问它。方法。实际上查询会起作用,但会被评估 locally从而导致性能问题或意外行为。
例如
static IEnumerable<TChild> GetChildrenOf<TChild, TParent>(DbContext db, int parentId)
where TChild : ChildEntity, IChild<TParent>
where TParent : ParentEntity, IParent<TChild>
{
// Works, but causes client side filter evalution
return db.Set<TChild>().Where(c => c.ForeignKey == parentId);
// This correctly translates to SQL, hence server side evaluation
return db.Set<TChild>().Where(c => EF.Property<int>(c, ChildForeignKeyPropertyName<TParent>()) == parentId);
}
简而言之,这是可能的,但要小心使用,并确保它在它允许的有限通用服务场景中是值得的。替代方法不使用接口(interface),而是使用 EF Core 元数据、反射或 Func<...>
(的组合)/Expression<Func<..>>
类似于 Queryable
的通用方法参数扩展方法。
编辑:关于第二个问题编辑,流畅的配置
modelBuilder.Entity<ChildOne>()
.HasOne(p => p.ParentOne)
.WithMany(c => c.ChildOnes)
.HasForeignKey(fk => ((IChild<ParentOne>)fk).ForeignKey);
modelBuilder.Entity<ChildOne>()
.HasOne(p => p.ParentTwo)
.WithMany(c => c.ChildOnes)
.HasForeignKey(fk => ((IChild<ParentTwo>)fk).ForeignKey);
为 ChildOne
生成以下迁移
migrationBuilder.CreateTable(
name: "ChildOne",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ForeignKey = table.Column<int>(nullable: false),
Name = table.Column<string>(nullable: true),
Balance = table.Column<decimal>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ChildOne", x => x.Id);
table.ForeignKey(
name: "FK_ChildOne_ParentOne_ForeignKey",
column: x => x.ForeignKey,
principalTable: "ParentOne",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChildOne_ParentTwo_ForeignKey",
column: x => x.ForeignKey,
principalTable: "ParentTwo",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
注单ForeignKey
列并尝试将其用作 ParentOne
的外键和 ParentTwo
.它遇到与直接使用受限接口(interface)属性相同的问题,因此我认为它不起作用。
关于c# - 我可以在 EF Core 中使用带有外键的接口(interface)并使用 Fluent API 将其设置为外键吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51211301/
编写一个仅用于集中其他接口(interface)的接口(interface)是好的做法还是坏的做法? interface InterfaceA : InterfaceB, InterfaceC { }
有没有一种方法可以确定具体类型从任意接口(interface)列表?我知道类型转换,但我想知道所有满意的接口(interface)。 例如,给定: type Mover interface { Mo
我正在尝试制作斐波那契堆。 (在我正在上的算法课中多次提到它们,我想检查一下。)我希望堆使用任何类型的节点,所以我定义了一个 Node 接口(interface): package node type
这是我的代码: type IA interface { FB() IB } type IB interface { Bar() string } type A struct {
示例 A: // pseudo code interface IFoo { void bar(); } class FooPlatformA : IFoo { void bar() {
合并它编译的 leppies 反馈 - 但 IMO 有一些缺点,我希望编译器强制每个子类定义它们自己的 Uri 属性。现在的代码: [] type UriUserControl() = inh
我正在构建一个项目,该项目从用户那里获取一个术语,然后执行谷歌搜索并返回一个 json 格式的标题列表。 我正在使用 serpwow API 来执行谷歌搜索并试图解析响应。 但是我收到的错误是: pa
我只想在其他接口(interface)中实现某些接口(interface),我不希望它们能够被类直接继承。 提前致谢! 最佳答案 您不能在 C# 中执行此操作 - 任何类都可以实现它有权访问的任何接口
我是 Go 的新手,还有一些我还没有掌握的技巧 例如,我有一个可以这样调用的函数: myVar.InitOperation("foo",Operator.EQUAL,"bar") myVar.Init
我有一个通用接口(interface)来描述对输出流的访问,如下所示: interface IOutput { function writeInteger(aValue:Int):Void;
我正在做一个项目,我想通过某种接口(interface)(最好是 USB)将光电探测器电路安装到计算机上。但是,由于我是新手,所以我不知道应该朝哪个方向处理这个问题。假设我有一个带有 USB 连接的光
背景 我正在尝试创建一个简单的应用程序,以真正理解DDD + TDD + etc的整个堆栈。我的目标是在运行时动态注入DAL存储库类。这让我 域和应用程序服务层可测试。我打算用“穷人的DI”来完成 现
在 Java 中,接口(interface)扩展接口(interface)是完全合法的。 UML 中的这种关系看起来像“扩展”关系(实线、闭合、未填充的箭头)还是“实现”关系(虚线、闭合、未填充的箭头
我想创建一个具有相等和比较函数默认实现的接口(interface)。 如果我从类型 IKeyable 中删除所有内容除了Key成员,只要我不添加默认实现,它就是一个有效的接口(interface)。从
COM 中的双接口(interface)是能够通过 DispInterface 或 VTable 方法访问的接口(interface)。 现在有人可以告诉我这两种方法之间到底有什么区别吗? 我认为 V
我有一个类方法,它返回一个可以迭代的员工列表。返回列表的最佳方式是什么?通常我只返回一个 ArrayList。然而,据我了解,界面更适合这种类型的操作。哪个是最好使用的界面?另外,为什么返回接口(in
我想从包装类外部实例化一个内部非静态接口(interface)。 这可能吗? 考虑以下代码: shared class AOuterClass() { Integer val = 3; shared
我为一个类编写了一个接口(interface),如下所示: public interface IGenericMultipleRepository { Lazy> addresses { ge
我是 UML 的初学者,现在我正在创建一个序列图,问题是我想根据用户输入实现 DAO 接口(interface)。如何在时序图中正确绘制以实现接口(interface)。 最佳答案 您不会在 SD 上
要使用 jsr 303 验证创建有条件验证的组,请将接口(interface)类传递给注释,如下所示: @NotEmpty (groups={UpdateValue.class}) 我有很多不同的接口
我是一名优秀的程序员,十分优秀!