PRISM V4 Drop 2 : MVPVM Modularity with MEF

Sample Application: ModularityWithMef.zip (4.95 mb)

Although I have been a hard-core Unity (Dependency Injection) cronie since its conception I have to admit I was very pleased with MEF extensions to PRISM.  However, it did force me to rethink some design strategies that I've used since the Smart Client Software Factory - such as using generics to quickly wire-up base classes (you can't use generics with attributes).

Somewhat reluctant at first I scraped my earlier prototype/design and moved forward with a new design that is centered around MEF and PRISM V4 Drop 2.  To meet core objectives for my http://EHR.CodePlex.com project I'll have to build an infrastructure that will minimize refactoring when PRISM V4 is released; particularly since PRISM, MEF and Unity will serve as the main tools.

The sample that is provided in the PRISM V4 Drop 2 looks as follows with the exception of the View on the right.  I added a new new "Hello World" view utilizing the MVPVM pattern; I use PRISM/MEF to wire-up the infrastructure.  

Note: Module F Imports two Business Logic Layers (DoctorBLL/PatientBLL) which in turn Import their respective Data Access Layers.  It then uses these, within its presenter, to populate the view model properties that the two ComboxBoxes are bound to. 

The end result after refactoring the sample is as follows:

  • The Model instantiates the Presenter 
  • The Presenter instantiates the View and wires up the view model as applicable

