Introduction

When creating a windows form project or a class project for interacting with a backend database using code first approach scaffolding generates a model class and entity classes in the base project folder.

This article intention is to provide insight on how to organize generated class files into a manageable structure.

Standard folder structure

The following image of a solution depicts the organization of Entity Framework class.



Issues with a standard folder structure

The issues are as a project grows Interfaces and other classes added to the solution can make maintaining the solution difficult. Introducing more databases can be troublesome too. 

Moving classes to folders

In the following image the model/context class has been moved to a folder named Contexts by right clicking on the project name in solution explorer to create the folder. Next, select NorthWindContext.vb from the main project folder and drag the file to the new folder. 

Next a folder is created for the model.


The next step is to place each of the classes into one namespace no matter the folder. In this case the namespace is NorthWindModel.

The context class.
Imports System.Data.Entity
Imports NorthWind.Data.NorthWindModel
 
Namespace Contexts
    Partial Public Class NorthWindContext
        Inherits DbContext
 
        Public Sub New()
            MyBase.New("name=NorthWindModel")
        End Sub
 
        Public Overridable Property Categories As DbSet(Of Category)

One of the table classes.
Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel.DataAnnotations.Schema
 
Namespace NorthWindModel
 
    <Table("Contact")>
    Partial Public Class Contact
        Public Sub New()
            ContactContactDevices = New HashSet(Of ContactContactDevice)()
            Customers = New HashSet(Of Customer)()
        End Sub
 
        <Key>
        Public Property ContactIdentifier As Integer

Namespace separation

Using a namespace allows a developer to use another database with Entity Framework where there may be common class names. In the Visual Studio solution provided there are two databases setup with Entity Framework 6 (same works for Entity Framework Core).



In this solution both databases have a country class as per below. If there were two databases with classes in the main project folder in this case one of the country classes would not survive physically and moving one country class to another folder does not change scope, they need to be in different namespaces.

Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel.DataAnnotations.Schema
 
Namespace HumanResourcesModel
 
    Partial Public Class Country
        Public Sub New()
            locations = New HashSet(Of Location)()
        End Sub
 
        <Key>
        <Column("Country_id")>
        <StringLength(2)>
        Public Property CountryId As String
 
        <StringLength(40)>
        <Column("Country_name")>
        Public Property CountryName As String
        <Column("Region_id")>
        Public Property RegionId As Integer
 
        Public Overridable Property Region As Region
 
        Public Overridable Property Locations As ICollection(Of Location)
    End Class
End Namespace

And

Namespace NorthWindModel
 
    Partial Public Class Country
        Public Sub New()
            Customers = New HashSet(Of Customer)()
        End Sub
 
        Public Property Id As Integer
 
        Public Property CountryName As String
 
        Public Overridable Property Customers As ICollection(Of Customer)
    End Class
End Namespace

Working data

A best practice is to separate data operations from the model into folders. In this case there are two database operations done in separate folders.



North wind operations
Imports NorthWind.Data.Contexts
Namespace NorthWindOperations
    Public Class Operations
        Public Function CustomerJoined() As List(Of CustomerEntity)
 
            Using context = New NorthWindContext()
                Return (
                        From customer In context.Customers
                        Join contactType In context.ContactTypes On customer.ContactTypeIdentifier _
                            Equals contactType.ContactTypeIdentifier
                        Join contact In context.Contacts On customer.ContactIdentifier _
                            Equals contact.ContactIdentifier
                        Join country In context.Countries On customer.CountryIdentfier _
                            Equals country.Id
                        Select New CustomerEntity With
                            {
                                .CustomerIdentifier = customer.CustomerIdentifier,
                                .CompanyName = customer.CompanyName,
                                .ContactIdentifier = customer.ContactIdentifier,
                                .FirstName = contact.FirstName,
                                .LastName = contact.LastName,
                                .ContactTypeIdentifier = contactType.ContactTypeIdentifier,
                                .ContactTitle = contactType.ContactTitle,
                                .Address = customer.Street,
                                .City = customer.City,
                                .PostalCode = customer.PostalCode,
                                .CountryIdentifier = customer.CountryIdentfier,
                                .CountyName = country.CountryName}
                    ).ToList()
 
            End Using
        End Function
    End Class
End Namespace

Then for human resources
Imports NorthWind.Data.HumanResourcesModel
 
Namespace HumanResourcesOperations
    Public Class Operations
        Public Function EmployeeJoined() As List(Of EmployeeItem)
 
            Using context As New HumanResourcesContext
                Return (From employee In context.Employees
                        Join dept In context.Departments On employee.DepartmentId _
                            Equals dept.DepartmentId
                        Join job In context.Jobs On employee.JobId _
                            Equals job.JobId
                        Select New EmployeeItem With
                            {
                                .Id = employee.EmployeeId,
                                .Title = job.JobTitle,
                                .FirstName = employee.FirstName,
                                .LastName = employee.LastName,
                                .Department = dept.DepartmentName,
                                .Location = dept.Location
                            }
                        ).ToList()
            End Using
        End Function
    End Class
End Namespace

Even though both database data operations have the same class name they are separated by a namespace which means to use both in the same project use the appropriate namespace. Keeping things simple a test project is used to show how to use both databases in the same class.

Imports NorthWind.Data
 
<TestClass()> Public Class UnitTest1
    <TestMethod()> Public Sub HumanResourcesTest()
 
        Dim operations As New HumanResourcesOperations.Operations
        Dim employeeItems = operations.EmployeeJoined()
 
        Assert.IsTrue(employeeItems.Count = 40,
                      "Expected 40 employees items")
 
    End Sub
    <TestMethod()> Public Sub NorthWindTest()
 
        Dim operations As New NorthWindOperations.Operations
        Dim customerItems = operations.CustomerJoined()
 
        Assert.IsTrue(customerItems.Count = 98,
                      "Expected 98 customer items")
 
    End Sub
End Class

In a production application import aliasing would be better than having to type out the full namespace. Here North and Human are aliases to the actual namespaces.

Imports North = NorthWind.Data.NorthWindOperations
Imports Human = NorthWind.Data.HumanResourcesOperations
 
<TestClass()> Public Class UnitTest1
    <TestMethod()> Public Sub HumanResourcesTest()
 
        Dim operations As New Human.Operations
        Dim employeeItems = operations.EmployeeJoined()
 
        Assert.IsTrue(employeeItems.Count = 40,
                      "Expected 40 employees items")
 
    End Sub
    <TestMethod()> Public Sub NorthWindTest()
 
        Dim operations As New North.Operations
        Dim customerItems = operations.CustomerJoined()
 
        Assert.IsTrue(customerItems.Count = 98,
                      "Expected 98 customer items")
 
    End Sub
End Class

Summary

This article provided alternate folder solution for a Visual Studio solution with one or more Entity Framework models which provides separation of models using physical folders and namespaces which allows models to live harmoniously in one Visual Studio solution. An alternate would be to create two distinct class project, one for each model while a limitation would be having to access both class projects in a front end project to gather data rather than from one main namespace. Both have their places, this article has offered a alternate solution in one class project. 

See also

Entity Framework: Wiki portal 
Entity Framework TechNet 

Source code


GitHub repository which has data scripts in the folder beneath the solution by the name of DatabaseScripts. Before running the scripts, inspect them along with the database connections in the class project and test project to match your database server be it SQL-Express, SQL-Server or localdb.