Configure the Key when using Views as the basis for Entities

When using a View as the basis for an Entity in the Entity Framework (rather than a Table), make sure that the Key fields/properties are configured properly in the Model (EDMX).

We recently experienced an anomaly where a Select All from a View-based Entity was not returning All the Entities we expected. There weren’t any filters, sorts, or joins, it was a straight select using Linq to Entities.
Apparently, when we added the View to the Model, EF picked some columns (per its own magical algorithm) and made those the Key fields/Properties. When it then loads the Entities from the recordset, any record with the same Key replaces an Entity that already has that Key. So several of the records were, because of the EF generated Key, being replaced this way.
We corrected the Key on the Entity (in both the Conceptual model and the Storage model), and the Select All returned all the right Entities, as expected.
In our case, the View only returns 5 columns, so it was simple enough to make all the fields part of the Key. For a larger query, if no compound key is apparent, I expect we would need to add a RowId to the View so that the EF can use that as a surrogate Key. We do not actually use the View Key in the app (we only use the values in those rows to populate a list box that we then parse into several fields on selection), so we can use a surrogate Key without being concerned about the actual values that are actually being used.

Purpose and Principles of the Data Layer

At its core, the Data Layer’s main purpose is:

Data Layer’s Main Purpose:

To abstract all interactions with the database so that business objects can be written to deal with business rules, not with database interaction.

For example, when promoting a standard Deal, the business logic comprises:

  1. validate that the Deal is in a promotable state
  2. promote the Deal
  3. generate the related Deal Transactions
  4. generate the related Confirmation

In addition to these items, there are interactions with the Deal Pricing, Deal Costing, Deal Transaction Pricing and a variety of other lookup tables. Records need to be saved, retrieved, updated and deleted to facilitate the persistence of the Deal’s new “Promoted” state. The Data Layer will encapsulate the code that interacts with the database, so the Deal Promoter class only needs to be concerned with the business rules of promoting a deal, and not with the mechanics of persisting those changes to the database.

ORM Principles

In order to effectively serve as an Object-Relational Mapping (ORM) tool, the Data Layer needs to implement the following principles.

Principle 1: The Data Layer should generate the code necessary to deal with different tables and should provide a common API for working with the resulting data objects.

A typical database interaction involves the following steps:

  1. Get the connection string
  2. Open the connection
  3. Create the command object
  4. Execute the command
  5. Close the connection

The only thing that changes from table to table is the names of the table and its columns. All of the database code is identical.

Principle 2: The Data Layer should be able to work with the entire Object Graph by saving and retrieving related objects as a set.

Some objects are more complex than others, for example, a Deal has Pricing, Charges and Transactions that are an integral part of it. It also has Confirmations that are related to it. When saving a Deal, the pieces and parts of the Deal should get saved too.

Principle 3: The Data Layer should handle failures within the context of a transaction and roll back the changes to a consistent, stable state.

Sometimes, when saving a complex object, an error may occur in one of the pieces. In this case, the Data Layer should gracefully handle the error and leave the object and the database in a stable, consistent state.

Principle 4: The Data Layer should intelligently map database tables to appropriate Business Objects.

In several cases, a business object will represent a concept differently than the database might. For example, the database table EMPLOYEE contains all the records for the Employees, Managers and Direct Reports business classes.

Principle 5: The Data Layer should handle concurrency properly.

When objects are saved to the database, concurrency problems arise because the data being saved is about to overwrite data that has already changed since it was last retrieved. Concurrency resolutions include: Overwrite, Merge and Discard. The Data Layer needs to support these options and allow developers to choose which resolution to employ.

Query Support

Principle 6: The Data Layer should support LINQ.

In order to provide data sources for drop downs and grids the Data Layer needs to be able to support querying, including sorting, grouping and summarizing. There are three choices to do this:

1) Oracle native SQL queries

Implementing this technique often pushes Business and UI logic all the way back into the database. It makes ORM a challenge as the query objects are not like table-based as they are not updateable and do not usually have the necessary key fields.

2) Custom querying support in the Data Layer

Implementing this technique is complex, non-standard and may have performance issues.

3) LINQ (The .Net framework’s built in query language)