After wire-up the Presenter handles all business logic (you'll find no code in the code-behind files).   Below is all of the code required to wire-up the module A view with A)dd, E)dit, D)elete and P)rint buttons.   Modules A and E both use the same CRUDControl and each have their own CRUD View Models (derived from CRUDViewModelBase) which permit base functionality to be overridden, i.e., notice that Module E does not have a P)rint button.   The only wire-up required for the CRUDControl is shown on line 23 below where the DoctoCRUDViewModel.ParentViewModel is set with a reference.

The only issue I ran into, which will result in a message in the user forum is I couldn't figure out how to [Import] parts in the Bootstrapper - so currently there is some code in the MainWindow code-behind but as soon as I work this out I'll move what little code remains there into the Presenter.

Sample Application: ModularityWithMef.zip (4.95 mb)


Tags:
Categories: MEF | MVPVM | Prototype


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

PRISM 2.2: can't use StaticResource when resource is in App.xaml

I lost count of the number of variations of "can't use StaticResource" that I BING'd for over the last couple of days; countless hours banging my head against the desk.

This adventure started when I created a prototype from the DirectoryLookupModule sample from the Prism 2.2 package (ref this prototype link).  As you can see from the link it went through a major transformation and it wasn't until the MVPVM infrastructure was in place (with Business and Data access layers consuming a simple service) that I started to implement a DataTemplate.

The problem was I couldn't access my DataTemplate from the merge dictionary entry for App.xaml UNLESS I made it a DynamicResource.  Where this worked just fine the pit-bull side of me had to know why....

It wasn't until the second day that I noticed that "all" of the resources in this sample application were using DynamicResource, it was this revelation that started the road to discovery.   At the end of the road was the following obsure little declaration in the constructor.   The Prism bootstrapper was being loaded during the constructor versus OnStartup!  This little oversite prevents the StaticResource from being usable in the Shell or any of its Modules.

The happy ending on this story was that while updating the Prism forum with this tidbit (it was where I started my journey to discover the cause of this odd-ball bug) I noticed that Prism 4 had a Drop 2 so  naturally  I downloaded it and at first was somewhat dismayed that Unity was no where in the picture but was shortly taken aback by MEFs angle at the problem; it was very cool to say the least and I am now in the process of moving my MVPVM prototype to Prism 4.

 


Tags: , , ,
Categories: MEF | MVPVM


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

MVPVM: Sharing a CRUD control with multiple views

The object is to reuse CRUD logic, such as enabling/disabling buttons, setting IsEditing flags that XAML can bind to, etc, without polluting our view models.   For this we have a CRUDViewModelBase class that we derive from and set flags based on our needs - in the example below (right image) we set IsPrintVisible to false effectively hiding the Print button for the Patient (Module D in this prototype application DirectoryLookupModularity.zip (5.82 mb) ).

Note that the DoctorViewModel and PatientViewModel have overrides for each of the button clicks as well as a reference to it's parent view model and shared criteria view model.   The CRUD logic is completely decoupled and can be encapsulated within each view model for each concern; we have a clear separation of concerns (SOC).

Once we assign the PatientCRUDViewModel to Module D's View we can see below that the [P] button is not visible.   The default behavior is to have all buttons visible as shown for Module A

Since the Modules job (ModuleA.cs in image below) to load the Presenter, and the Views (DefaultViewA.xaml) have no code in their code-behinds, it is the Presenter's (DoctorPresenter.cs) responsibility to execute business logic as required.  Below we can see where the doctorCrudVm.ParentViewModel is provided a reference to its parent view. 

A single presenter / view can handle multiple CRUD requirements easily using the CRUDControl.

Note: Where a View can only have one view model, a view model can be shared across numerous views (reused).  In the case of our sample application the Shell and Modules B and C all share the CriteriaViewModel which is defined in the bootstapper as a singleton (the MVPVM framework represented in the sample link above easily supports this). 

It is the ability to reuse this domain object, across multiple views and modules, that dictates our project structure.  The PrototypeApp.Interface contains all the classes used by this application (ideally I would have the Business and Data layers in their own projects so they can be reused across multiple solutions but this is just a sample prototype).

A key point not to miss here is that you don't want to populate your view model domain objects with business logic that does not pertain to managing its view.

DirectoryLookupModularity.zip (4.91 mb)


Tags: , , ,
Categories: MVPVM | Prototype


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

MVPVM: Integration testing using the Presenter

The MVPVM pattern lends itself to effective integration/unit testing.   Since their is no code in the code-behind and the Presenter processes all business logic against the ViewModel and View it is the best component to perform integration test against. 

In the following application (download from this link DirectoryLookupModularity.zip (5.82 mb)) there are three view models:

  1. CriteriaViewModel - utilized by views E, B and C
  2. DoctorViewModel - utilized by view A
  3. PatientViewModel - utilized by view D

The MVPVM framework used for the attached application consistently uses the following for bootstrapping modules:

The ModuleBase<TPresenter> (see below image) instantiates the specified Presenter, e.g., on line 14 below we identify the "DoctorPresenter" as the presenter.

The PresenterBase<TView, TViewModel> (see below image) specifies the View and it's ViewModel which in this case is DefaultViewA and DoctorViewModel respectively.  On line 23 we register the view (DefaultViewA) with the TopLeft region which was declared in the Shell's XAML.  The remaining logic is the code required to wire-up a CRUD control and to get the current module load counter value so that we can see that the module dependency attributes are working correctly.

These baseclasses do all of the wiring up so that we can focus on business rules; we don't spend a lot of time getting our module up and ready for the requirements at hand.

The integration test for Module A's presenter is shown below (lines 46-52); it verifies that the infrastructure properly wired up the View and ViewModel. 

Did you notice that on line 24 above we set the container to null and on the following line use the container.RegisterServices() method?   On the surface this may look odd but it lends itself to testing the Prism environment.

We use the same RegisterServices in our bootstrapper as we do in our integration/unit test - we do this to ensure our environments match for testing purposes.  To accomplish this we use a IUnityContainer Extension class (IUnityContainerExtension), the only time container will be null is during testing so we instantiate a MockBootstrapper (lines 25-27 below).   From that point on we have the same registrations to ensure our test will run without complaint.


Tags: , , ,
Categories: MVPVM | Prototype


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

WPF assembly: XmlnsDefinition - identify types for XAML usage

If a picture says a thousand words then the following should save me some typing as to the value of XmlnsDefinition (reference line 5 below) - it provides an easy to remember Url mapping to your namespaces. 

My PrototypeApp (born from the Prism DirectoryLookupModularity sample) has an Prototype.Interface project where much of the application specific code resides.   Within the AssemblyInfo.cs file (under the Properties folder) I was able to map all of the applicable namespaces to a single, easy to remember, url.

Excerpt from AssemblyInfo.cs follows: 

[assemblyAssemblyVersion("1.0.0.0")]
[assemblyAssemblyFileVersion("1.0.0.0")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp,
                            "PrototypeApp.Interface.Constants")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp
                           "PrototypeApp.Interface.ViewModels")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp,
                            "PrototypeApp.Interface.Controls")]
