【问题标题】:How to create and populate a DbSet(Of T)?如何创建和填充 DbSet(Of T)?
【发布时间】:2020-10-22 08:32:36
【问题描述】:

我正在模拟 DbContext,我需要 .Set(Of TEntity) 方法来返回底层数据存储的内容(在本例中为 List(Of TEntity))。

这是我的.Setup() 扩展方法:

<Extension>
Public Sub Setup(Of TEntity As Class)(Instance As Mock(Of Db.Context), Entities As List(Of TEntity))
  Dim oReturn As Func(Of DbSet(Of TEntity))
  Dim oSetup As Expression(Of Func(Of Db.Context, DbSet(Of TEntity)))

  oReturn = Function() Entities.AsQueryable
  oSetup = Function(Context) Context.Set(Of TEntity)

  Instance.Setup(oSetup).Returns(oReturn)
End Sub

问题是当oReturn函数运行时,会出现错误:

无法将IQueryable'1[Item] 类型的对象转换为DbSet'1[Item] 类型

由于DbSet(Of TEntity)是一个抽象类,所以不能直接实例化。似乎没有办法将这些数据放入DbSet。 (旁注:这加深了 EF 本身如何管理任务的奥秘——我浏览了一下源代码,但发现是空的。)

.AsEnumerable.ToArray 都不起作用。同样的投射错误。

this answer,但我无法进行类似的调整,因为 DbContext 也用于管理我的 ASP.NET 身份存储 - 存储库模式在我的代码中并不普遍适用。我必须改为模拟 DbContext

然后是this question,但仍未得到答复。

我怎样才能将 List(Of TEntity) 数据放入 DbSet(Of TEntity)

