AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1943&pId=-1
ASP.NET 4.0 and the Entity Framework 4 - Part 4 - A 3 Layered Approach to the Entity Framework
page
by Vince Varallo
Feedback
Average Rating: 
Views (Total / Last 10 Days): 62118/ 62

Introduction

The previous 3 articles in this series showed you how easy it is to use the Entity Framework 4 to create a database, add, update, delete, and select records from tables, and also how to execute stored procedures.  The actual amount of coding required to create a web page that can manage data in a table is quite small.  However, the examples didn't really show you any type of pattern.  They simply showed you the syntax for making the web page work.  I'm sure by now you've read an article or a book about separating your logic into the user interface, the business logic layer, and the data access layer and are scratching your head thinking that the Entity Framework kind of encapsulates the business layer and data access layer and all you need is a GridView control on your form and you're in business.  For some applications this can and will work fine.  Every pattern isn't for every application or every developer.  However, just from experience I have always found it better to at least separate out the three layers logically. 

This article will create the same web page that was created in the second and third articles except it will introduce a pattern for developing the application as a three layered application.  Notice that I didn't say three tiered application. A tier really means a logical and physical separation of the layers. A layer implies just a logical separation. For many applications that's all you need.  I'm not one to advocate over-engineering a system. I always follow the simpler, better approach as long as the developers working with you understand the pattern. 

This article will create a web application as the user interface layer and two class libraries for the business logic layer (BLL) and the data access layer (DAL). The code to communicate with the database will be encapsulated in the DAL project. The entities in the Data Context will act as the data transfer objects (DTOs) that pass data between the DAL and BLL. The concept of a DTO is not new. Previously you would need to write your own custom classes but the entity framework can save you all that custom coding.

The goal of this article is to create a web page that allows a user to maintain the records in a table called UserAccounts. The final web page looks like the following image.

The drop down list at the top of the page allows you to navigate from user to user and displays the properties on the page.  The user can simply click the Save button to add or update records or click the Delete button to remove a record.

Step 1: Create the Database

To keep things simple create a database in SQL Server 2008 called OrderSystem and add a single table called UserAccounts to the database. The fields for this table are shown in the following diagram.

The Id field should be marked as an Identity field and also the Primary Key.  All access to the table will be done with stored procedures.  Add the following stored procedures to the OrderSystem Database.

1.    The UserAccounts_SelectAll procedure returns a resultset containing all the records in the UserAccounts table.

CREATE PROCEDURE [dbo].[UserAccounts_SelectAll] 
AS
    SET NOCOUNT ON
 
    SELECT Id, FirstName, LastName, AuditFields_InsertDate, AuditFields_UpdateDate
      FROM UserAccounts
 
    RETURN

2.    The UserAccounts_SelectById procedure returns a single record from the UserAccounts table given a specific Id.

CREATE PROCEDURE [dbo].[UserAccounts_SelectById]      
(
    @Id int 
)
 
AS
    SET NOCOUNT ON 
 
    SELECT Id, FirstName, LastName, AuditFields_InsertDate, AuditFields_UpdateDate
      FROM UserAccounts
     WHERE Id = @Id
 
    RETURN

3.    The UserAccounts_Insert procedure adds a record to the UserAccounts table.