[assemblyXmlnsDefinition(http://www.Global-Webnet.com/PrototypeApp
                           "PrototypeApp.Interface.Converters")]

Demo source code follows: DirectoryLookupModularity.zip (5.82 mb)


Tags: , ,
Categories: Prototype


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Unit Testing ASP.NET/Client Application Service / Programmatically Starting Web Development Server (WebDev.WebServer.exe)

What is seemingly a very simple process turned into quite the adventure - a costly one in terms of following dead-ends and beating my head against the wall - I first started with the programmatically started service (failing miserably) and evolved into the attribute driven approach which I finally got to work but it had constraints.....

The two processes (programmatic and attribute driven) have functional examples in my http://EHR.CodePlex.com project.   I will detail how to make both methods work, highlighting areas left out of available blogs and research material that will result in errors that yield no answers on the internet.  

The adventure started with an annoying problem with my EHR unit test - I have a number of test that test the Client Application Service (for security) and if I don't manually start up the ApplicationService web service they will fail - this morning I rolled up my sleeves and decided to automate this process so at any time I can run all test in solution and have the service automatically start for the test that depend on it.

Below shows the syntax for the StartWebApplication() TestContext extension method that does the job of starting up the service during test initialize programmatically. 

Before going into more detail the following source demonstrates how to use the attribute method for starting the ApplicationService:

THE KEY to using attributes is to first ensure you have the namespace available - you'll find many resources that will correctly inform you that you must use the following:

using Microsoft.VisualStudio.TestTools.UnitTesting.Web;

However, what is not easily found is the DLL that this unit test resides in!!!   After a long search and destroy mission (online failed) I started searching the Microsoft.VisualStudio DLLs and found it in the Microsoft.VisualStudio.QualityTools.WebTestFramework 

A second piece of information that can really eat your lunch if it escapes you is that you MUST have a DEFAULT.ASPX file in the service!!!  Since I was using a Client Application Service (which doesn't even have a service, never mind a default.aspx file) I ran into the errors you'll see below that affected both the programmatic method as well as the attribute method.   Ensure you have a Default.aspx file.

CONSTRAINTS

I could be missing something but I could not get a breakpoint to work on the unit test that had this attribute - this was unacceptable but at least I got the web development server to start automajically (for the first time) so I was going to make it work.   I decided to have an initialize (empty) service that would start the service so that it was available for the rest of the unit test.

I then ran into a problem - you cannot control the order of your test (outside of an ordered test).  I want to be able to "run all test in solution" and have it work (which it currently does).   I saw blogs that tell you that you can use the Priority attribute but it doesn't work - later this was confirmed on the MSDN site that holds the information for this attribute; it clearly states that this is not used by the test engine and is only for developer use.

My work-around; I found that the same test always is the first test to run so I put the attribute on it :)  Ugly work-around but it works.   You'll find it on my CanGetCloudContainer() unit test which is always the first test to run in the solution.   Later I was able to get the programmatic approach to work consistently (by adding a default.aspx file) so I really don't need this solution but keep it in there as a code reference (for this blog).   I did code the TestContextExtension so that if a WebDev.Server is available it will not attempt to programmatically load it.   This supports both "all unit test in solution" (attribute) and the programmatic approach (if running only that test fixture during development).

HEAD-BANGERS

