vb.net - 首先使用 Entity Framework 5 数据库进行依赖注入(inject)。入门?

标签 vb.net entity-framework dependency-injection entity-framework-5 ef-database-first

我正在考虑使用 Entity Framework 5 和数据库优先(连接到 SQL Server 2008 R2)创建 VB.NET 11 WPF MVVM 应用程序。

我选择了数据库优先,因为我正在将现有解决方案迁移到 WPF MVVM,当然数据库已经存在。

我想开始使用依赖注入(inject),这样我就可以对尽可能多的代码进行单元测试。

我似乎无法找到关于如何将依赖注入(inject)与 EF DB-First(尤其是与 vb.net)结合使用的清晰简洁的演练。尽管我确信即使是 C# 示例也可以。

我真正想要的是一个简单的分步指南,解释如何设置解决方案,如何设置每个部分以进行依赖注入(inject)等,但这些似乎很难实现。

到目前为止,我已经创建了解决方案及其项目,如下所示;

  • DBAccess - 这里只包含我的 .edmx 文件和一个能够向构造函数提供 ConnectionString 的小 mod。
  • DBControl - 这里包含各种类,我用它们在 EDMX 和 ViewModel 之间提供一个层。具体来说,我在这里填充复杂类型(我使用设计器创建的),以便通过 UI 显示“友好”数据,并将这些“友好”复杂类型转换为映射实体以进行保存/更新。我的数据库中每个表都有一个类。每个方法都有两个“FetchFriendlyRecords”方法(其中一个接受过滤器)和一个“AddUpdateFriendlyRecord”方法。我为每个类创建了一个接口(interface)。每个类在其构造函数中接受一个 DbContext,我只是从 DBAccess 项目传递我的 DBContext。
  • MainUI - 这里包含我的 MVVM 层,并引用 DBControl 项目中的每个类以提供数据绑定(bind)等。

我看到有人建议,与其花时间编写一个能够使用 EF 进行单元测试的复杂解决方案,不如创建一个填充了测试数据的固定模拟数据库,然后简单地将代码指向模拟数据库,而不是现场的。不过,我更希望能够创建一个内存中解决方案,该解决方案无需访问 SQL Server 即可运行。

任何帮助都会很棒,包括告诉我我是否做错了!

更新:

我采用了下面 Paul Kirby 提供的解决方案,并创建了我认为的“某种”存储库模式。

我创建了一个界面;

Public Interface IFriendlyRepository(Of T)
        ReadOnly Property FriendlyRecords As ObservableCollection(Of T)
        Function GetFilteredFriendlyRecords(predicates As List(of Func(Of T, Boolean))) As ObservableCollection(Of T)
        Function AddEditFriendlyRecord(ByVal RecordToSave As T) As EntityException
        Sub SaveData()
End Interface

然后我逐个类地实现了这个接口(interface);

Namespace Repositories
    Public Class clsCurrenciesRepository
        Implements Interfaces.IFriendlyRepository(Of CriticalPathDB.FriendlyCurrencies)

        Private _DBContext As CriticalPathEntities                          'The Data Context

        Public Sub New(ByVal Context As DbContext)   
            _DBContext = Context
        End Sub

        Public ReadOnly Property FriendlyRecords As ObservableCollection(Of FriendlyCurrencies) Implements Interfaces.IFriendlyRepository(Of CriticalPathDB.FriendlyCurrencies).FriendlyRecords
            Get
                ' We need to convert the results of a Linq to SQL stored procedure to a list,
                ' otherwise we get an error stating that the query cannot be enumerated twice!
                Dim Query = (From Currencies In _DBContext.Currencies.ToList
                             Group Join CreationUsers In _DBContext.Users.ToList
                             On Currencies.CreationUserCode Equals CreationUsers.User_Code Into JoinedCreationUsers = Group
                             From CreationUsers In JoinedCreationUsers.DefaultIfEmpty
                             Group Join UpdateUsers In _DBContext.Users.ToList
                             On Currencies.LastUpdateUserCode Equals UpdateUsers.User_Code Into JoinedUpdateUsers = Group
                             From UpdateUsers In JoinedUpdateUsers.DefaultIfEmpty
                             Where (Currencies.Deleted = False Or Currencies.Deleted Is Nothing)
                             Order By Currencies.NAME
                             Select New FriendlyCurrencies With {.Currency_Code = Currencies.Currency_Code,
                                                                .NAME = Currencies.NAME,
                                                                .Rate = Currencies.Rate,
                                                                .CreatedBy = If(Currencies.CreationUserCode Is Nothing, "", CreationUsers.First_Name & " " & CreationUsers.Last_Name),
                                                                .CreationDate = Currencies.CreationDate,
                                                                .CreationUserCode = Currencies.CreationUserCode,
                                                                .Deleted = Currencies.Deleted,
                                                                .LastUpdateDate = Currencies.LastUpdateDate,
                                                                .LastUpdatedBy = If(Currencies.LastUpdateUserCode Is Nothing, "", UpdateUsers.First_Name & " " & UpdateUsers.Last_Name),
                                                                .LastUpdateUserCode = Currencies.LastUpdateUserCode}).ToList

                Return New ObservableCollection(Of FriendlyCurrencies)(Query)
            End Get
        End Property

        Public Function GetFilteredFriendlyRecords(predicates As List(of Func(Of FriendlyCurrencies, Boolean))) As ObservableCollection(Of FriendlyCurrencies) Implements Interfaces.IFriendlyRepository(Of CriticalPathDB.FriendlyCurrencies).GetFilteredFriendlyRecords
            Dim ReturnQuery = FriendlyRecords.ToList

            For Each Predicate As Func(Of FriendlyCurrencies, Boolean) In predicates
                If Predicate IsNot Nothing Then
                    ReturnQuery = ReturnQuery.Where(Predicate).ToList
                End If
            Next
            Return New ObservableCollection(Of FriendlyCurrencies)(ReturnQuery)
        End Function

        Public Function AddEditFriendlyRecord(ByVal RecordToSave As FriendlyCurrencies) As EntityException Implements Interfaces.IFriendlyRepository(Of CriticalPathDB.FriendlyCurrencies).AddEditFriendlyRecord

            Dim dbCurrency As New Currency
            ' Check if this Staff Member Exists
            Dim query = From c In _DBContext.Currencies
                        Where c.Currency_Code = RecordToSave.Currency_Code
                        Select c

            ' If Asset exists, then edit.
            If query.Count > 0 Then
                dbCurrency = query.FirstOrDefault
            Else
                'Do Nothing
            End If

            dbCurrency.Currency_Code = RecordToSave.Currency_Code
            dbCurrency.NAME = RecordToSave.NAME
            dbCurrency.CreationDate = RecordToSave.CreationDate
            dbCurrency.CreationUserCode = RecordToSave.CreationUserCode
            dbCurrency.LastUpdateDate = RecordToSave.LastUpdateDate
            dbCurrency.LastUpdateUserCode = RecordToSave.LastUpdateUserCode
            dbCurrency.Deleted = RecordToSave.Deleted

            ' Save Asset Object to Database
            If query.Count > 0 Then
                ' If Asset exists, then edit.
                Try
                    '_dbContext.SaveChanges           'We could save here but it's generally bad practice
                Catch ex As EntityException
                    Return ex
                End Try
            Else
                Try
                    _DBContext.Currencies.Add(dbCurrency)
                    '_dbContext.SaveChanges           'We could save here but it's generally bad practice
                Catch ex As EntityException
                    Return ex
                End Try
            End If
            Return Nothing
        End Function

        Public Sub SaveData() Implements Interfaces.IFriendlyRepository(Of CriticalPathDB.FriendlyCurrencies).SaveData
            _DBContext.SaveChanges()
        End Sub
    End Class