CREATE PROCEDURE [dbo].[UserAccounts_Insert]
(
    @FirstName nvarchar(50),
    @LastName nvarchar(50),
    @AuditFields_InsertDate datetime,
    @AuditFields_UpdateDate datetime
)     
AS
    INSERT INTO UserAccounts (FirstName, LastName, AuditFields_InsertDate, 
                              AuditFields_UpdateDate)
    VALUES (@FirstName, @LastName, @AuditFields_InsertDate, 
            @AuditFields_UpdateDate)
 
    SELECT CAST(SCOPE_IDENTITY() AS INTAS Id

4.    The UserAccounts_Update procedure updates a record in the UserAccounts table.

CREATE PROCEDURE [dbo].[UserAccounts_Update]
(
    @Id int,
    @FirstName nvarchar(50),
    @LastName nvarchar(50),   
    @AuditFields_UpdateDate datetime
)     
AS
    SET NOCOUNT ON
 
    UPDATE UserAccounts
       SET FirstName = @FirstName,
           LastName = @LastName,
           AuditFields_UpdateDate = @AuditFields_UpdateDate
     WHERE Id = @Id
 
    RETURN

5.    The UserAccounts_Delete procedure deletes a record in the UserAccounts table given a specific Id.

CREATE PROCEDURE [dbo].[UserAccounts_Delete]
(
    @Id int 
)
      
AS
    SET NOCOUNT ON
 
    DELETE 
      FROM UserAccounts
     WHERE Id = @Id
 
    RETURN
Step 2: Create the Data Access Layer

The next step is to create a new Class Library project using Visual Studio 2010.  This project will be the data access layer.

1.    Launch Visual Studio 2010.

2.    Select FileàNew Project from the menu.

3.    Select Visual C# Class Library project and name the project OrderSystemDAL. 

4.    Make sure the "Create directory for solution" checkbox is checked and change the Solution Name to OrderSystem.

5.    Click the OK button.

6.    Visual Studio will create the solution and project for you.  Now you can add an Entity Data Model to the project.  Right click on the OrderSystemDAL project and select AddàNew Item… from the pop-up menu.

7.    The Add New Item dialog should appear.  Select ADO.NET Entity Data Model from the list of templates and change the name of the file to OrderSystemDataModel.edmx.  Click the Add button.

8.    The Entity Data Model Wizard will appear.  Select "Generate from database" and click the Next button.

9.    The next screen asks you for the database.  Click the New Connection button.  Enter the name of your server and then select the OrderSystem database from the list of databases and click the OK button.

10. Click the Next button.

11. The next screen asks you to choose your database objects.  You want to select the UserAccounts table and the five stored procedures create in step 1.  You can see the tables or stored procedures by click on the plus(+) sign next to the Tables or Store Procedures nodes.  Simply check the box next to the object to indicate that you want to import the object. 

12. Click the Finish button.  Visual Studio will now create the OrderSystem data model for you.

You still need to create the functions to execute the stored procedures.  To do this you simply right click on the stored procedure under the Stored Procedures folder and select Add Function Import from the pop-up menu.

13. Click the plus sign next to the Stored Procedures folder in the Model Browser and right click on the UserAccounts_Delete stored procedure. 

14. Select Add Function Import… from the pop-up menu. 

15. The Add Function Import dialog box should appear.  Leave the Function Import Name and Stored Procedure Name the same.  Since this stored procedure does not return any value select None from the "Returns a Collection Of" list.  Now click the OK button.  The method will now appear under the Function Imports node in the Model Browser.

16. Right click on the UserAccounts_Insert stored procedure and select Add Function Import… from the pop-up menu.  Since this procedure will return the Id of the newly added record you need to change the "Returns a Collection Of" to Scalars and choose Int32 from the list.  Click the OK button.

17. Repeat the same steps for the other procedures.  For the SelectAll and SelectById procedures you should change the "Returns a Collection Of" to Entities and select UserAccounts from the list.  The Update procedure does not return a value either so you can select None for its return type.

You now have all you need in the Entity Data Model.  The next step is to create a class in the data layer to manage the calls to the procedures associated with the UserAccount table.

18. In the Solution Explorer, right click on the OrderSystemDAL project and select AddàClass… from the pop-up menu.

19. Name the class UserAccountsData.cs and click the Add button.

20. Change the class declaration to make it public and static.

public static class UserAccountsData

21. Add the follow methods to the class.

public static int Insert(string firstName, string lastName, DateTime insertDate)
{
    using (OrderSystemEntities db = new OrderSystemEntities())
    {
        return Insert(db, firstName, lastName, insertDate);
    }
}
 
public static int Insert(OrderSystemEntities db, string firstName, 
                         string lastName, DateTime insertDate)
{
    return db.UserAccounts_Insert(firstName, lastName, insertDate, insertDate).ElementAt(0).Value;
}
 
public static void Update(int id, string firstName, string lastName, 
                          DateTime updateDate)
{
    using (OrderSystemEntities db = new OrderSystemEntities())
    {
        Update(db, id, firstName, lastName, updateDate);
    }
}
 
public static void Update(OrderSystemEntities db, int id, string firstName, 
                          string lastName, DateTime updateDate)
{
    db.UserAccounts_Update(id, firstName, lastName, updateDate);
}
 
public static void Delete(int id)
{
    using (OrderSystemEntities db = new OrderSystemEntities())
    {
        Delete(db, id);
    }
}
 
public static void Delete(OrderSystemEntities db, int id)
{
    db.UserAccounts_Delete(id);
}
 
public static UserAccount SelectById(int id)
{
    using (OrderSystemEntities db = new OrderSystemEntities())
    {
        return SelectById(db, id);
    }
}
 
public static UserAccount SelectById(OrderSystemEntities db, int id)
{
    return db.UserAccounts_SelectById(id).ElementAtOrDefault(0);
}
 
public static List<UserAccount> SelectAll()
{
    using (OrderSystemEntities db = new OrderSystemEntities())
    {
        return SelectAll(db);
    }
}
 
public static List<UserAccount> SelectAll(OrderSystemEntities db)
{
    return db.UserAccounts_SelectAll().ToList();
}

This is the class you should use to manipulate the data in the UserAccounts table.

Step 3: Create the Business Logic Layer

The Business Logic Layer (BLL) sits between the User Interface and the Data Access Layer and implements the business logic of the application.  This is where validation rules should be implemented. 

1.    Right click on the OrderSystem solution in the Solution Explorer and select Addà New Project… from the pop-up menu.

2.    Select Visual C# Class Library and name the project OrderSystemBLL and click the OK button.

3.    Right click on the References in the BLL project and select Add Reference.  The Add Reference dialog box should appear.

4.    Click on the Projects tab and select OrderSystemDAL from the list of projects.  Click the OK button.  You can now use the classes declared in the DAL project

5.    You also need to add a reference to the System.Data.Entity namespace.

6.    Right click on the Class1.cs file in the Solution Explorer and select Rename from the pop-up menu.  Rename the file UserAccountEO.  The EO is a suffix which stands for Edit Object.  This distinguishes this class from the UserAccount entity that was declared in the DAL.  Visual Studio will prompt you to rename all reference to this class, you should click the Yes button.

7.    Add the following using statements.

using System.Collections;
using OrderSystemDAL;

8.    Add the following property declarations to the class.

public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime InsertDate { get; set; }
public DateTime UpdateDate { get; set; }
 
public string FullName
{
    get
    {
        return LastName + ", " + FirstName;
    }
}

The FullName property is a read only property that is useful when displaying this object to the user.

9.    Add the follow methods to implement the logic for saving a record to the database.  The Save method will either add or update a record depending of the Id of the object.  A zero Id indicates that the record should be added.  The data in this object is validated before it tries to save it to the database.  If there are any validation errors the record will not be saved and the specific validation error message will be passed back to the caller.

public bool Save(ref ArrayList validationErrors)
{
    ValidateSave(ref validationErrors);
 
    if (validationErrors.Count == 0)
    {
        if (Id == 0)
        {
            Id = UserAccountsData.Insert(FirstName, LastName, DateTime.Now);
        }
        else
        {
            UserAccountsData.Update(Id, FirstName, LastName, DateTime.Now);
        }
        return true;
    }
    else
    {
        return false;
    }
}
 
private void ValidateSave(ref ArrayList validationErrors)
{
    if (FirstName.Trim() == "")
    {
        validationErrors.Add("The First Name is required.");
    }
 
    if (LastName.Trim() == "")
    {
        validationErrors.Add("The Last Name is required.");
    }
}

10. Now you can add the methods for deleting a record.  If you needed to do any referential integrity checks or other validation before deleting you would add the logic in the ValidateDelete method.

public void Delete(ref ArrayList validationErrors)
{
    ValidateDelete(ref validationErrors);
 
    if (validationErrors.Count == 0)
    {
        UserAccountsData.Delete(Id);
    }
}
 
private void ValidateDelete(ref ArrayList validationErrors)
{
    //Check for referential integrity.
}

11. The next methods to add will allow a caller of this object to load a specific record.

public bool Select(int id)
{
    UserAccount userAccount = UserAccountsData.SelectById(id);
 
    if (userAccount != null)
    {
        MapData(userAccount);
        return true;
    }
    else
    {
        return false;
    }
}
 
internal void MapData(UserAccount userAccount)
{
    Id = userAccount.Id;
    FirstName = userAccount.FirstName;
    LastName = userAccount.LastName;
    InsertDate = userAccount.AuditFields_InsertDate;
    UpdateDate = userAccount.AuditFields_UpdateDate;
}

12. The last method to add will create a list of UserAccountEO objects.  This is used when you want to select all the records from the UserAccounts table.

public static List<UserAccountEO> SelectAll()
{
    List<UserAccountEO> userAccounts =  new List<UserAccountEO>();
 
    List<UserAccount> userAccountDTOs = UserAccountsData.SelectAll();
 
    foreach (UserAccount userAccountDTO in userAccountDTOs)
    {
        UserAccountEO userAccountEO = new UserAccountEO();
        userAccountEO.MapData(userAccountDTO);
        userAccounts.Add(userAccountEO);
    }
 
    return userAccounts;
}
Step 4: Create the Web Form

The next step is to add the Web Application to the solution and build the page that will allow a user to add, update, delete, and select records from the UserAccounts table.

1.    Right click on the OrderSystem solution and select AddàNew Project from the pop-up menu.

2.    Select ASP.NET Empty Web Application from the list of project templates.  Change the name to OrderSystemUI and click the OK button.

3.    You'll need to add a reference to the OrderSystemBLL project before proceeding.  Right click the References and select Add Reference from the pop-up menu and click the Projects tab from the dialog.  Select the OrderSystemBLL project and then click the Add button.

4.    Next you have to add the connection string to your web.config file.

<connectionStrings>
    <add name="OrderSystemEntities" connectionString=
"metadata=res://*/OrderSystemDataModel.csdl|res://*/OrderSystemDataModel.ssdl|
res://*/OrderSystemDataModel.msl;provider=System.Data.SqlClient;
provider connection string=&quot;Data Source=YOURSERVER;
Initial Catalog=OrderSystem;Integrated 
security=True;MultipleActiveResultSets=True&quot;" 
providerName="System.Data.EntityClient" />
  </connectionStrings>

Be sure to change the Data Source to your server when adding this to your web.config file.

5.    Now you're ready to add the web form.  Right click on the OrderSystemUI project in the Solution Explorer and select AddàNew Item… from the pop-up menu.

6.    Select the Web Form template and change the name to Users.aspx.  Click the Add button.

7.    The HTML view of the web form should appear in Visual Studio.  Add the following code between the div tags.

<table>
  <tr>
      <td colspan="2">
        <asp:Label runat="server" ID="lblErrorLabel" 
             Text="Please correct the following issues:" ForeColor="Red" 
             Visible="false"></asp:Label>
        <asp:Label runat="server" ID="lblErrorMessages" ForeColor="Red"   
             Visible="false"></asp:Label>
    </td>
  </tr>
  <tr>
    <td>Select A User:</td>
    <td><asp:DropDownList runat=server ID="ddlUsers" AutoPostBack="True">
        </asp:DropDownList> </td>
  </tr>        
  <tr>
    <td>First Name:</td>
    <td><asp:TextBox runat="server" ID="txtFirstName"></asp:TextBox></td>
  </tr>
  <tr>
    <td>Last Name:</td>
    <td><asp:TextBox runat="server" ID="txtLastName"></asp:TextBox></td>
  </tr>
  <tr>
    <td>Inserted:</td>
    <td><asp:Label runat="server" ID="lblInserted"></asp:Label> </td>
  </tr>
  <tr>
    <td>Updated:</td>
    <td><asp:Label runat="server" ID="lblUpdated"></asp:Label> </td>
  </tr>
</table>    
<asp:Button runat=server ID="btnSave" Text="Save" />
<asp:Button ID="btnDelete" runat="server" Text="Delete" />

This code uses an HTML table to format the controls on the web form.  If you switch to Design view the form should look like the following image.

Step 5: Selecting Records to Load a Drop Down List

The first task we'll do is to load the drop down list in the page load event with the list of records in the UserAccounts table.  We'll also add an extra entry in the list to allow the user to select the option of creating a new user.

1.    Double click on the web form in Design view to create the Page_Load event in the code behind.

2.    Add the following using statement.

using OrderSystemBLL;

3.    Add the following code to the Page_Load event.

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        LoadUserDropDownList();
    }
}

