Search code examples
c#winformsentity-frameworkn-tier-architecturelayered

How do you make a layered solution's UI project resolve EF Entities without referencing the data access layer


I am working on a WinForms application which I have structured with 3 layers, each layer a separate project, as follows:

Projects in solution

Within the SampleNtierDAL project is a DalServices class which is defined as follows:

namespace SampleNtierDAL
{
    public class DalServices
    {
        public static List<Employee> GetEmployees()
        {
            List<Employee> employeeList = null;
            using (SampleNtierEntities aSampleNtierEntitiesDbContext = new SampleNtierEntities())
            {
                employeeList = (from emp in aSampleNtierEntitiesDbContext.Employees select emp).ToList();
            }
            return employeeList;
        }
    }
}

Within the SampleNtierBLL project is a BllServices class defined as follows:

namespace SampleNtierBLL
{
    public class BllSerices
    {
        public static List<Employee> GetEmployees()
        {
            return DalServices.GetEmployees();
        }
    }
}

Te SampleNtierUI project has a WinForm button event which is supposed to ask the BllServices to get an employee list as follows:

namespace SampleNtierUI
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnGetListOfEmployees_Click(object sender, EventArgs e)
        {
            List<Employee> anEmployeeList = BllSerices.GetEmployees();
        }
    }
}

The problem here is that the WinForm is not able to see the entity model called "Employee" which was created by Entity Frameworks 6. The SampleNtierBLL project class BllServices sees the class because it references the SampleNtierDAL and has a use statement at the top to resolve the Employee class. I thought about adding similar references in the WinForm to resolve the Employee class but I have seen in other articles that you are not supposed to make references to the Data Access Layer but such articles don't show how to address this problem I have.
Can someone please show me how to get the Entity model (Employee class) recognized by the UI (WinForm) without making references to the data access layer where the Entity Framework generated the Entity called Employee? Thanks in advance.

Update to my post 11/5/2015 5:13 PM

After more testing with the procedure I added in on post 11/5/2015 3:14 PM I found that it does actually also work on Visual Studio 2015 with .NET 4.5.2.

Update to my post 11/5/2015 3:14 pm:

Thank you Reza for all the work you put into providing the response information. I was not able to make your solution work with Visual Studio 2013 or Visual Studio 2015 as it is documented. However, I did find an internet video on Pluralsight.com by Julie Lerman which does provide a solution. Her solution did not mention to use the NuGet manage part which your's did so I added that to the mix and ended up with a working project. Julie Lerman's video is titled:

"Separating Generated Domain Classes from EDMX" and is located here:

http://www.pluralsight.com/training/player?course=entity-framework5-getting-started&author=julie-lerman&name=ef5-m6-solutions&clip=2&mode=live

By combining part of your procedure with hers I ended with the following steps which work for me on Visual Studio 2013 but not on Visual Studio 2015.

My sample originally had the following project:

  • SampleNtierDAL
  • SampleNtierBLL
  • SampleNtierUI

To this list of projects I add a fourth project called SampleNtierModels so the list is now as follows:

  • SampleNtierDAL
  • SampleNtierBLL
  • SampleNtierUI
  • SampleNtierModels

Steps to follow: 1) Setup project references as follow:

  1. SampleNtierBLL references SampleNtierDAL and SampleNtierModels
  2. SampleNtierDAL references SampleNtierModels
  3. SampleNtierDAL does not reference any project
  4. SampleNtierUI references SampleNtierBLL and SampleNtierModels

2) Next, start the File Explorer program (the tool used to browse file system files) and move the ModelEmployee.tt file from the DAL project file folder to the models file folder.

3) Using the Visual Studio Solution Explorer, go to the SampleNtierModels project, right click and select Add Existing Item. In the dialog, select "All Files" in order to see the ModelEmployee.tt file and select it to add it to the project (do not choose link file).

4) Next, select the SampleNtierDAL project, then right click on the ModelEmployee.tt file and delete it which also automatically delete all the .cs files under it. When done the ModelEmployee.tt node should be gone.

5) Select the SampleNtierModels project then click on the ModelEmployee.tt node which will open up the MoadelEmployee.tt file in the editor. At the top of the contents is an assigment to a const string inputFile. Set this string to locate the ModelEmployee.edmx file as follows:

const string inputFile = @"..\SampleNiter\SampleNtierDAL\ModelEmployee.edmx