End Namespace

我使用构造函数注入(inject)将 dbContext 插入到类中。

我曾希望能够使用我现有的上下文和"Effort" Unit Testing Tool来模拟一个假的dbContext。 .

但是,我似乎无法让它发挥作用。

在此期间,在我的单元测试项目中,我将使用 SQLCMD 命令删除(如果它已经存在)并创建一个空的测试数据库,并使用与我的实时数据库相同的架构。

然后,我创建一个引用测试数据库的 dbContext,用测试数据填充它,并针对此进行测试。

作为注释,我将重构我的“添加/编辑”方法以使用实际的基本实体,而不是我的“友好”复杂版本,这是当时最简单的方法。

最佳答案

如果您的工作是数据库优先,那么我的建议如下。

  1. 打开 .edmx 文件,右键单击任意空白区域并选择“添加代码生成项”
  2. 在“在线模板”区域中,搜索“EF 5.x DbContext Generator for VB”。
  3. 为 .tt 文件命名,然后点击“添加”。这将改变您的 .edmx 文件生成支持代码的方式,以便您的实体都是 POCO,从而通过保持主逻辑与 EF 断开连接来简化整体测试。

完成此操作后,您可能想要研究诸如工作单元模式之类的内容。这是一个快速代码示例,稍后我将对其进行解释。

public interface IUnitOfWork
{
    IDbSet<Location> Locations { get; }
    void Commit();
}

public class EFUnitOfWork : IUnitOfWork
{
    private readonly YourGeneratedDbContext _context;

    public EFUnitOfWork(string connectionString)
    {
        _context = new YourGeneratedDbContext();
    }

    public IDbSet<Location> Locations
    {
        get { return _context.Locations; }
    }

    public void Commit()
    {
        _context.SaveChanges();
    }
}

这是一个基本的工作单元,它公开了一些位置列表作为示例(抱歉,它是用 C# 编写的,但我不太了解 VB)。

请注意,它公开了 IDbSet 对象 - 这就是神奇之处。如果在您的 DBAccess 项目中,您使用此工作单元或存储库模式来隐藏 EF,并且因为它实现了一个接口(interface)并返回 IDbSet 对象,任何需要数据的地方都可以用 DI 注入(inject)这个 IUnitOfWork 构造函数,并在需要进行单元测试时替换为返回模拟 IDbSet 对象的模拟版本(它们最终只是 IQueryables)。

您可能会发现,通过新模板中的 POCO 生成,您甚至可以免除在 DBControl 项目中所做的大量工作。

无论如何,这只是定位项目以实现最佳单元测试和 DI 的一些基本内容。

关于vb.net - 首先使用 Entity Framework 5 数据库进行依赖注入(inject)。入门?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12850340/

相关文章:

java - Spring:没有限定符的Autowire bean

c# - 帮助程序/中间类在数据库中没有列( Entity Framework )

dependency-injection - 在依赖注入(inject)中什么是依赖?

mysql - vb.net 代码可以在本地运行,但不能在 Web 服务器上运行

asp.net - 如何将 Stream 转换为 Byte()

C# - Entity Framework - 连接方法

asp.net-mvc - WebGrid 和 EF4 属性

javascript - 如何向底层库提供 db、logger 等依赖项?

.net - 在加载表单时防止绘制 .NET 表单?

vb.net - 防止 ListView 取消选择