4.    The LoadUserDropDownList is a custom method that you must create.

private void LoadUserDropDownList()
{
  ddlUsers.DataSource = UserAccountEO.SelectAll();
 
  ddlUsers.DataTextField = "FullName";
  ddlUsers.DataValueField = "Id";
  ddlUsers.DataBind();
 
  ddlUsers.Items.Insert(0, new ListItem("Create New User"""));
}

The drop down list's DataSource source property is set to the List of UserAccountEO objects that are returned from the UserAccountEO.SelectAll method.  The DataTextField is then set to "FullName".  The DataValueField is then set to "Id".  The next line binds the data to the drop down list.  The last line adds a new item to the list in the first position.  The text of the item is "Create New User" and this will be used to determine if the user is adding or updating an existing user.

Set the OrderSystemUI project as the startup project and set this page as the startup page and run the solution.  There are no records in the table yet so all you'll see is the "Create New User" entry in the drop down list. 

Step 6: Adding and Updating Records

The next step will be to add the code to allow the user to create or update records in the table.

1.    Switch to Design view and double click on the Save button to create the button click event handler.

2.    Add the following code to the click event handler.

protected void btnSave_Click(object sender, EventArgs e)
{
    ArrayList validationErrors = new ArrayList();
    UserAccountEO userAccount = new UserAccountEO();
 
    if (ddlUsers.SelectedItem.Value != "")
    {
        //Updating
        userAccount.Id = Convert.ToInt32(ddlUsers.SelectedValue);
    }
 
    userAccount.FirstName = txtFirstName.Text;
    userAccount.LastName = txtLastName.Text;
 
    userAccount.Save(ref validationErrors);
 
    if (validationErrors.Count > 0)
    {
        ShowValidationMessages(validationErrors);
    }
    else
    {
        //Reload the drop down list
        LoadUserDropDownList();
   
        //Select the one the user just saved.
        ddlUsers.Items.FindByValue(userAccount.Id.ToString()).Selected = true;
 
        lblErrorLabel.Visible = false;
        lblErrorMessages.Visible = false;
    }
}

 