ERROR:  The ASP.NET Web application at 'C:\_\_EHR\Layers\Service\GWN.EHR.ApplicationService' is already configured for testing by another test run. Only one test run at a time can run tests in ASP.NET. If there are no other test runs using this Web application, ensure that the Web.config file does not contain an httpModule named HostAdapter.

This error occurs if you are using the attribute method and you crashed out of the unit test.   Behind the scenes when the test starts the Web.Config is modified; the HostAdapter is added to it.  When the test are all completed it is removed.  If you crash out of the test and this is not removed then you will see the above error.   To watch this at work simply load your Web.Config of your service and run the test - you will be notified when the Web.Config file is changed (first to add and the second to remove the HostAdapter statement).

ERROR:  The Web request 'http://localhost:2035/AppService' completed successfully without running the test. This can occur when configuring the Web application for testing fails (an ASP.NET server error occurs when processing the request), or when no ASP.NET page is executed (the URL may point to an HTML page, a Web service, or a directory listing). Running tests in ASP.NET requires the URL to resolve to an ASP.NET page and for the page to execute properly up to the Load event. The response from the request is stored in the file 'WebRequestResponse_CanGetCloudBlobConta.html' with the test results; typically this file can be opened with a Web browser to view its contents.

The above error occurs if the the web application/service does not have a Default.aspx file.   If you debug the test you'll see the ASP.NET Web Development Server load, the test will fail and then the Web Development server will close.

With the Default.aspx file missing you can see the "in progress" test below fails for a "Configuration Error".   At other times I could get the programmatic approach to work without the default.aspx however I will ensure I have one to ensure I don't stumble upon the following error again:

 

The error for CanGetCloudBlobContainer (attribute load of Web Development Server) above had the following "The web site could not be configured correctly" error also: 

Source code for TestContext extension that loads the WebDev.WebServer.exe follows:

using System.Diagnostics;

using System.Linq;

using Microsoft.VisualStudio.TestTools.UnitTesting;

 

namespace GWN.Library.Tests.Extensions

{

    public static class TestContextExtension

    {

        private static Process _currentProcess;

 

        public const string WebDevWebServerExe =
            @"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.exe";

 

        public static string StartWebApplication(this TestContext testContext,

            string appName, string port, string fullAppPath)

        {

            if (_currentProcess != null)

                return null;

 

            // If other processes, i.e., attribute process, loaded webserver

            // then don't run this process (it is already available).  We only

            // run one service at this point so we'll worry about multiple ones

            // when the need arises..

            if (Process.GetProcesses()

                .Any(process => process.ProcessName.Contains("WebDev.WebServer")))

                    return null;

 

            var fullWebPath = string

                .Format("/port:{0} /path:\"{1}{2}\" /vpath:\"/{3}\"",

                       port,

                       testContext.GetRootPath(),

                       fullAppPath,

                       appName);

 

            var startinfo = new ProcessStartInfo(WebDevWebServerExe, fullWebPath)

                {

                    WindowStyle = ProcessWindowStyle.Hidden,

                };

 

            _currentProcess = Process.Start(startinfo);

 

            return fullWebPath;

        }

 

        public static string GetRootPath(this TestContext testContext)

        {

            var offset = testContext.TestDir.IndexOf("TestResults");

            var path = testContext.TestDir.Substring(0, offset);

            return path;

        }

    }

}

ATTRIBUTE APPROACH (full source since image is cut off)

/// <summary>

/// Determines whether this instance [can get BLOB container].

/// </summary>

[TestMethod]

[HostType("ASP.NET")]

[AspNetDevelopmentServerHost(
   @"C:\_\_EHR\Layers\Service\GWN.EHR.ApplicationService", "/AppService")]

[UrlToTest("http://localhost:2035/AppService")]

public void CanGetCloudBlobContainer()

{

    // Get list

    var blobList = client.ReadContainerList();

 

    // Get first container

    var blobContainer = blobList.FirstOrDefault();

 

    Assert.IsNotNull(blobContainer);

 

    // Use first container (name) to get container from list

    var cloudBlobContainer = client.ReadContainer(blobContainer.Name);

 

    // Assert the names match

    Assert.AreEqual(blobContainer.Name, cloudBlobContainer.Name);

}


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Goodbye Windows Mobile - HELLO Silverlight for Windows Phone 7 !!!!