Implementing LINQ provides powerful, sophisticated, query capabilities. LINQ to Entities also raises performance by taking advantage of the Entity Framework’s knowledge of the database objects.

Principle 7: The Data Layer should support asynchronous communication.

One of the worst aspects of application performance is the perceived lag while waiting for data to be retrieved from a database, transferred over the network and rendered in the UI. Asynchronous communication is the recommended way to prevent this lag by allowing the UI to be responsive while the data is retrieved, transferred, and even rendered, asynchronously. In Silverlight, all network communication is asynchronous, so the Data Layer must support asynchronous communication.

Oracle Support

Principle 8: The Data Layer should support Oracle specific features, such as Sequences, Packages and Oracle Data Types.

Most Oracle tables have a key field that is a numeric tied to a Sequence value. Much of the legacy code is embedded in the Database in Oracle Packages. There are also some Oracle specific Data Types (particularly LOB’s) that need to be translated to/from their .Net equivalents. The Data Layer needs to handle all three of these situations properly.

Trouble Shooting Support

Principle 9: The Data Layer should support granular logging for debugging and troubleshooting.

When debugging and troubleshooting, a detailed log of what is happening can be a very useful tool. Especially in asynchronous or Inversion of Control situations where the code cannot be easily stepped through, a log is critical to the discovery and elimination of bugs.

Performance Enhancement

Principle 10: The Data Layer should support server-side and client-side caching to improve performance.

Data Caching on the Server-side allows redundant calls for data from different clients to be served in a single database request. Data Caching on the Client-side allows redundant calls for data on the client to be served on the client without any network traffic at all.

Principle 11: The Data Layer should support validation at the client and at the server to improve performance.

Eliminating round trips by providing client-side validation will improve performance. Providing server-side validation will ensure data integrity at the server.

IdeaBlade DevForce – Model Setup Walk-through – Step 2: The DevForce Projects

Our Problem:


We need to create an Enterprise application that is web-based, has an Excel-ish interface and pulls data from an Oracle database.

Technologies Chosen*:

  • Silverlight for the UI
  • Prism 2 for the modular framework
  • Unity for DI
  • IdeaBlade DevForce for the Business Object Layer and Async functionality
  • MS Entity Framework for the ORM
  • DevArt dotConnect for Oracle (EF Drivers)
  • Oracle 11 Database

* See here for our reasoning.

Prerequisites:

  1. Complete the procedures documented in Step 1
  2. Install IdeaBlade’s DevForce for Silverlight tool.
  3. Install the Silverlight development environment. This comprises:
    1. The Silverlight 3 Developer Runtime
    2. The Silverlight 3 Tools for VS 2008
    3. The Silverlight 3 SDK

Creating the DevForce model projects:


There will be two DevForce model projects, one will be a C# library project that runs on the server, the other will be a Silverlight library that runs in the Silverlight client. The DevForce framework takes care of wiring these two together with a couple of WCF services that are hosted in the Web application that hosts the DevForce components and the Silverlight app.