private void ShowValidationMessages(ArrayList validationErrors)
{
    if (validationErrors.Count > 0)
    {
        lblErrorLabel.Visible = true;
        lblErrorMessages.Visible = true;
        lblErrorMessages.Text = "<br />";
 
        foreach (string message in validationErrors)
        {
            lblErrorMessages.Text += message + "<br />";
        }
    }
    else
    {
        lblErrorLabel.Visible = false;
        lblErrorMessages.Visible = false;
    }
}

This code creates an ArrayList to hold any validation errors that are discovered in the Business Layer.  It then creates an instance of the UserAccountEO object and sets its properties based on what the user has entered on the web page.  After the Save method is called the Count of the validationErrors is checked.  If there are errors then they are displayed to the user by calling the ShowValidationMessages method.

Step 7: Retrieving a Single Record

We still have to add the code so that when a user selects an item in the drop down list the system will retrieve the record and display the information on the web page.  This will be done in the SelectedIndexChanged event.

1.    Switch back to Design view and double click on the Users drop down list.  This should create the SelectedIndexChanged event handler.

2.    Add the following code

txtFirstName.Text = "";
txtLastName.Text = "";
lblInserted.Text = "";
lblUpdated.Text = "";
 
if (ddlUsers.SelectedValue != "")
{
    int userAccountId = Convert.ToInt32(ddlUsers.SelectedValue);
 
    UserAccountEO userAccount = new UserAccountEO();
    if (userAccount.Select(userAccountId))
    {
        txtFirstName.Text = userAccount.FirstName;
        txtLastName.Text = userAccount.LastName;
        lblInserted.Text = userAccount.InsertDate.ToString();
        lblUpdated.Text = userAccount.UpdateDate.ToString();
    }
}