5) Next, select the SampleNtierDAL project and open the ModelEmployee.Context.tt file and scroll down ot the section referencing using clauses and add the SampleNtierModels name space as shown below:

if (container.FunctionImports.Any())
{
#>
using System.Data.Entity.Core.Objects;
using System.Linq;
using SampleNtierModels;
<#
}

7) Select the SampleNtierBLL project, open the BLLServices.cs file and add the following using statements:

using SampleNtierModels;
using SampleNtierDAL;

8) Select the SampleNtierDAL project, open the DalServices.cs file and add the following using statement:

using SampleNtierModels;

7) Select the SampleNtierUI project, open the Form1.cs file and add the following using statements:

using SampleNtierModels;
using SampleNtierBLL

8) Click on Tools in menu bar then NuGet Package Manager then

  1. Select installed packages
  2. Search for Entity Framework
  3. Select the displayed EntityFramework package then click its "Manage" button
  4. In the list box make sure the following are selected: SampleNtierBLL, SampleNtierDAL, SamleNtierUI

9) Select the SampleNtierDAL project, right click on ModelEmployee.Context.tt node and select "Run Custom Tool".

10) Select the SampleNtierModels project, right click on ModelEmployee.tt node and select "Run Custom Tool".

11) Copy the entire contents of the App.Config file in the SampleNtierDAL project into the App.Config file of the SampleNtierUI project.

Below is a sample picture showing the what the project looked like in the end along with a sample debug session showing 3 rows of data returned from entity framework which originated from the DAL then to the BLL and finally the UI. The Winform UI also shows no reference was needed to the DAL.

This problem is now solved so once again thank you Reza for helping to solve this problem. I have left a message with Juile requesting for an update to here process which includes Visual Studio 2015.

enter image description here


Solution

  • Short Answer

    You should put your models in a separate project than your DAL to make your models visible to User Interface (UI) project without referencing Data Access Layer (DAL) Project. And all your projects should have a reference to your models project.

    The key point in doing this using a Database/Model First Approach is "Add Existing Item" to your models project and select .tt file of models and choose Add as Link from dropdown of Add button in the dialog.

    Step by Step Guide to Create a Layered Solution

    To put your models in a separate project using entity framework follow these stpes:

    Create Projects

    1. Create a project and name it for example Sample.DAL
    2. Create another project and name it Sample.Models
    3. Create another project and name it Sample.BLL
    4. Create another project and name it Sample.UI

    Config References

    Add references to projects with these rules:

    1. Sample.UI is dependent to Sample.BLL and Sample.Models
    2. Sample.BLL is dependent to Sample.DAL and Sample.Models
    3. Sample.DAL is dependent to Sample.Models
    4. Sample.Models has no dependency to other projects

    Config Sample.DAL

    1. Add your SampleDB.edmx to Sample.DAL project
    2. Expand Sample.edmx node and expand SampleDB.tt node and delete all .cs files under SampleDB.tt
    3. In properties of SampleDB.tt clear CustomTool

    Config Sample.Models

    1. Add Existing Item to Sample.Models and in the dialog, go to folder of Sample.DAL and choose All files from combo box and select SampleDB.tt and click on drop down of Add button and from the menu choose Add As Link
    2. Choose SampleDB.tt and in properties, set CustomToolNamespace to Sample.DAL
    3. Right click on SampleDB.tt and Run Custom Tool

    Manage Nuget Packages

    1. Right click on solution and select Manage Nuget Packages for Solution and select installed packages, select Entity Framework and click Manage. Choose Sample.DAL and Sample.BLL and Sample.UI

    Config Sample.BusinessLogic

    Create a YourEntityBusinessLogic class with this code:

    public class YourEntityBusinessLogic
    {
        public List<YourEntity> GetAll()
        {
            var context = new YourDBContext();
            return context.YourEntities.ToList();
        }
    } 
    

    Pay attention to not expose YourDBContext in public properties or as input parameters of methods and constructors or as return values.

    Config Sample.UI

    1. Copy your connection string from app.config file of Sample.DAL and paste to app.config of Sample.UI before <entityframework> tag.

    2. Create a Form and set it as start up and run this code as test in FormLoad or wherever you want:

    //Shows count of records in your table
    var business = new YourEntityBusinessLogic();
    MessageBox.Show(business.GetAll().Count().ToString());
    

    Solution structure

    Here is the solution structure, and as you see the context is in Sample.DAL and models are in Sample.Models.

    So you don't need to add a reference to DAL in your UI project.

    enter image description here