Print Page | Close Window

Attempting to mock data at *design* time

Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2010
Forum Discription: For .NET 4.0
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=2076
Printed Date: 13-May-2026 at 12:12am


Topic: Attempting to mock data at *design* time
Posted By: jsobell
Subject: Attempting to mock data at *design* time
Date Posted: 18-Aug-2010 at 9:53pm
I've just been experimenting to see if I can use saved CacheState data to provide mocking in Blend and VS2010, but have hit an error in the EntityManager constructor.
It fails in Blend when it tries to resolve IdeaBlade.EntityModel.DataSourceResolver.

Has anyone ever managed to get this to work? I've attached the data as a resource, and that is found fine within Blend, but is there any way to fool or force the internal resolver so we can use a 'snapshot' of our live data for Blend development?

Using DF to avoid creating dozens of mock repositories on a large project? Now that would be a coup!



Replies:
Posted By: smi-mark
Date Posted: 19-Aug-2010 at 9:20am
Are you using SL? I found this problem with SL but I believe they were/are addressing it. It's supposed to work under WPF.


Posted By: WardBell
Date Posted: 20-Aug-2010 at 1:21am

We are well on our way to making DevForce play well with Blend. Actually, we probably are THERE. But we haven't documented it well and I don't feel that I'm particularly close to anything worthy of calling a "Best Practice." Let's say I have some "ok" practices :-/

Here's visual evidence of an existence proof: yes, you truly can use a DevForce EntityManager and entities at design time:
 
ComboBox Sample in Blend
 
This example comes from my slight re-design of the "Simple ComboBox in Silverlight" sample code.
 
[Aside: the existing sample is on our web site now. The revised one will be released with our forthcoming "DevForce Cookbook." Look for the Cookbook to arrive within a matter of weeks.]
 
The original point of the sample was to demonstrate how to wire up a ComboBox to DevForce entities AND bind that ComboBox to other DevForce entities.
 
The previous version looked bloody awful; this one just looks bad.
 
However, it's beautiful for the purpose of this forum post!  You can plainly see employee, "Ima", in on the Blend art board. "Ima" does not exist in the Northwind tutorial database. She's a fake Employee, ginned up in a MainPageDesignViewModel that derives from the production MainPageViewModel. The view doesn't know; it's just binding to a ViewModel (VM).
 
All of the fakery happens in the design VM's constructor, shown here:
    public MainPageDesignViewModel() {

      EntityManager = new NorthwindManager(false);
      EntityManager.DefaultQueryStrategy = QueryStrategy.CacheOnly;
      var designEmp = new Employee {
        EmployeeID = 1, FirstName = "Ima", LastName = "Blast", ReportsToEmployeeID = 2,
      };
      var designManagerEmp = new Employee {
        EmployeeID = 2, FirstName = "Slim", LastName = "Ornun", ReportsToEmployeeID = null,
      };
      EntityManager.AttachEntity(designEmp);
      EntityManager.AttachEntity(designManagerEmp);
      PotentialManagers = new List<Employee> { designManagerEmp };
      CurrentEmployee = designEmp;
      Messages.Add("We're in DesignTime now !!!");
    }
The design version of the VM (a) creates two fake employess (Ima and her boss, Slim), (b) creates a disconnected, cache-only EntityManager (EM) - to ensure there is no attempt to talk to the database, and (c) adds the two employees to that EM. Because both fakes are in cache, DevForce can implement the navigation between Ima and Slim via the "Manager" property. I neglected to show this in the ComboBox but you can see it in the message which reads "Ima Blase whose manager is Slim Ornun".
 
It is only a few short hops from here to doing the cache-to-file trick that Jason wants. In fact, I've done what Jason wants on a different project and we'll provide a similar, smaller example in one of the Cookbook recipes .... although perhaps not in the first Cookbook edition. Trust me, it works great.
 
To be precise, it worked great for test runs of your application that you wanted to be fast and free of the need to talk to a database. It didn't work in Blend (although for different reasons than Jason cites).
 
