I am considering creating a VB.NET 11 WPF MVVM application using Entity Framework 5 and Database First (connecting to SQL Server 2008 R2).
I chose Database First because I am migrating an existing solution to WPF MVVM, where the database already exists, of course.
I would like to start using Dependency Injection so that I can Unit Test make the most of my code.
It seems that I can not find a clear and concise idea of how to use dependency injection with EF DB-First and, in particular, with vb.net. Although even a C # example would be fine, I'm sure.
I would really like this simple step-by-step guide explaining how to set up a solution, how to set up each part ready for Injection Dependency, etc., but it seems hard to find.
So far I have created a solution and its projects as follows:
- DBAccess There is nothing but my .edmx file, and a small mod to be able to connect ConnectionString to the constructor.
- DBControl . The various classes that I use to provide the layer between my EDMX and my ViewModels are stored here. In particular, I fill in the complex types (which I created using the constructor) here to display the "Friendly" data through the user interface, as well as converting these "friendly" complex types into displayed objects for saving / updating. I have one class for a table in my database. Each of them has two methods "FetchFriendlyRecords" (one accepts filters) and the method "AddUpdateFriendlyRecord". I created an interface for each class. Each class accepts a DbContext constructor in the constructor, and I just pass my DBContext from the DBAccess project.
- MainUI . This is where MVVM layers and links to each class are stored in the DBControl project to provide DataBinding, etc.
I saw that instead of spending time creating a complex solution to be able to Unit Test with EF, it’s easier to create a proprietary mock database with filled test data and just specify the code in the mock database, rather than live. However, I would rather be able to create an in-memory solution that will work without any problems with SQL Server.
Any help would be great, including telling me if I'm going to get it all wrong!
Update:
I made the decision provided by Paul Kirby below and created a “Grade” repository template, which I suppose.
I created an interface,
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
Then I implemented this interface in a class by class;
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 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 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
I used constructor injection to insert dbContext into the class.
I was hoping that I could fake a fake dbContext using my existing context and the “Amplification” Unit Testing Tool .
However, it seems like I can't get this to work.
At the same time, in my Unit Test project, I drop (if it already exists) and create an empty test database using the SQLCMD command, using the same schema as my current database.
Then I create a dbContext that references the test database, populate it with test data and check for it.
As a note, I will reorganize my Add / Edit method to work with the actual base Entity, and not with my "friendly" complex version, it was the easiest method at that time.