To create the projects:

  1. Open the Entity Framework model solution created in Step 1 of this walkthrough.
  2. Select Tools | DevForce Object Mapper
  3. DevForce naturally works with EDMX files (Model | Add Entity Model), but we have an EDML file from the devArt Entity Developer, so we have to use File | Open to get to it.

    Select File | Open, change the Files of type to All Files. Select the Model_Widget.edml file and click Open.
    At this point, DevForce has opened the EF model. Now, we have to tell it how we want the projects to be configured.

  4. First, click the New Project button for the Domain Model project.
    The Project Name needs to follow our assembly naming convention, so for this walkthrough, I am using Acme.BirdTrap.Model.Widget (with no suffix).
    I will leave the Project Type and Location at their defaults.
  5. Second, click the New Project button for the Silverlight project.
    The Project Name needs to follow our assembly naming convention, so for this walkthrough, I am using Acme.BirdTrap.Model.WidgetSL (with the SL = Silverlight suffix).
    I will leave the Project Type and Location at their defaults.
  6. Back on the new model tab:
    1. Set the Namespace to: Acme.BirdTrap.Model.Widget
    2. Set the EntityManager name to something more model specific, we use a standard of ModuleEntityManager as the application may need to work with multiple modules’ EntityManagers at the same time.
      I have selected to name the Entity Manager: WidgetEntityManager.

    New Devforce Project Settings

    [Note: See Ward Bell’s essay on the topic of Large Models for more about splitting your model and using multiple Entity Managers.]

  7. Select the Model_WidgetEF.edml branch of the Model tree on the left. You will see the settings available for this EF Model.
    1. Change the DataSourceKeyName to something more model specific, we use a standard of ModuleDataSource as each module/model needs a different DataSource definition.
      I have selected to name the Data Source Key: WidgetDataSource
    2. This is also the place where you inject your own base Entity class implementations into the DevForce model. At the very least, you should create a BaseEntity class where you can put common Entity functionality. Click the Injected Base Types button and add Acme.BirdTrap.Utility.Persistence.BaseEntity to the list, setting it as the default. (In a future posting, I will be examining the Injected Base Types in greater depth.)
    3. The name pluralizer is a great feature of DevForce but it is completely irrelevant to us as devArt’s Entity Developer tool has taken care of pluralization for us.

  8. Click the Save button on the toolbar. DevForce will now create and initialize the two projects we have defined. When it has finished, close the DevForce Object Mapper.
  9. Next, we need to create the BaseEntity class we added to the Injected Base Types earlier.
    1. In the new C# project, Acme.birdTrap.Model.Widget, add a new class named Acme.BirdTrap.Utility.Persistence.BaseEntity that inherits from IdeaBlade.EntityModel.Entity
      using IdeaBlade.EntityModel;
      
      namespace Acme.BirdTrap.Utility.Persistence
      {
          public class BaseEntity : Entity
          {   
          }
      }
    2. In the new Silverlight project, Acme.birdTrap.Model.WidgetSL, add an Existing Item, then navigate to the BaseEntity class you just created in the C# project, drop down the Add button and choose Add as Link.
  10. I bet you wish we were done at this point. We are not. DevForce made some assumptions in generating the projects that we need to fix.

    First, and really only a cosmetic issue, DevForce created a Solution Folder for the DomainModel. You can choose to keep this or not. This whole solution is for my DomainModel, so I do not need this new folder. If I incorporate the Model projects into a larger Module level Solution, then I will use a Solution Folder for the Model projects in that solution, but I do not need one here. To remove the folder, I drag the two new projects up out of the folder into the Solution level and then Remove the folder.

  11. Second, and by far more serious, DevForce pulled in a bunch of referenced components, some of theirs, some of the .Net Framework’s and some from the Silverlight SDK.
    There are three places where referenced assemblies can safely live:

    1. \WINDOWS\Microsoft.NET\Framework\**.*
    2. \Program Files\Reference Assemblies\**.*
    3. Your solution’s dependencies\**.*

    1. Examining the C# project (Acme.BirdTrap.Model.Widget), we find the IdeaBlade assemblies are referenced from Program Files. We can change that by: first, removing the Ideablade.Core, Ideablade.EntityModel, Ideablade.Validation,assemblies; then adding them back from the dependencies\IdeaBlade DevForce\ folder.
    2. Examining the Silverlight project (Acme.BirdTrap.Model.WidgetSL), we find the IdeaBlade Silverlight assemblies are referenced wrong, which can be corrected as above, only adding back from the dependencies\IdeaBlade DevForce\SilverlightAssemblies folder this time.
      However, we also find that the System.ComponentModel.DataAnnotations assembly is being pulled from \Program Files\Microsoft SDKs\ folder. Remove it and add it back from dependencies\Silverlight\Libraries\Client\.
  12. That’s it, right? We’ve created the files and all is well. If you try to Build your solution right now, you will get errors originating from the EF project. But it was fine before we started, and we didn’t change it — or did we?
    In fact, we did. When you point the DevForce Object Mapper at the EF project’s EDML file, DevForce adds a bunch of attributes to some of the elements in that file. In doing so, the DevArt Entity Developer notices the EDML has changed and wipes the generated *.designer.cs file. To restore this, you need to re-open the EDML file and regenerate the generated code. But here’s the rub, if you open the EDML file from within Visual Studio, the devArt Entity Developer will only regenerate the code if you make changes to the model. But we don’t want to make changes to the model.
    Instead, open the EDML file from Windows Explorer. This will open the Entity Developer and reveal a Generate Code button on the toolbar:
    Generate Code button
    Clicking this button, will regenerate the missing generated code without requiring a model change. You can then exit the Entity Developer.
  13. DONE.