【问题讨论】:

    标签: entity-framework mocking entity-framework-6 moq dbcontext


    【解决方案1】:

    我不是通过创建和填充新的DbSet(Of TEntity) 来做到这一点的,正如这个问答标题所假设的,而是通过模拟DbSet(Of TEntity)

    <Extension>
    Public Sub Setup(Of TEntity As Class)(Instance As Mock(Of Db.Context), Entities As List(Of TEntity), ContextSetup As Expression(Of Func(Of Db.Context, DbSet(Of TEntity))))
      Dim oDbSetReturn As Func(Of DbSet(Of TEntity))
      Dim oDbSetSetup As Expression(Of Func(Of Db.Context, DbSet(Of TEntity)))
      Dim oDbSetMock As Mock(Of DbSet(Of TEntity))
      Dim oDbSet As DbSet(Of TEntity)
    
      oDbSetMock = New Mock(Of DbSet(Of TEntity))
      oDbSetMock.Setup(Entities)
      oDbSet = oDbSetMock.Object
    
      oDbSetReturn = Function() oDbSet
      oDbSetSetup = Function(Context) Context.Set(Of TEntity)
    
      Instance.Setup(ContextSetup).Returns(oDbSet)
      Instance.Setup(oDbSetSetup).Returns(oDbSetReturn)
    End Sub
    

    这是 DbSet(Of TEntity) 设置的配套方法:

    <Extension>
    Public Sub Setup(Of TEntity As Class)(Instance As Mock(Of DbSet(Of TEntity)), Entities As List(Of TEntity))
      Dim oRemoveRangeReturn As Func(Of IEnumerable(Of TEntity), IEnumerable(Of TEntity))
      Dim oRemoveRangeSetup As Expression(Of Func(Of DbSet(Of TEntity), IEnumerable(Of TEntity)))
      Dim oAddRangeReturn As Func(Of IEnumerable(Of TEntity), IEnumerable(Of TEntity))
      Dim oAddRangeSetup As Expression(Of Func(Of DbSet(Of TEntity), IEnumerable(Of TEntity)))
      Dim oRemoveReturn As Func(Of TEntity, TEntity)
      Dim oRemoveSetup As Expression(Of Func(Of DbSet(Of TEntity), TEntity))
      Dim oAddReturn As Func(Of TEntity, TEntity)
      Dim oAddSetup As Expression(Of Func(Of DbSet(Of TEntity), TEntity))
    
      Instance.As(Of IQueryable(Of TEntity)).Setup(Function(Queryable) Queryable.GetEnumerator).Returns(Entities.AsQueryable.GetEnumerator)
      Instance.As(Of IQueryable(Of TEntity)).Setup(Function(Queryable) Queryable.ElementType).Returns(Entities.AsQueryable.ElementType)
      Instance.As(Of IQueryable(Of TEntity)).Setup(Function(Queryable) Queryable.Expression).Returns(Entities.AsQueryable.Expression)
      Instance.As(Of IQueryable(Of TEntity)).Setup(Function(Queryable) Queryable.Provider).Returns(Entities.AsQueryable.Provider)
    
      oRemoveRangeSetup = Function(DbSet) DbSet.RemoveRange(It.IsAny(Of IEnumerable(Of TEntity)))
      oAddRangeSetup = Function(DbSet) DbSet.AddRange(It.IsAny(Of IEnumerable(Of TEntity)))
      oRemoveSetup = Function(DbSet) DbSet.Remove(It.IsAny(Of TEntity))
      oAddSetup = Function(DbSet) DbSet.Add(It.IsAny(Of TEntity))
    
      oRemoveRangeReturn = Function(Range)
                             Entities = Entities.Except(Range)
                             Return Entities
                           End Function
    
      oAddRangeReturn = Function(Range)
                          Entities.AddRange(Range)
                          Return Entities
                        End Function
    
      oRemoveReturn = Function(Entity)
                        Entities.Remove(Entity)
                        Return Entity
                      End Function
    
      oAddReturn = Function(Entity)
                     Entities.Add(Entity)
                     Return Entity
                   End Function
    
      Instance.Setup(oRemoveRangeSetup).Returns(oRemoveRangeReturn)
      Instance.Setup(oAddRangeSetup).Returns(oAddRangeReturn)
      Instance.Setup(oRemoveSetup).Returns(oRemoveReturn)
      Instance.Setup(oAddSetup).Returns(oAddReturn)
    End Sub
    

    它是这样称呼的:

    Private ReadOnly Property DbContextFactory As Func(Of Db.Context)
      Get
        Dim oContextMock As Mock(Of Db.Context)
    
        Return Function()
                 oContextMock = New Mock(Of Db.Context)
                 oContextMock.Setup(Of Db.City)(Me.Cities, Function(Context) Context.Cities)
                 oContextMock.Setup(Of Db.Role)(Me.Roles, Function(Context) Context.Roles)
                 oContextMock.Setup(Of Db.User)(Me.Users, Function(Context) Context.Users)
    
                 Return oContextMock.Object
               End Function
      End Get
    End Property
    

    这应该可行。我目前无法测试它,但我会尽快测试并提供结果。

    --编辑 1--

    我在Instance.Setup(ContextSetup) 收到“不支持的表达式”错误。为此,我在 Moq repo 上 opened a ticket

    --编辑 2--

    再一次,stakx 来救援。

    解决方案在铸件中找到。大多数模型需要DbSet(Of T),而两个身份派生模型(Db.RoleDb.User)需要IDbSet(Of T)

    这会导致一对重载的扩展方法:

    <Extension>
    Public Sub Setup(Of TEntity As Class)(
        Instance As Mock(Of Db.Context),
        Entities As List(Of TEntity),
        DbSetSetup As Expression(Of Func(Of Db.Context, IDbSet(Of TEntity)))
      )
    
      Instance.Setup(DbSetSetup).Returns(DbSet(Entities))
      Instance.Setup(MethodSetup(Of TEntity)).Returns(MethodReturn(Entities))
    End Sub
    
    <Extension>
    Public Sub Setup(Of TEntity As Class)(
        Instance As Mock(Of Db.Context),
        Entities As List(Of TEntity),
        DbSetSetup As Expression(Of Func(Of Db.Context, DbSet(Of TEntity)))
      )
    
      Instance.Setup(DbSetSetup).Returns(DbSet(Entities))
      Instance.Setup(MethodSetup(Of TEntity)).Returns(MethodReturn(Entities))
    End Sub
    
    Private Function MethodSetup(Of TEntity As Class)() As Expression(Of Func(Of Db.Context, DbSet(Of TEntity)))
      Return Function(Context) Context.Set(Of TEntity)
    End Function
    
    Private Function MethodReturn(Of TEntity As Class)(Entities As List(Of TEntity)) As Func(Of DbSet(Of TEntity))
      Return Function() DbSet(Entities)
    End Function
    
    Private Function DbSet(Of TEntity As Class)(Entities As List(Of TEntity)) As DbSet(Of TEntity)
      With New Mock(Of DbSet(Of TEntity))
        .Setup(Entities)
        Return .Object
      End With
    End Function
    

    最后这一切都奏效了。

    谢谢stakx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-06-17
      • 2013-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多