Introduction

This sample shows a WPF application built on top of the Microsoft ADO.NET Entity Framework. The application shows how the Entity Framework can be used in some common design patterns that promote testability and maintainability of code.

There is a 'useFakes' boolean flag located in the code behind App.xaml in the EmployeeTracker project that determines whether the application runs against Microsoft SQL Server using the Entity Framework or against in-memory fakes. When set to false the app will try and attach the MDF file located in the EntityFramework project to the local Microsoft SQL Server Express instance. This connection can be changed in the App.config file of the EmployeeTracker project.

Building the Sample

Press F5

Description

The solution is made up of the following projects:

Model

This project contains the business model.

The Department and Employee objects have some embedded logic that keeps navigation properties synchronized. Setting the Department property on an Employee results in that Employee being added to the Employees collection on the new Department and being removed from the Employees collection on the old Department. Similar logic applies if an Employee is added to or removed from the Employees collection on a Department. The same fixup approach is also implemented on the Manager/Reports relationship. This logic is not required when running against the Entity Framework using change tracking proxies as the proxies will perform this fix-up automatically but the logic is central to the business model and should remain in place when using Fakes or any other persistence mechanism.

There are a set of tests defined in the abstract class Tests\Model\Entities\FixupTestsBase.cs that test fix-up behavior. This class is derived from to run the same tests against the following versions of the business objects:

Common

This project contains a set of interfaces for data retrieval and persistence using the Unit of Work and Repository pattern. The project includes an implementation for some of the interfaces that can be re-used with multiple data access approaches. The IEmployeeContext interface represents the underlying functionality required for data access and is implemented in the EntityFramework and Fakes projects.

EntityFramework

This project contains the Entity Data Model (EmployeeModel.edmx) that maps between the database (Employee.mdf) and the business objects defined in the Model project. There is also a custom T4 template (ContextTemplate.tt) that is based on the default template but has entity generation removed as we are using the pre-defined business objects. The T4 template also adds the IEmployeeContext interface to the generated context.

The Entity Data Model includes a Model Defined Function (MDF) which calculates an Employee’s tenure. The Employee repository in the Common project has a private method for calculating tenure which is marked with an EdmFunction attribute. When running against the Entity Framework the MDF will be used instead of this method and evaluation will occur in the database. The method includes an implementation which is used when running against fakes.

Fakes

This project contains in-memory versions of the data access components, these are primarily used for unit testing but the WPF application can also be run against these implementations. Also included is a class for instantiating a fake context that is pre-populated with a set of sample data.

EmployeeTracker

This project is a user interface implemented in WPF using the Model-View-ViewModel pattern. The entry point for the application is in the code behind file for App.xaml where a UnitOfWork and repositories are constructed. When running against EF there is a connection string in App.config which controls access to the database. If you don't have a local Microsoft Sql Server Express instance available called .\SQLEXPRESS then you will need to update this connection.

Tests

This project contains unit tests for components in the other projects. When testing EF components a connection string in App.config is used. The database is not attached during testing so only the metadata sections of the connection string are used.


  

Screenshot

 

Sample Code

