如何创建和填充 DbSet(Of T)?

How to create and populate a DbSet(Of T)?

我正在模拟 DbContext,我需要 .Set(Of TEntity) 方法来 return 底层数据存储的内容(在本例中是 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函数运行时,出现错误:

Unable to cast object of type IQueryable'1[Item] to type DbSet'1[Item]

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

.AsEnumerable.ToArray 都不起作用。相同的转换错误。

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

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

如何将 List(Of TEntity) 数据放入 DbSet(Of TEntity)

我不是通过创建和填充一个新的 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) 收到 'Unsupported Expression' 错误。为此,我已经 opened a ticket 在最小起订量仓库中。

--编辑 2--

再一次,stakx 来救援。

答案在铸件中。大多数模型想要 DbSet(Of T),而两个 Identity-derived 模型(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