When I downloaded and installed the Silverlight For Windows Phone 7 w/Visual Studio express application I was happy to see Mobile development was available in my VS2010 application also.  I was also glad to see Silverlight 4.0 as I was unable to play with it once I removed VS2010 Beta 2 and installed VS2010 CTP. 

Below you'll find a Silverlight 4.0 application, hosted in a ASP.NET MVC2 website, as well as the new Silverlight for Windows 7 emulator.  

The cool thing about Silverlight for Windows Phone 7 is that it is based on XAML!!   This was a pleasant surprise.  

I've been waiting patiently for Windows Mobile to emerge for VS2010 so that I could start my new framework (http://ehr.codeplex.com/) in which WPF, Silverlight and Mobile can all share the same codebase (multi-targeting).   I didn't really have XAML in mind for code sharing but it made for a fun exercise for learning - the following XAML is the exact code used for both GWN.EHR.Mobile and GWN.EHR.Silverlight MainPage views (with the exception of font sizes); the code-behind files also share identical code. 

<gwnLibrary:GWNPage

    x:Class="GWN.EHR.MainPage"

    xmlns:gwnLibrary="clr-namespace:GWN.Libary;assembly=GWN.Libary"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800" >

 

  <Grid x:Name="LayoutRoot" Background="Black">

    <Grid.RowDefinitions>

      <RowDefinition Height="Auto"/>

      <RowDefinition Height="*"/>

    </Grid.RowDefinitions>

 

    <!--TitleGrid is the name of the application and page title-->

    <Grid x:Name="TitleGrid" Grid.Row="0">

      <StackPanel>

        <TextBlock Text="MY APPLICATION" x:Name="textBlockPageTitle"

                  Foreground="White"/>

        <TextBlock Text="page title" x:Name="textBlockListTitle"

                  Foreground="White" FontSize="20"/>

      </StackPanel>

    </Grid>

 

    <!--ContentGrid is empty. Place new content here-->

    <Grid x:Name="ContentGrid" Grid.Row="1">

    </Grid>

  </Grid>

</gwnLibrary:GWNPage>

I was disappointed to find that, unlike Silverlight 4.0, Silverlight for Windows Phone 7 does not support MEF.   My initial attempts to migrate code over failed.  Research indicated that Prism support for Silverlight 4.0 is not slotted to start until April (CTP in Sept) so it looks like I'm going to be in limbo for a while...

The problem is I have aggressive goals of having modules completed for my http://EHR.CodePlex.com application by Jan 2011.  Coming out of the gate I decided to adapt the ContainerModel (Mobile DI container) for Silverlight 4.0 and Mobile; my plans are to make it look and feel like Unity to minimize refactoring when it is supported.  If MEF was supported it would have been my primary foundation however it's attribute system won't be easily emulated.  Likewise I'll use Prism concepts for XAML/Dependency properties to minimize refactoring for it when it is released.

My immediate goals are to get my GWN.EHR.POC application (bottom left in above image) into my new GWN.Library.xxxx libraries using my Unity like ContainerModel - to have WPF, Silverlight and Mobile all running from the same codebase using linked files.

 


Tags:
Categories: EHR | Silverlight | Unity


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Upgrading VS2010 Beta 2 UML diagrams to VS2010 Release Candidate