C#
Edit|Remove
        /// <summary>  
        /// Initializes a new instance of the MainViewModel class.  
        /// </summary>  
        /// <param name="unitOfWork">UnitOfWork for co-ordinating changes</param>  
        /// <param name="departmentRepository">Repository for querying department data</param>  
        /// <param name="employeeRepository">Repository for querying employee data</param>  
        public MainViewModel(IUnitOfWork unitOfWork, IDepartmentRepository departmentRepository, IEmployeeRepository employeeRepository)  
        {  
            if (unitOfWork == null)  
            {  
                throw new ArgumentNullException("unitOfWork");  
            }  
  
            if (departmentRepository == null)  
            {  
                throw new ArgumentNullException("departmentRepository");  
            }  
  
            if (employeeRepository == null)  
            {  
                throw new ArgumentNullException("employeeRepository");  
            }  
  
            this.unitOfWork = unitOfWork;  
  
            // Build data structures to populate areas of the application surface  
            ObservableCollection<EmployeeViewModel> allEmployees = new ObservableCollection<EmployeeViewModel>();  
            ObservableCollection<DepartmentViewModel> allDepartments = new ObservableCollection<DepartmentViewModel>();  
  
            foreach (var dep in departmentRepository.GetAllDepartments())  
            {  
                allDepartments.Add(new DepartmentViewModel(dep));  
            }  
  
            foreach (var emp in employeeRepository.GetAllEmployees())  
            {  
                allEmployees.Add(new EmployeeViewModel(emp, allEmployees, allDepartments, this.unitOfWork));  
            }  
  
            this.DepartmentWorkspace = new DepartmentWorkspaceViewModel(allDepartments, unitOfWork);  
            this.EmployeeWorkspace = new EmployeeWorkspaceViewModel(allEmployees, allDepartments, unitOfWork);  
  
            // Build non-interactive list of long serving employees  
            List<BasicEmployeeViewModel> longServingEmployees = new List<BasicEmployeeViewModel>();  
            foreach (var emp in employeeRepository.GetLongestServingEmployees(5))  
            {  
                longServingEmployees.Add(new BasicEmployeeViewModel(emp));  
            }  
  
            this.LongServingEmployees = longServingEmployees;  
  
            this.SaveCommand = new DelegateCommand((o) => this.Save());  
        } 
 
Visual Basic
Edit|Remove
        ''' <summary>  
        ''' Initializes a new instance of the MainViewModel class.  
        ''' </summary>  
        ''' <param name="unitOfWork">UnitOfWork for co-ordinating changes</param>  
        ''' <param name="departmentRepository">Repository for querying department data</param>  
        ''' <param name="employeeRepository">Repository for querying employee data</param>  
        Public Sub New(ByVal unitOfWork As IUnitOfWork, ByVal departmentRepository As IDepartmentRepository, ByVal employeeRepository As IEmployeeRepository)  
            If unitOfWork Is Nothing Then  
                Throw New ArgumentNullException("unitOfWork")  
            End If  
  
            If departmentRepository Is Nothing Then  
                Throw New ArgumentNullException("departmentRepository")  
            End If  
  
            If employeeRepository Is Nothing Then  
                Throw New ArgumentNullException("employeeRepository")  
            End If  
  
            Me.unitOfWork = unitOfWork  
  
            ' Build data structures to populate areas of the application surface  
            Dim allEmployees As New ObservableCollection(Of EmployeeViewModel)()  
            Dim allDepartments As New ObservableCollection(Of DepartmentViewModel)()  
  
            For Each dep In departmentRepository.GetAllDepartments()  
                allDepartments.Add(New DepartmentViewModel(dep))  
            Next dep  
  
            For Each emp In employeeRepository.GetAllEmployees()  
                allEmployees.Add(New EmployeeViewModel(emp, allEmployees, allDepartments, Me.unitOfWork))  
            Next emp  
  
            Me.DepartmentWorkspace = New DepartmentWorkspaceViewModel(allDepartments, unitOfWork)  
            Me.EmployeeWorkspace = New EmployeeWorkspaceViewModel(allEmployees, allDepartments, unitOfWork)  
  
            ' Build non-interactive list of long serving employees  
            Dim longServingEmployees As New List(Of BasicEmployeeViewModel)()  
            For Each emp In employeeRepository.GetLongestServingEmployees(5)  
                longServingEmployees.Add(New BasicEmployeeViewModel(emp))  
            Next emp  
  
            Me.LongServingEmployees = longServingEmployees  
  
            Me.SaveCommand = New DelegateCommand(Sub(o) Me.Save())  
        End Sub 
 
Sample Source Code Files

More Information

For more information on Entity Framework and related topics with this sample, click a link below.