Until DF 2010 v. 6.0.5, it simply wasn't viable to use DF entities in Cider (Visual Studio visual designer) or Blend because we required that the Desktop (aka Web aka "Full DotNet") model carry the same assembly name as the Silverlight model. No problem for the CLR but most tools, including design tools, just choked.
 
Using the same assembly name is no longer required. In fact, is heavily discouraged.
 
Now this ComboBox sample dates from 6.0.2 (I think) ... certainly before we got rid of the assembly name restriction. It did not Blend.
 
All I had to do was some assembly renaming plus clean and re-build. The view popped right up in Cider and Blend.
 
There was no data visualization though. Just a bunch of weird boxes. The XAML looked ok ... obviously it ran ok ... but it made no sense rendered in Cider or Blend.
 
A little dragging around was helpful but I still wanted to see sample data.
 
So I refactored the ViewModel to facilitate a derived, design-time version of that ViewModel and, presto, that's how I was able to get the layout you see in the image above.
 
[While I was at it, I replaced the button click wiring with Blend Behaviors ... and deleted the entire custom code-behind]
 
You can do the same things to your application, or wait for the Cookbook if you want to see the details.
 
As I mentioned, the cache-to-file variation is not far off. That will take some explanation, however, as there are a few places you can stumble on the way ... as Jason discovered.   Please hang in there.


Posted By: jsobell
Date Posted: 24-Aug-2010 at 12:42am
Hi Ward,
We've been using mocked entities in Blend without any problems, and it's really only the loading of cached data that we're missing. It's a pain creating mock objects, particularly when they involve tree structures :)
I'm looking forward to seeing what's in this cookbook though.


Posted By: WardBell
Date Posted: 24-Aug-2010 at 10:04am
Glad to hear it's working for you. 6.0.4 made a big difference in this regard.
 
Not sure how quickly I can get to the example you want so let me give you some hints.
 
You have to create the cache file from within a Silverlight Client. You'll be working with SILVERLIGHT types in your SL app so you have to populate your cache with the entities defined as the Silverlight types.
 
Here is an  extract from actually working, code-behind of a tiny SL app called KioskTestingDataMaker:
using System;
using System.Windows;
using System.Windows.Controls;
using System.IO;

namespace Kiosk.Testing.DataMaker {
  public partial class MainPage : UserControl {
    public MainPage() {
      InitializeComponent();
    }

    private void HandlePageLoaded(object sender, RoutedEventArgs e) {
      DataMaker = new TestDataMaker();
      DataMaker.ReadyToWrite += EnableSaveButton;
      DataMaker.Build();
    }

    private void EnableSaveButton(object sender, EventArgs args) {
      saveButton.IsEnabled = true;
      statusTB.Text = "Cache data is ready; click SAVE button to save to local file system.";
    }

    private void HandleSaveButtonClick(object sender, RoutedEventArgs e) {
      statusTB.Text = "Save to the file named "+ DataMaker.CacheFileName +
        " in the directory 'Kiosk.Model.Testing.SL'." ;
      OpenSaveFileDialog();
    }

    private bool OpenSaveFileDialog() {

      SaveFileDialog sfd = new SaveFileDialog() {
        DefaultExt = "dat",
        Filter = "Data files (*.dat)|*.dat|All files (*.*)|*.*",
        FilterIndex = 1
      };

      if (sfd.ShowDialog() == true) {
        statusTB.Text = "Saving cache data to " + sfd.SafeFileName +
          " ... please wait for next message ...";
 
        using (Stream stream = sfd.OpenFile()) {
          DataMaker.WriteCacheToStream(stream);
          stream.Close();
        }

        statusTB.Text = "Saved test data to " + sfd.SafeFileName +
          " ... You're done ... close the application.";
        return true;
      }
      statusTB.Text = "You cancelled ... cache data was not saved; you may close the app now." ;
      return false;

    }