This code calls the Select method of the UserAccountEO object to grab the record and load the properties in the business object.  The textboxes and labels are set to the properties of the object that was returned.

If you run the project now you should be able to pull up the records that were added previously and then update them.

Step 8: Deleting Records

The last step is to add the code to the Delete button's click event to delete a record.

1.    Switch back to Design view and double click on the Delete button to generate the click event handler.

2.    Add the following code.

if (ddlUsers.SelectedItem.Value != "")
{
    ArrayList validationErrors = new ArrayList();
    UserAccountEO userAccount = new UserAccountEO();
 
    userAccount.Id = Convert.ToInt32(ddlUsers.SelectedValue);
    userAccount.Delete(ref validationErrors);
 
    if (validationErrors.Count > 0)
    {
        ShowValidationMessages(validationErrors);
    }
    else
    {
        LoadUserDropDownList();
        txtFirstName.Text = "";
        txtLastName.Text = "";
        lblInserted.Text = "";
        lblUpdated.Text = "";
        lblErrorLabel.Visible = false;
        lblErrorMessages.Visible = false;
    }
}
 

This code creates an instance of a UserAccountEO object and sets its Id property to the value selected in the drop down list.  It then calls the Delete method.  Once the record is deleted the drop down list is reloaded so the user is removed and the textboxes and labels are cleared.

Summary

This article demonstrated a three layered pattern for using the Entity Framework  4 and Visual Studio 2010.  This isn't a terribly complicated architecture and can meet the need for many department level applications or line of business applications. A nice advantage of using the Entity Framework in this pattern is that you don't have to code the DTO objects yourself, the EF handles that for you and you also get the Intellisense when calling stored procedures.

Good luck on your project and happy coding.


Product Spotlight
Product Spotlight 

©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-20 4:37:27 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search