Living on the bleeding edge means we have to bleed occasionaly so I wasn't totally surprised when my modeling project wouldn't load under the VS2010 Release candidate - but I was concerned about the hemorrhage this was causing as I not only had days of work invested in UML diagrams for my personal project (http://ehr.codeplex.com/) but also weeks of work invested for my current contract.  

So when a co-worker told me about the Microsoft Visual Studio 2010 Architecture Model Upgrade Tool I was ecstatic!   I was relieved when upon upgrade completion I found that all of my diagrams opened up - I was now able to continue forward instead of losing countless time rebuilding old diagrams from print-outs.

Note: I did have some inconvenience during the first upgrade and used lessons learned to streamline the second.   Some tips follow:

1.  I didn't keep the default install path.   I selected the root folder of my documentation (Modeling is the Beta 2 and ModelingRC was the new diagrams moving forward).

2.  Since I was under source control on both projects I was sure to reset the readonly flag on all files in the Beta 2 Modeling folder (the second time).   Since I couldn't "check out" all files from the solution because I couldn't load the project to do so - my first attempt failed to upgrade because the files were readonly and I ended up going back and forth until I got through all of the failed files.   

3.  By installing to the root folder I was able to use the /upgrade /recursive command line which simplifies the switches required to run the upgrade utility.  

 
Note: I had to insert a space between /upgrade and /recursive 


Tags: , ,
Categories: Visual Studio 2010


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

VS2010 RC - Azure "One of the request inputs is out of range" error

Things were starting to move rather smoothly, my base classes seemed to be pretty solid and all that remained was to add CRUD capabilities to CloudBlockBlob objects (Tables and Containers are done). 

I setup my unit test to create a new container for my Blob and I was greeted with a foreboding "One of the request inputs is out of range" with the inner exception reporting "The remote server returned an error: (400) Bad Request"; another head-banging session was about to begin....

Line 209 below is where the fun started, my container ("Images") didn't exist and the baseclasses are smart enough to create the container if it isn't available.   What had me chasing my tail for a while was "what input was wrong".   The short version - Azure only accepts lower case!!

Once I figured this out I updated by baseclasses to ensure that key points would convert to lowercase so that I would no longer be greeted by this obscure message in the future - I can use whatever I like and it will ensure nothing reaches the backend in anything other than lowercase.

 

  189 /// <summary>

  190 /// Determines whether this instance [can create cread update delete

          BLOB from file name].

  191 /// </summary>

  192 [TestMethod]

  193 [DeploymentItem("Images\\Jesus.jpg")]

  194 [DeploymentItem("Images\\AngelRays.jpg")]

  195 public void CanCreateCreadUpdateDeleteBlobFromFileName()

  196 {

  197     string imageName = "Jesus.jpg";

  198     string imageName2 = "AngelRays.jpg";

  199 

  200     string container = "Images";

  201 

  202     Assert.IsTrue(File.Exists(imageName));

  203 

  204     string fullName = new FileInfo(imageName).FullName;

  205     string fullName2 = new FileInfo(imageName2).FullName;

  206 

  207     //::::[ CREATE ]:::::

  208 

  209     string uniqueName = client.CreateBlob(container, fullName);

  210     Assert.IsNotNull(uniqueName);

  211 

  212 

  213     // ::::[ READ ]::::::

  214 

  215     using (MemoryStream stream = client.ReadBlobStream(container, uniqueName))

  216     {

  217         Assert.IsNotNull(stream);

  218         Bitmap image = new Bitmap(stream);

  219 

  220         // Save image as file

  221         image.Save("_"+imageName, ImageFormat.Jpeg);

  222         Assert.IsTrue(File.Exists("_"+imageName));

  223     }

  224 

  225     // ::::[ UPDATE ]:::::

  226 

  227     client.UpdateBlob(container, uniqueName, fullName2);

  228     using (MemoryStream stream = client.ReadBlobStream(container, uniqueName))

  229     {

  230         Assert.IsNotNull(stream);

  231         Bitmap image = new Bitmap(stream);

  232 

  233         // Save image as file

  234         image.Save("-" + imageName, ImageFormat.Jpeg);

  235         Assert.IsTrue(File.Exists("-" + imageName));

  236     }

  237     byte[] bytes1 = File.ReadAllBytes("_" + imageName);

  238     byte[] bytes2 = File.ReadAllBytes("-" + imageName);

  239     Assert.AreNotEqual(bytes1.Length, bytes2.Length);

  240 

  241 

  242     //:::[ DELETE ]:::::

  243     bool deleted = client.DeleteBlob(container, uniqueName);

  244     Assert.IsTrue(deleted);

  245 

  246     var deletedFileIsNull = client.ReadBlob(container, uniqueName);

  247     Assert.IsNull(deletedFileIsNull);

  248 }