    private TestDataMaker DataMaker { getset; }

  }
The XAML that goes with it has a Save button and a TextBlock to show the instructions. Dumb and ugly as it comes.
 
I leave as an exercise for you the implementation of the class called TestDataMaker. It's primary job is to query for the entities you want for your test into its EntityManager.
 
The TestDataMaker app asks the user to identify where the file should be saved. That is because this app is a browser-based and must ask for permission. Were it out-of-browser with elevated permissions, it might do this without asking; I wrote this for SL 3 before elevated trust existed.
 
The next challenge is to find and read the cache to load your test- or design-time EntityManager. I imagine there are many ways to do this. What I did was save the file to one of my Silverlight Testing projects. Everytime I run the DataMaker it replaces that file. Meanwhile, that file has a build action of 'Resource" in the test project. Resources are easy to read in Silverlight so my TestRepository has no trouble streaming in the Resource and loading the test EntityManager.
 
You see the "write" side of this equation in the code above where I ask the user to save the file to a file with the name give by the DataMaker.CacheFileName property (e.g., "TestDataCache.dat"), located in the local file system folder, "Kiosk.Model.Testing.SL".
 
Finally, you have to remember to rebuild the Kiosk.Model.Testing.SL test project so that it incorporates the revised resource file.
 
The whole process is more baroque and manual than I would like. Fortunately, I don't have to re-make the test data very often. I can live with it.
 
Btw, note that the resource file is checked in with the project ... so I've got my test data under source control :-/
 
HTH


Posted By: WardBell
Date Posted: 24-Aug-2010 at 10:10am
@jsobell I invite you to improve this approach and share with the rest of us!


Posted By: jsobell
Date Posted: 24-Aug-2010 at 6:14pm
Hi Ward,
The generation and inclusion of the mock data in my test follows the same approach as yours, and I also include the mock data as a resource. I don't have any issue with this approach.

The code for restoring the cache is where my experiment fails:
            var xx = new RiskStructureDB();
            xx.DefaultQueryStrategy = QueryStrategy.CacheOnly;
            Stream stream = Application.GetResourceStream(new Uri(@"RiskStructureEditor;component/MockData/mock.bin", UriKind.Relative)).Stream;
            xx.CacheStateManager.RestoreCacheState(stream, RestoreStrategy.Normal, true);


This fails in 6.0.5 with the following exception:

   at IdeaBlade.EntityModel.DataSourceResolver.FindKey(String keyName, String keyExtension)
   at IdeaBlade.EntityModel.DataSourceResolver.BuildRelatedInfo(String dataSourceKeyName, IIdGenerator idGenerator)
   at IdeaBlade.EntityModel.DataSourceResolver.set_DataSourceInfoMap(Dictionary`2 value)
   at ReadDataSourceResolverFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
   at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Int32 id, RuntimeTypeHandle declaredTypeHandle, String name, String ns)
   at ReadEntityCacheStateFromXml(XmlReaderDelegator , XmlObjectSerializerReadContext , XmlDictionaryString[] , XmlDictionaryString[] )
   at System.Runtime.Serialization.ClassDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, DataContract& dataContract)
   at System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
   at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions(XmlReaderDelegator reader, Boolean verifyObjectName)
   at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
   at IdeaBlade.EntityModel.SerializationFns.RestoreDCS(Type instanceType, Stream stream, Boolean closeOnExit, IEnumerable`1 knownTypes)
   at IdeaBlade.EntityModel.EntityCacheState.Restore(Stream stream, Boolean closeOnExit)
   at IdeaBlade.EntityModel.CacheStateManager.RestoreCacheState(Stream stream, RestoreStrategy strategy, Boolean closeOnExit)
   at RiskStructureEditor.MainPageModel.LoadCacheState() in C:\Dev\RiskStructure\Silverlight\RiskStructureEditor\RiskStructureEditor\MainPage.model.cs:line 350


This appears to be related to the old issues of the DataSourceResolver serialization etc.
Any suggestions?
Once I get the cache retrieval working I'll happily join you in finding an elegant application of this approach :)


Posted By: WardBell
Date Posted: 25-Aug-2010 at 9:11am
Hmmm .... we will look into this ASAP. The code snippet I gave you USED to work (6.0.3) but I haven't tried it recently. Maybe something happened. Will follow up. Didn't want you to feel lonely meanwhile.


Posted By: WardBell
Date Posted: 25-Aug-2010 at 11:17am

Perhaps you may take some comfort in this: I just ran the code and application from which I took that extract (above). Ran it on the nearly latests 6.0.5 bits ... after deleting old test data file and reconstructing ... and it all works ... on my machine :-)

