继承的通用存储库问题
Generic repository issue with inheritance
我在 .Net 中有 N 层解决方案,使用 PetaPoco 作为 microORM。我得到了从 PetaPoco 的模板生成器生成的实体。这些实体 T
来自基础 class Record<T>
。然后我扩展它们,添加更多数据访问工具和自定义 Save()
和 Delete()
方法来覆盖 Record<T>
默认方法。
以下是实体中覆盖的 Delete
方法(我要调用的方法)
public partial class Document : Record<Document>
{
public new int Delete()
{
int rowsAffected;
using (var uow = DB.GetInstance().GetTransaction())
{
rowsAffected = base.Delete();
LogSystem("Deleting", "Deleting a document", 0, 0, GUID);
uow.Complete();
}
return rowsAffected;
}
}
然后,当我创建通用存储库时,调用的方法来自基础 class Record<T>
,而不是来自实体的自定义方法。当我调用 entityRepository.Delete()
方法时,应该调用实体中的 Delete()
方法,而不是默认 Record<T>
class.
中的方法
通用存储库 class 如下所示:
public abstract class GenericRepository<T> : IGenericRepository<T> where T : Record<T>, new()
{
public void Delete(T entity)
{
entity.Delete();
}
}
Confession: I have no knowledge of PetaPoco and its template generator.
与 PetaPoco 恕我直言相比,该问题与 OO(特别是继承)的相关性更高。如果通用存储库是使用 Record<T>
创建的,它也是每个实体的基础 class,那么发生的事情是预期的。您需要仔细研究 classes 的继承。
您可能需要对我在下面提出的解决方案进行一些更改以匹配 ORM 功能。
解决方案 1:
声明一个新的基础 class 或如下所示的接口:
public interface IEntityBase{.....
在接口中包含必要的成员,例如 Delete
方法。
然后,也从该接口派生每个实体:
public partial class Document : Record<Document>, IEntityBase
然后,使用此接口而不是 Record<T>
something 创建一个通用存储库,如下所示:
public abstract class GenericRepository<T> : IGenericRepository<T> where T : IEntityBase
通过这种方式,您可以在不更改大部分现有代码的情况下实现 objective。
解决方案 2:
另一个脏解决方案是将Record<T>
转换为它的实际实例。
public void Delete(T entity)
{
if(typeof(T) == typeof(Document))
Document myEntity = (Document)entity;
myEntity.Delete();
}
在每个方法中进行强制转换绝对不是个好主意。在您的代码中寻找一些更好的集中位置。我只是想给你指路。
解决方案 3:
另一个想法是将 Record<T>
中的 Delete
方法变成 virtual
方法。但我不确定你的 ORM 是如何工作的,甚至你的 ORM 是否允许这样做,所以我不会对此发表评论。
这个问题的原因是在你的 GenericRepository
中你将 T 指定为 "something deriving from Record"。所以我们调用 entity.Delete()
,这个调用将链接到这个基本方法,因为这只对 T 的所有可能实例执行一次。
如果您有权访问 Record<T>
class,只需将 Delete()
方法设为虚拟方法并在 Document
中覆盖它。
如果没有,请尝试使用这样的方法:
public class ExtendedRecord<T>: Record<T>
{
public virtual new int Delete()
{
base.Delete();
}
}
public partial class Document : ExtendedRecord<Document>
{
public override int Delete()
{
// ...
}
}
public abstract class GenericRepository<T> : IGenericRepository<T>
where T : ExtendedRecord<T>, new()
{
// ...
}
我在 .Net 中有 N 层解决方案,使用 PetaPoco 作为 microORM。我得到了从 PetaPoco 的模板生成器生成的实体。这些实体 T
来自基础 class Record<T>
。然后我扩展它们,添加更多数据访问工具和自定义 Save()
和 Delete()
方法来覆盖 Record<T>
默认方法。
以下是实体中覆盖的 Delete
方法(我要调用的方法)
public partial class Document : Record<Document>
{
public new int Delete()
{
int rowsAffected;
using (var uow = DB.GetInstance().GetTransaction())
{
rowsAffected = base.Delete();
LogSystem("Deleting", "Deleting a document", 0, 0, GUID);
uow.Complete();
}
return rowsAffected;
}
}
然后,当我创建通用存储库时,调用的方法来自基础 class Record<T>
,而不是来自实体的自定义方法。当我调用 entityRepository.Delete()
方法时,应该调用实体中的 Delete()
方法,而不是默认 Record<T>
class.
通用存储库 class 如下所示:
public abstract class GenericRepository<T> : IGenericRepository<T> where T : Record<T>, new()
{
public void Delete(T entity)
{
entity.Delete();
}
}
Confession: I have no knowledge of PetaPoco and its template generator.
与 PetaPoco 恕我直言相比,该问题与 OO(特别是继承)的相关性更高。如果通用存储库是使用 Record<T>
创建的,它也是每个实体的基础 class,那么发生的事情是预期的。您需要仔细研究 classes 的继承。
您可能需要对我在下面提出的解决方案进行一些更改以匹配 ORM 功能。
解决方案 1:
声明一个新的基础 class 或如下所示的接口:
public interface IEntityBase{.....
在接口中包含必要的成员,例如 Delete
方法。
然后,也从该接口派生每个实体:
public partial class Document : Record<Document>, IEntityBase
然后,使用此接口而不是 Record<T>
something 创建一个通用存储库,如下所示:
public abstract class GenericRepository<T> : IGenericRepository<T> where T : IEntityBase
通过这种方式,您可以在不更改大部分现有代码的情况下实现 objective。
解决方案 2:
另一个脏解决方案是将Record<T>
转换为它的实际实例。
public void Delete(T entity)
{
if(typeof(T) == typeof(Document))
Document myEntity = (Document)entity;
myEntity.Delete();
}
在每个方法中进行强制转换绝对不是个好主意。在您的代码中寻找一些更好的集中位置。我只是想给你指路。
解决方案 3:
另一个想法是将 Record<T>
中的 Delete
方法变成 virtual
方法。但我不确定你的 ORM 是如何工作的,甚至你的 ORM 是否允许这样做,所以我不会对此发表评论。
这个问题的原因是在你的 GenericRepository
中你将 T 指定为 "something deriving from Record"。所以我们调用 entity.Delete()
,这个调用将链接到这个基本方法,因为这只对 T 的所有可能实例执行一次。
如果您有权访问 Record<T>
class,只需将 Delete()
方法设为虚拟方法并在 Document
中覆盖它。
如果没有,请尝试使用这样的方法:
public class ExtendedRecord<T>: Record<T>
{
public virtual new int Delete()
{
base.Delete();
}
}
public partial class Document : ExtendedRecord<Document>
{
public override int Delete()
{
// ...
}
}
public abstract class GenericRepository<T> : IGenericRepository<T>
where T : ExtendedRecord<T>, new()
{
// ...
}