Source code available at http://EHR.CodePlex.com  (GWN.Contrib.Azure project) 


Tags: , ,
Categories: Azure | Visual Studio 2010 | EHR


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

VS2010 RC - Unit Testing Azure Create, Read, Update and Delete (CRUD)

I did some head-banging this morning while writing a unit test for Azure table storage CRUD operations.   My WebRole application uses the PersonDataSource (line 8) as an ObjectDataSource and works without issue so I was perplexed as to why my unit test kept crashing on line 40.   The UpdateItem(testdata) was successfully executing but the value was not being updated to "TestOrganization" as expected.

    1 /// <summary>

    2 /// Determines whether this instance can create read update delete

    3 /// azure table records.

    4 /// </summary>

    5 [TestMethod]

    6 public void CanCreateReadUpdateDeleteAzureTableRecords()

    7 {

    8     PersonDataSource mockData = new PersonDataSource();

    9     EHRPerson testData = null;

   10 

   11     string uniqueId = Guid.NewGuid().ToString();

   12 

   13     //:::::[ CREATE ]:::::::

   14     bool isSuccessful = mockData.CreateItem(new EHRPerson

   15     {

   16         ID = uniqueId,

   17         Name = "EHRTest",

   18         Organization = "EHROrganization",

   19         PersonType = "EHRPersonType",

   20         ProfessionalTraining = "EHRProfessionalTraining"

   21     });

   22     Assert.IsTrue(isSuccessful);

   23 

   24     //:::::[ READ ]:::::::

   25     testData = new List<EHRPerson>(mockData.Read())

   26         .FirstOrDefault<EHRPerson>(p => p.ID.Equals(uniqueId));

   27 

   28     Assert.IsNotNull(testData);

   29     Assert.AreEqual("EHRTest", testData.Name);

   30     Assert.AreEqual("EHROrganization", testData.Organization);

   31     Assert.AreEqual("EHRPersonType", testData.PersonType);

   32     Assert.AreEqual("EHRProfessionalTraining", testData.ProfessionalTraining);

   33 

   34     //:::::[ UPDATE ]:::::::

   35     testData.Organization = "TestOrganization";

   36 

   37     mockData.UpdateItem(testData);

   38     testData = new List<EHRPerson>(mockData.Read())

   39         .FirstOrDefault<EHRPerson>(p => p.ID.Equals(uniqueId));

   40     Assert.AreEqual("TestOrganization", testData.Organization);

   41 

   42     //:::::[ DELETE ]:::::::

   43     mockData.DeleteItem(testData);

   44 

   45     testData = new List<EHRPerson>(mockData.Read())

   46         .FirstOrDefault<EHRPerson>(p => p.ID.Equals(uniqueId));

   47     Assert.IsNull(testData);

   48 

   49 }

What I found was that my item (TItem) parameter was being reset to default items on line 330 (image below) effectively overwriting my changes with original values.    However, if I ran my Cloud application it successfully updated the data (PersonDataSource as ObjectDataSource).   

I discovered that I was being returned the reference of the item that was read previously (line 25 above) which meant I was passing around a reference so line 330 below effectively replaced it with the original contents thus entry = item.  This wasn't a problem with the ObjectDataSource because I trust that ASP.NET creates a new instance and populates it with form values prior to sending it to the ObjectDataSource.

I resolved the issue by updating my TableServiceEntity base class to implement ICloneable so that I don't have to manage it at higher levels.    All of my table entities, i.e., EHRPerson will be POCO so this should work well.  

Source code available at http://ehr.codeplex.com/ (GWN.Contrib.Azure project) 

 


Tags: , ,
Categories: Azure | Visual Studio 2010 | EHR


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know