That doesn't make it work on yours of course. At best it indicates that the process works SOMEWHERE on SOME VERSION of DevForce.
 
Please check again that you are constructing the cache file with a Silverlight application.
 
In early days, I tried to create the cache file with a Desktop client. I got the similar serialization failures. You have to build the cache file in an SL "DataMaker" app referencing the same Silverlight model  that you will consume in your SL production app.
 
You probably are doing exactly what I just said ... but I think it worth emphasizing because it is so easy to miss this distinction and sink into the morass (as I first did months ago).


Posted By: WardBell
Date Posted: 25-Aug-2010 at 11:33am
So ... if you still think you're following the recipe and it still isn't working for you ... even with fresh DF 6.0.5 bits ... maybe you can code a little Northwindexample of what you are doing? That's something we could sink our teeth into. Thx. 


Posted By: jsobell
Date Posted: 01-Sep-2010 at 12:28am
Sorry, been moving house over the weekend so haven't been coding for a few days...

Not quite sure about this comment:

Hmmm .... we will look into this ASAP. The code snippet I gave you USED to work (6.0.3) but I haven't tried it recently. Maybe something happened. Will follow up. Didn't want you to feel lonely meanwhile.

Which code snippet are you referring to? The snippet you posted only saves the mock data, and there's no problem there; it's recreating the cache in design mode that breaks with internal null reference errors in all the tests I've done.

I'll check out the proper 6.05 when it's online, and see if anything has been magically fixed.

Cheers,
 Jason


Posted By: WardBell
Date Posted: 01-Sep-2010 at 1:31am
Hi Jason - I did look into it ASAP. The kiosk app I demonstrated at DevConnections in April only works because I can save to cache and restore from cache.
 
Now that app did not work in design mode at the time because there were gremlins in VS and Blend and in DevForce that worked against it. I must have missed the "design mode" component of your post. It would help if you said "Blend" a few hundred more times so I wouldn't miss it :-)
 
I haven't had time to see if there are challenges going the next step and working with cached data in a design time VM consumed in Blend (or Cider).
 
I've been living in 6.0.5 for what seems like forever (6 or 7 weeks is "forever" around here) so I'm not sure if the version upgrade will relieve your troubles.
 
If you have something that fails, I'd be happy to check it out. Can you send me a small Northwind example? I'd be much obliged. Otherwise, I'll have to put this off for at least two weeks.
 
p.s.: trust the move went well and you haven't locked yourself out of the house yet.
 
 


Posted By: WardBell
Date Posted: 02-Sep-2010 at 7:54pm
Update: We reproduced your case, found the obstacle to deserializing the cache while in Blend or Cider, and we have a fix. Unfortunately, the obstacle remains in 6.0.5.
 
I'm hoping myself to get an early 6.0.6 to try out the fix. Perhaps you can wait another 6 or 7 weeks for the official 6.0.6 ... although I'll be happy to send you an EAP of 6.0.6 as soon as we feel even slighly comfortable with it.
 
Thanks for driving hard on this issue.  That just makes the product better.


Posted By: mikedfox
Date Posted: 02-Dec-2010 at 12:50pm
Ward, this still seems to be broken. I'm adapting your recent clientui example and when I try to add items to my design time data manager using load cache, I get an error in serialization. It says that there are unknown types. At runtime, the same load from cache works.
 
As an alternate, I tried manually creating some sample data, but get an error trying to add entities with an identity key. Its looking for an idgenerator, even though at runtime it works.
 
I have 6.0.6.1


Posted By: WardBell
Date Posted: 02-Dec-2010 at 1:50pm
Mike - I am at Firestarter today so can't look at it this second. I take it you are not creating and attaching entities in code but, rather, trying to import them into the Design Entity Manager cache. I will look at this when I get back.


Posted By: WardBell
Date Posted: 05-Dec-2010 at 10:15am
@Mike - I posted an update to our BookShelf sample application so that it now uses an EntityCacheState for design time data. The zip includes a long document with a chapter on this subject. Find it at http://www.ideablade.com/BookShelfDF/BookShelfDF.zip. - http://www.ideablade.com/BookShelfDF/BookShelfDF.zip. I just http://neverindoubtnet.blogspot.com/2010/12/design-with-database-data-in-devforce.html - posted that chapter in my blog.
 