Conclusion


In this part of the walk through, we have created the pair of DevForce C# and Silverlight projects that contain our Domain Model.
The solution successfully Builds and the EF unit test we created in Step 1 still passes. Now we need to create a pair of Unit Test projects to ensure that the DevForce framework is configured properly and can successfully CRUD the database through the EF layer. But that will have to wait until the next part of the walk through.

Until then: I’m off to take the dog for a walk! Woof!

IdeaBlade DevForce – Model Setup Walk-through – Step 1: The Entity Framework Project

Our Problem:


We need to create an Enterprise application that is web-based, has an Excel-ish interface and pulls data from an Oracle database.

Technologies Chosen*:

  • Silverlight for the UI
  • Prism 2 for the modular framework
  • Unity for DI
  • IdeaBlade DevForce for the Business Object Layer and Async functionality
  • MS Entity Framework for the ORM
  • DevArt dotConnect for Oracle (EF Drivers)
  • Oracle 11 Database

* See here for our reasoning.

Prerequisites:


Install the DevArt dotConnect for Oracle drivers. (This includes the DevArt Entity Developer tool).

Database Structure:


For this walkthrough, I have created the following table:

CREATE TABLE SAMPLE_WIDGET 
(
    SAMPLE_WIDGET_ID int,
    DESCRIPTION varchar(100),
    CONSTRAINT SAMPLE_WIDGET_PK 
    PRIMARY KEY (SAMPLE_WIDGET_ID)
);
/
CREATE SEQUENCE SAMPLE_WIDGET_ID_SEQ;
/
CREATE TRIGGER SAMPLE_WIDGET_GETSEQ 
BEFORE INSERT
ON SAMPLE_WIDGET
FOR EACH ROW WHEN (NEW.SAMPLE_WIDGET_ID IS NULL)
BEGIN
    SELECT SAMPLE_WIDGET_ID_SEQ.nextval INTO :NEW.SAMPLE_WIDGET_ID FROM dual;
END;
/
INSERT INTO SAMPLE_WIDGET (DESCRIPTION) VALUES ('Widget 1');
INSERT INTO SAMPLE_WIDGET (DESCRIPTION) VALUES ('Widget 2');
INSERT INTO SAMPLE_WIDGET (DESCRIPTION) VALUES ('Widget 3');
INSERT INTO SAMPLE_WIDGET (DESCRIPTION) VALUES ('Widget 4');
COMMIT;
/

