4

Entity Framework 5 と Database First (SQL Server 2008 R2 への接続) を使用して VB.NET 11 WPF MVVM アプリケーションを作成しようとしています。

もちろん、データベースが既に存在する WPF MVVM に既存のソリューションを移行しているため、データベース ファーストを選択しました。

できるだけ多くのコードを単体テストできるように、依存性注入の使用を開始したいと考えています。

EF DB-First、特にvb.netで依存性注入を使用する方法の明確で簡潔なウォークスルーを見つけることができないようです。C# の例でも問題ありませんが、私は確信しています。

私が本当に欲しいのは、ソリューションをセットアップする方法、依存性注入の準備ができている各部分をセットアップする方法などを説明する簡単なステップバイステップのガイドですが、これらを手に入れるのは難しいようです.

これまでのところ、次のようにソリューションとプロジェクトを作成しました。

  • DBAccess - これには、私の .edmx ファイルと、コンストラクターに ConnectionString を提供できるようにするための小さな mod しか含まれていません。
  • DBControl - これには、EDMX と ViewModel の間のレイヤーを提供するために使用するさまざまなクラスが含まれています。具体的には、UI を介して「より使いやすい」データを表示し、保存/更新のためにこれらの「使いやすい」複合型をマップされたエンティティに変換するために、ここで複合型 (デザイナーを使用して作成したもの) を埋めています。データベースのテーブルごとに 1 つのクラスがあります。それぞれに 2 つの"FetchFriendlyRecords"メソッド (1 つはフィルターを受け入れます) と " AddUpdateFriendlyRecord " メソッドがあります。各クラスのインターフェイスを作成しました。各クラスはコンストラクターで DbContext を受け入れます。DBAccess プロジェクトから DBContext を渡すだけです。
  • MainUI - これは、MVVM レイヤーを格納し、DataBinding などを提供するために DBControl プロジェクトの各クラスを参照します。

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

次に、このインターフェイスをクラスごとに実装しました。

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

コンストラクター インジェクションを使用して、dbContext をクラスに挿入しました。

既存のコンテキストと"Effort" Unit Testing Toolを使用して、偽の dbContext をモックアップできることを望んでいました。

しかし、私はこれを機能させることができないようです。

暫定的に、ユニット テスト プロジェクトで、ライブ データベースと同じスキーマを使用して、SQLCMD コマンドを使用して (既に存在する場合は) 削除し、空のテスト データベースを作成します。

次に、テスト データベースを参照する dbContext を作成し、テスト データを入力して、これに対してテストします。

注意として、「フレンドリーな」複雑なバージョンではなく、実際の基本エンティティで動作するように「追加/編集」メソッドをリファクタリングします。これは当時最も単純な方法でした。

4

1 に答える 1

6

DB ファーストで作業している場合は、次のことをお勧めします。

  1. .edmx ファイルを開き、空白スペースを右クリックして、[コード生成項目の追加] を選択します。
  2. [オンライン テンプレート] エリアで、「EF 5.x DbContext Generator for VB」を検索します。
  3. .tt ファイルに名前を付けて、追加をクリックします。これにより、エンティティがすべて POCO になるように .edmx ファイルがバッキング コードを生成する方法が変更され、メイン ロジックが EF から切り離されたままになるため、全体的なテストが簡素化されます。

それが完了したら、おそらく Unit of Work パターンのようなものを調べたいと思うでしょう。簡単なコード例を次に示します。後で説明します。

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 を非表示にし、インターフェイスを実装して IDbSet オブジェクトを返すため、単体テストが必要な場合は、データにこの IUnitOfWork コンストラクターを DI で挿入し、モック IDbSet オブジェクト (最終的には単なる IQueryables) を返すモック バージョンに置き換える必要があります。

その新しいテンプレートの POCO 生成により、DBControl プロジェクトで行っている多くの作業をなくすことさえできることに気付くかもしれません。

とにかく、これは最適な単体テストと DI のためにプロジェクトを配置するという点で、いくつかの基本的なことです。

于 2012-10-12T18:04:00.987 に答える