You get that serialization error because DevForce can't find your entity model while in the designers. I don't know yet why we can't find it and I'm going to get to the bottom of that question. Meanwhile, the workaround is simple: tell DevForce where to find your model assembly ... which you do with a line such as follows. You must execute it BEFORE creating any EntityManager:
 
    // Register the model's assembly name among probed assemblies
    // Must be called BEFORE the first EM creation else
    // fails w/ deserialization exception
    IdeaBlade.Core.IdeaBladeConfig.Instance.ProbeAssemblyNames
        .Add(typeof(BookClubEntities).Assembly.FullName);
 
The "BookClubEntities" class happens to be the specialized EntityManager defined in the application model; any class from that model would do for finding the model assembly.


Posted By: mikedfox
Date Posted: 06-Dec-2010 at 10:46am
thanks Ward, that did the trick


Posted By: Wayne
Date Posted: 26-Dec-2011 at 2:23pm
Following this thread with version 6.1.3 in a Silverlight app - loading from cache works fine at runtime but in Blend (design time) this line now seems to cause a failure:
IdeaBlade.Core.IdeaBladeConfig.Instance.ProbeAssemblyNames.Add(typeof(NorthwindIBEntities).Assembly.FullName);
Debugging Blend design time with VS2010 - Blend cannot seem to figure out whether to add the assembly name to 
IdeaBlade.Core or IdeaBlade.Core.SL (common names inside)
So the line fails and so then 
EntityCacheState.Restore(res.Stream)
 
fails too with null exceptions for each field and record during the load.
Is there a work-around to add the assembly full names ?
Thank you in advance.
 
 
 


Posted By: WardBell
Date Posted: 26-Dec-2011 at 9:53pm
Are you sure about the cause that you have identified? Could it be a null exception encountered during deserialization.Have you loaded Silverlight 5 by chance?
I ask because there is a critical serialization bug in the Silverlight 5 release (RTW) acknowledged by Microsoft; apparently they took a dependence on a serialization method that does not exist in desktop .NET 4. "So what?" Well both Cider and Blend are actually running desktop .NET 4 when they display your Silverlight XAML. Too bad for all of us; serialization does not work.
 
Microsoft has no intention of "fixing" SL 5 to address this. They have advised us that the developer should either revert to SL 4 (remove all SL 5 from the machine) or hang on for .NET 4.5; you can try a CTP of .NET 4.5.
 
Frankly, we don't think any of these workarounds are appropriate. We are trying to find something else to recommend; no luck so far.
 
It doesn't look good for designing with data that has been restored from an Entity Cache file ... or for any tactic that involves (de)serialization.
 
For now we suggest that you create design data dynamicall with a "Data Mother" class: a class that instantiates design-time entities and populates an offline EntityManager; these become the data source for design time data binding.
 
We regret the (temporary) loss of the entity cache file feature. I wish we knew a way around it. I'll certainly let you know if we come up with something.
 
Root for .NET 4.5 I guess.
 
If this is not the immediate cause of your problem and you are willing to live entirely in SL 4, please post again with more detail and we can get you going.


Posted By: Wayne
Date Posted: 26-Dec-2011 at 11:38pm
Interesting - I had installed Silverlight 5 but then uninstalled it and went back to Silverlight 4 on this development machine.
 
Additional - while stepping though each line after asking Blend to open User Control for designing -
 
IdeaBlade.Core.IdeaBladeConfig.Instance.ProbeAssemblyNames.Add(typeof(NorthwindIBEntities).Assembly.FullName);
is the first line of code that fails (subsequent  deserialization also fails) - but executing the code to use the design data during a regular run and stepping though the code - that line resolves the conflict, succeeds and the cached data load properly.
 
I will look into the possibility are some left overs of Silverlight 5 after reverting back to Silverlight 4.
 
Is there an example of the "Data Mother" class available that you could share >
 
Thank you for your help.
 
 



Print Page | Close Window