Creating the EF Project

  1. In Visual Studio 2008, select File | New Project | C# Class Library.

    Naming the Project
    There will be 3 model projects:

    • The EF project
    • The server-side DevForce project
    • The client-side (Silverlight) DevForce project

    We have opted to follow a [Company].[Application].[Component].[Module][Suffix] project naming convention.
    For example: Acme.BirdTrap.Model.WidgetSL would denote a Silverlight Model project for the Widget module of the BirdTrap application at Acme company.

    For this walkthrough, I will name the EF project:

    Acme.BirdTrap.Model.WidgetEF.csproj

    And, I will name the Model solution:

    Acme.BirdTrap.Model.sln

    Project Creation Dialog

  2. Delete the Class1.cs
  3. Add the references so we can use Oracle and the EF.

    But, wait. Before we add the references, we need to decide where the referenced items will live. Every solution should carry with it all the components (DLL’s) that are not part of the official Microsoft environment and that are needed to build it. These should be checked in to SCM with that solution. If not, then developers will be pointing projects to their Program Files directory and/or other random places when they add references to projects.
    To address this problem, we create two solution level folders, one for dependencies and one for installations.

    Project Folders

    The installation folder.
    Contains the Full Install package for each of the Third Party tools we use. These are checked in and promoted along with the code. This ensures that everyone is building with the same set of tools. If someone updates their PC and our solution to use a new version of a tool, the promoted code will be accompanied by a new Install package so all developers can update the version of the tool their using before trying to compile the updated code.
    The dependencies folder.
    Contains all the DLL files that are referenced by the projects in the Solution, except the ones that are part of the Microsoft .Net Framework. In so far as technologies like Silverlight are installed to the GAC as extensions to the .Net Framework, those DLL’s are referenced from the GAC and are not copied to the dependencies folder. All other referenced DLL’s ARE copied to dependencies though. This includes the Silverlight Toolkit files, the IdeaBlade files, DevArt files, etc.

    So, before we can create the references in our WidgetEF project, we need to create our installations and dependencies folders and copy the DLL files from the installed locations in Program files to the appropriate locations in the dependencies folders.

    Now the files are in the right place, we can add the references to the WidgetEF project.

    1. From the Add Reference dialog, on the .NET tab, select the System.Data.Entity 3.5 and System.Runtime.Serialization 3.0 components.
    2. On the Browse tab, navigate to the dependencies\DevArt dotConnect folder, select the DevArt.Data.Oracle.Entity.dll.
  4. Create the Entity Model. In the WidgetEF project, select to Add a New Item. Select the Data category, and choose the DevArt Entity Model item. When naming the model file, we have been naming them Model_EF.
    For this walkthrough, I will name the model file: Model_WidgetEF.edml.
    At this point, DevArt’s Entity Developer will open.

    1. Select the Project |Properties menu item. Select the Model tab.
    2. Change the EntityContainerName to Entities. The one that the tool provides is based on the model file name, which is not useful to us within the application. Naming ALL of your model Containers as Entities means that all of your modules can use Namespace.Entities.ObjectClass to access the object classes.
    3. Change the Namespace to the correct one. Again, the default is based on the model file name, so it will never be correct. I used: Acme.BirdTrap.Model.WidgetEF.
    4. Check the “View Generation” checkbox.
    5. Use the Database Explorer pane to connect to your database.
    6. Drag the SAMPLE_WIDGET table over to the Model pane.
    7. When we created the SAMPLE_WIDGET table, we used a SEQUENCE and a TRIGGER to auto-increment the primary key Id field. This makes our table behave the same way as an Identity column in SQL Server, which EF knows how to deal with. We need to tell our model, that the SampleWidgetId field is an autonumber field. DevArt has made this easy.
      In the Project Explorer, there are two main branches.
      The top branch (Acme.BirdTrap.Model.WidgetEF), is the Conceptual (or Object) model.
      The lower branch (Acme.BirdTrap.Model.WidgetEF.Store) is the Storage (or Database) model.
      (The Mapping model is not represented in this tree because it is handled in the Mapping editor for each Class/Property in the Conceptual model.)

      1. Open up the lower branch (the .Store branch).
      2. Expand the Tables/Views branch.
      3. Expand the SAMPLE_WIDGET table branch.
      4. Select the SAMPLE_WIDGET_ID column.
      5. Now, in the Properties pane, you will see the properties of the SAMPLE_WIDGET_ID column.
      6. Select the Store Generated Pattern property and change the value to Identity.
    8. Save the Model.
    9. Close the Entity Developer.

  5. Back in VS, expand the file tree below the new .edml file so you see the following files:
    Model_WidgetEF.edml
    The XML definition of the model itself. This corresponds to the .edmx file created by the MS Entity Designer. You will usually edit this file using the DevArt Entity Developer tool, but you will occasionally need to edit the XML directly in a text editor. There is plenty of technical documentation on MSDN about this file, but none of it is particularly instructive.
    Model_WidgetEF.cs
    A one-time generated file with a Partial declaration of the Entities class for you change as needed. (We change it in one of the steps below.)
    Model_WidgetEF.Designer.cs
    The generated code for your entities. Do not change this file. Period.
    Model_WidgetEF.edml.view
    The diagram layout, written in XML. This file is only relevant to the Entity Developer tool.(And I love that DevArt put this in a separate file instead of tacking it on the bottom of the EDMX file like MS did.)
    Model_WidgetEF.edps

    This XML file defines the tool settings, output path and connection string used by the Entity Developer and the code generator. You will not need to change this file until you need to build against a different database. These changes will be explained further in a later posting on building and deploying the model solution.
    Model_WidgetEF.PregeneratedViews.cs
    The pregenerated views used by the Entity Framework. If these views are pregenerated, they can be compiled into the assembly and they will not need to be generated dynamically at application startup. In my projects, this seems to have saved us anywhere from 1-10 seconds off the total spin up time.
  6. In order to fix the ORA-01790 weirdness, open the Model_WidgetEF.cs file and add this snippet:
    partial void OnContextCreated()
    {
    	DevArt.Data.Oracle.Entity.OracleEntityProviderServices.TypedNulls = true;
    }
  7. Last, but not least, in order to deploy this project to run against a different database, we will need to be able to regenerate the PregeneratedViews without running the UI tool. I.e. Regenerate from the command line. The easiest way to do that will be using a T4 template.
    There is a good article here about using a T4 template to create the views for a MS EDMX model. I started by using their CSharp.Views.tt file.

    The change necessary to make this work for our DevArt EDML model is:

    string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".views", "") + ".edmx";

    Should be:

    string edmxFileName = Path.GetFileNameWithoutExtension(this.Host.TemplateFile).ToLowerInvariant().Replace(".pregeneratedviews", "") + ".edml";
    
  8. A weird thing happened when I added the .tt file to my project. Even though I clicked cancel, it still ran the .tt template anyway, and it created a Model_WidgetEF.PregeneratedViews1.cs file that I don’t want. To fix this, and get the .tt nested properly under the .edml file, we need to manipulate the .csproj file by hand. Close the project in VS. Open the .csproj file in the text editor of your choice. Locate this code:
    <Compile Include="Model_WidgetEF.PregeneratedViews.cs">
      <DependentUpon>Model_WidgetEF.edml</DependentUpon>
    </Compile>
    <Compile Include="Model_WidgetEF.PregeneratedViews1.cs">
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
      <DependentUpon>Model_WidgetEF.PregeneratedViews.tt</DependentUpon>
    </Compile>

    Move the contents of the second tag into the first and remove the second, so the code looks like this:

    <Compile Include="Model_WidgetEF.PregeneratedViews.cs">
      <DependentUpon>Model_WidgetEF.edml</DependentUpon>
      <AutoGen>True</AutoGen>
      <DesignTime>True</DesignTime>
    </Compile>

    Now find this code:

    <None Include="Model_WidgetEF.PregeneratedViews.tt">
      <Generator>TextTemplatingFileGenerator</Generator>
      <LastGenOutput>Model_WidgetEF.PregeneratedViews1.cs</LastGenOutput>
    </None>

    Remove the 1 from the PregeneratedViews1.cs file name and add a element, so the code looks like this:

    <None Include="Model_WidgetEF.PregeneratedViews.tt">
      <Generator>TextTemplatingFileGenerator</Generator>
      <LastGenOutput>Model_WidgetEF.PregeneratedViews.cs</LastGenOutput>
      <DependentUpon>Model_WidgetEF.edml</DependentUpon>
    </None>

    Note: The T4 engine and the Entity Developer tool will each take ownership of the .PregeneratedViews.cs file. So depending which tool ran last, the views may be nested under the .edml file, or under the .tt file.

  9. DONE. The EF model project is complete. Before going any further, let’s set up a simple unit test to make sure we have connectivity and can read data.
  10. Creating the EF Unit Test Project

  11. Add a new Test Project to the solution. We name each test project with the same name as the assembly it tests, suffixed with .Test, so in this case, it is: Acme.BirdTrap.Model.WidgetEF.Test.
  12. Add references to:
    * The model project: Acme.BirdTrap.Model.WidgetEF
    * System.Data
    * System.Data.Entity
    (You do not need to add Linq or Xml or any DevArt components.)
  13. Copy or link the app.config from the Model EF project to the Test project.
  14. Create a test class that performs the basic CRUD operations:
    using System;
    using System.Data;
    using System.Linq;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    namespace Acme.BirdTrap.Model.WidgetEF.Test
    {
        /// <summary>
        /// Summary description for UnitTest1
        /// </summary>
        [TestClass]
        public class ModelWidgetEfTest
        {
    
            private readonly string _testWidget = "UT" + DateTime.Now;
    
            [TestMethod]
            public void LoadWidgetsTest()
            {
                //instantiate the model
                var entities = new Entities();
    
                //get the widgets
                var widgets = from item 
                              in entities.SampleWidgets 
                              select item;
                
                //make sure we got some widgets
                Assert.IsNotNull(widgets, "LoadWidgetsTest: returned Null");
                Assert.IsTrue(widgets.Count() > 0, "LoadWidgetsTest:  Returned no widgets.");
            }
    
            [TestMethod]
            public void CreateAWidgetTest()
            {
                //instantiate the model
                var entities = new Entities();
    
                //create a sample widget
                var widget = new SampleWidget { Description = _testWidget };
                //cache the id of the new widget
                var id = widget.SampleWidgetId;
                
                //add the widget to the Entities collection
                entities.AddToSampleWidgets(widget);  
                
                //save the new widget
                var result = entities.SaveChanges();
    
                Assert.IsTrue(result > 0, "CreateAWidgetTest: Save new widget failed.");
                Assert.AreNotEqual(0, widget.SampleWidgetId, "CreateAWidgetTest: Saved Id is still zero.");
                Assert.AreNotEqual(id, widget.SampleWidgetId, "CreateAWidgetTest: Save Id did not get updated by the trigger.");
            }
    
            [TestMethod]
            public void EditAWidgetTest()
            {
                //instantiate the model
                var entities = new Entities();
                
                //get the sample widget
                var widget = (from item
                              in entities.SampleWidgets
                              where item.Description == _testWidget
                              select item).First();
                
                //make sure we got the widget
                Assert.IsNotNull(widget, "EditAWidgetTest: returned Null");
                
                //change the widget
                widget.Description = "Changed Widget";
    
                //save the changed widget
                var result = entities.SaveChanges();
    
                Assert.IsTrue(result > 0, "EditAWidgetTest: Save changed widget failed.");
                Assert.AreEqual("Changed Widget", widget.Description, "EditAWidgetTest: Saved description changed unexpectedly");
            }
    
            [TestMethod]
            public void DeleteAWidgetTest()
            {
                //instantiate the model
                var entities = new Entities();
    
                //get the sample widget
                var widget = (from item
                              in entities.SampleWidgets
                              where item.Description == "Changed Widget"
                              select item).First();
    
                //make sure we got the widget
                Assert.IsNotNull(widget, "DeleteAWidgetTest: returned Null");
    
                //delete the widget
                entities.DeleteObject(widget);
    
                //save the deletion, so the widget record is removed
                var result = entities.SaveChanges();
    
                Assert.IsTrue(result > 0, "DeleteAWidgetTest: Save deleted widget failed.");
                Assert.AreEqual(EntityState.Detached, widget.EntityState, "widget was not detached when it was saved");
            }
    
            [TestMethod]
            public void TryToLoadTheDeletedWidgetTest()
            {
                //instantiate the model
                var entities = new Entities();
    
                var widgets = from item
                              in entities.SampleWidgets
                              where item.Description == "Changed Widget"
                              select item;
    
                Assert.IsTrue(widgets.Count() == 0, "TryToLoadTheDeletedWidgetTest:  Returned a widget when it shouldn't.");
            }
        }
    }
  15. Create an OrderedTest class that runs the CRUD operations in the right order: Load, Create, Edit, Delete, then TryToLoadAfterDelete.
  16. To run the OrderedTest, open the Test View pane (menu Test | Windows | Test View). Select the Ordered Test and run it. It should be green lights all the way.

Conclusion

In this part of the walk through, we have created the Entity Framework model project for an Oracle database using the DevArt dotConnect drivers and its accompanying Entity Developer tool.

In the next part of the walk through, we will set up the IdeaBlade DevForce models that sit on top of the EF model.

Until then: Get outside and enjoy the sunshine. Which is to say, “Take the dog for a walk!” Woof!

Follow

Get every new post delivered to your Inbox.