(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

WCF RIA Services Part 8 - Testing and Debugging

(9 votes)
Brian Noyes
>
Brian Noyes
Joined Jun 10, 2010
Articles:   19
Comments:   116
More Articles
21 comments   /   posted on Nov 16, 2010
Categories:   Data Access , Line-of-Business

This article is Part 8 of the series WCF RIA Services.

Introduction

In this article, I am going to focus on two aspects that are just as important as how to write the code – how to write the code in such a way as to be more testable, and how to debug and diagnose when your code is not doing exactly what you expect. On the testing front, I am mainly focusing on the ability to unit test important parts of your code, such as logic in a view model that consumes a domain service. I’ll also talk briefly about how to test on the server side, although full coverage of that will have to wait for another article. Along the way, you will learn a lot more about the magic that is going on under the covers of the domain context as well.

On the debugging front I will discuss what mechanisms and techniques you can use to understand what is going on outside of your code in the RIA Services stack on the client and server side as well as what is happening at the wire level to try to infer the cause when you are not getting the results you expect from your RIA Services calls.

You can download the code for this article here.

Unit Testability

With respect to unit testing, I am assuming you are familiar with the general concepts of unit testing and separation of concerns. If you have never tried to write unit tests, you are not going to learn the fundamentals here. And in fact, you are going to be thrown to the bottom of the deep end if you try to start here, because unit testing RIA Services code is not for the faint of heart. One of the downsides of the WCF RIA Services framework is that it is what I would call “test-resistant" code. The framework does a lot of work under the covers for you that gets intimately intertwined with the code you write that you want to test. It makes it hard to write unit tests that just exercise your logic code.

If you are new to unit testing, I’d strongly recommend the book The Art of Unit Testing by Roy Osherove as a great starting point for learning about unit testing concepts and techniques. Additionally, you need to understand how the Silverlight Unit Test framework works, including how to test asynchronous execution in that environment. I’ll explain some of the latter as I go, but some good pre-reading is the Silverlight Unit Test tutorial and this post on asynchronous testing by Jonas Folleso.

I am also assuming you understand the motivations and reasons for separating the logic of a view from the structure of the view – specifically the Model-View-ViewModel pattern that I showed in Part 4 of the series, or possibly the Model-View-Controller or Model-View-Presenter patterns. If you don’t separate your logic from the view definition, you are going to have a hard time unit testing it in the first place because the controls and elements in the view often expect that they are running in a UI context and will not have the same behavior if they are not being rendered. So the assumption here is that on the client side you are using the MVVM pattern.

The main thing I am going to focus on in this article has to do with the ability to unit test code in your view models if using a domain context in the view model, as well as the ability to unit test business logic that is executed in your domain service. In either case, the key concept when unit testing is the need to mock out calls to external dependencies. In the figure below, the unit under test is typically a chunk of logic code such as that found in a view model in the presentation layer or a business logic component on the server. The client code is whatever calls the exposed API directly. in the case of a view model, it may be the view or it may be some of the client hosting code that instantiates the view model, such as an application controller. In the case of a server business logic component, the calling code may be the WCF RIA Services infrastructure itself if it is something like a domain service method, or it could be a child component that gets called from your update methods within your domain service. The logic you are trying to test often has dependencies of its own, parts of the code that call out to downstream components that go outside the local execution scope themselves. Examples would be data access components on the server side or the service calls being made under the covers of the domain context on the client side.

In order to unit test that code in the middle, you need to be able to replace the client code with your unit test code, and you need to be able to substitute a mock or stub implementation of the dependency so that the unit under test calls the mock instead of the real dependency. You also need a way to specify the behavior of that mock object so that it behaves as the real dependency would in terms of inputs and outputs in the context of each unit test you are going to write against the unit under test. You can do this by writing “manual mocks” or classes that implement the same interface as the dependency but that have their behavior coded into the test environment or by using a mocking framework such as MOQ, Rhino Mocks, TypeMock Isolator, or JustMock. For this article, I’ll be using MOQ.

Unit Testing the View Model or Client-Side Service That Uses a DomainContext

There are a couple places in a Silverlight client architecture where you may create and use DomainContext objects. If you are following the MVVM pattern and separating the concerns of your application, the two most likely places you would be creating and making calls through a domain context is in a view model or a client side service (chunk of re-usable code obtained through service locator pattern). In either case, if the surrounding code has any complexity or logic surrounding those service calls and the manipulation of the entities, you should really be writing some unit tests as you write the code to help flesh out that behavior and verify its correctness. The trick is that the domain context makes asynchronous service calls you when make your Load, SubmitChanges and Invoke method calls. And in a unit test environment, you don’t want those calls to actually make service calls, you want to be able to mock them out.

You might be tempted, as I was, to say “no problem, just put an interface-based wrapper around the domain context object and mock that.” While that is actually fairly easy to do, it doesn’t actually make the problem much easier. The real challenge about unit testing code that is using a domain context as a dependency has to do with both the async API exposed, and the state management that the domain context does behind the scenes maintaining the collections of entities and the change tracking on those entities. Also things like associating errors in the service calls with the entities for validation purpose, etc. Bottom line, that little domain context is working its butt off for you, and if you try to mock out its public API, your mocking code will have to do a ton of work to simulate the same behavior. Since the domain context really becomes part of the functionality of your view model, you won’t really be testing the functionality of your view model anymore, you will just end up testing that your mock is doing what you told it to, which is pointless.

Dealing with the async nature of the domain context calls in your unit tests can be made much easier if you can transform the async API into single synchronous method calls for the purposes of mocking them in your unit tests. Mocking a synchronous API is easier because the outcome of any one call to a dependent object can be mocked as being complete when that method or property call completes. Most mocking frameworks are designed around mocking synchronous calls as well.

It turns out that there is a better place to do the mocking of the service calls rather than trying to do it on the asynchronous surface of the domain context. Under the covers of the domain context object, there is a DomainClient object. This is the object that actually makes the real service calls that hit the wire. Those service calls are still made based on an async API at the domain client level, but if you mock out the domain client, it is fairly easy to turn the async calls into synchronous calls at the point where they are dispatched. Then in the unit test environment, you can mock out the synchronous calls so that the unit code is easier to write and it runs more synchronously. You’ll see what I mean by this in the code shortly.

The call chain when you call through the domain context looks like the following figure:

For example, consider what happens when you do a Load operation on a domain context from your view model. The domain context takes the EntityQuery<T> that you passed into the Load method and passes it to a base class as an EntityQuery, a type not tied to a specific entity type. The base class makes an asynchronous call into the domain client to a method called BeginQueryCore. When completion of that async operation is indicated by the domain client through a callback to the domain context (normally when the web response is received in the real domain client used by the RIA Services code), the domain context then calls EndQueryCore and gets back a  QueryCompletedResult that contains the retrieved entities in a general purpose container that can contain any kind of entity. The contained entities are then unpacked from that payload by the domain context and it updates the entity collections exposed from the domain context. It then calls and Load completion callbacks or event handlers.

To mock out the calls to the service then, you just need to replace the domain client that your domain context would use by default and replace it with one where you can mock the calls to the server in a synchronous way. The approach I will be demonstrating in this part of the article is based on some guidance put out by Nikhil Kothari from the RIA Services product team on his blog in this post. That post doesn’t take it very far, only showing a simple query. So I am picking up where that left off and showing how to handle a little more complexity in terms of verifying queries as well as handling SubmitChanges calls.

The first step is to create a derived class from the DomainClient class that turns the async calls to the server into synchronous method calls that you can mock out more easily. Once you get down to the domain client level, there are really only three logical operations that are being dispatched on the wire by a domain client – Query, Submit, and Invoke. Basically, the DomainContext base class effectively translates between the type specific API exposed by the derived DomainContext class that gets code generated on the client for each domain service (i.e. TasksDomainContext)  and the more general purpose API exposed by the DomainClient base class. It doesn’t throw away all type safety, but wraps the specific entity types in generic wrappers and containers that get passed at the wire level (EntityQuery and EntityChangeSet for Query and Submit respectively).

Creating a Mock DomainClient

So if you want to mock out the domain client and do so with a blocking implementation that is easier to work with in unit tests, you just need to add a synchronous API to the DomainClient base class that gets called in a blocking way from the Begin/End method pairs associated with Query, Submit, and Invoke.

The following class shows the TestDomainClient class that I put together for this article. It handles the Query and Submit parts, but I’ll leave it as an exercise for the reader (or for me in a future post) to flesh out and demonstrate the Invoke operations. The first step is to derive from DomainClient and add a set of synchronous methods that can be mocked out in the unit tests.

 

 public abstract class TestDomainClient : DomainClient
 {
     private SynchronizationContext syncContext;
  
     protected TestDomainClient()
     {
         this.syncContext = SynchronizationContext.Current;
    }
  
     public abstract IQueryable<Entity> Query(EntityQuery query);
     public abstract IEnumerable<ChangeSetEntry> Submit(EntityChangeSet changeSet);
     public abstract InvokeCompletedResult Invoke(InvokeArgs invokeArgs);
   
     protected override sealed IAsyncResult BeginInvokeCore(InvokeArgs invokeArgs, 
         AsyncCallback callback, object userState) {}
     protected override sealed InvokeCompletedResult EndInvokeCore(IAsyncResult asyncResult) {}
     protected override sealed IAsyncResult BeginQueryCore(EntityQuery query, 
         AsyncCallback callback, object userState) {}
     protected override sealed QueryCompletedResult EndQueryCore(IAsyncResult asyncResult) {}
     protected override sealed IAsyncResult BeginSubmitCore(EntityChangeSet changeSet, 
         AsyncCallback callback, object userState) {}
     protected override sealed SubmitCompletedResult EndSubmitCore(IAsyncResult asyncResult) {}
 }

The overrides at the bottom of the class show the inherent API of the base class that I need to override. The three abstract synchronous methods at the top are the ones that I want to be able to mock out in the unit tests. The SynchronizationContext stuff at the top is a little necessity to deal with thread dispatching on the UI thread since this code will run in the Silverlight Unit Test framework, which runs on a UI thread itself.

The trick here is to just make the async methods call the new synchronous abstract ones and cause the async API (which will be called by the domain context) to return the results specified by the synchronous methods. The implementation for the Query side of things looks like this:

 protected override sealed IAsyncResult BeginQueryCore(EntityQuery query, AsyncCallback callback, object userState)
 {
     var results = this.Query(query);
     var asyncResult = new TestAsyncResult 
     { 
         Callback = callback, AsyncState = userState, 
         Entities = results.ToArray(), TotalCount = results.Count() 
     };
  
     this.syncContext.Post( cb => ((TestAsyncResult)cb).Complete(), asyncResult);
   
     return asyncResult;
 }
   
 protected override sealed QueryCompletedResult EndQueryCore(IAsyncResult asyncResult)
 {
     var localAsyncResult = (TestAsyncResult)asyncResult;
     return
         new QueryCompletedResult(
             localAsyncResult.Entities,
             localAsyncResult.IncludedEntities,
             localAsyncResult.TotalCount,
             new ValidationResult[0]);
 }

You can see that what the code does is call the synchronous abstract method in the BeginQueryCore method with the EntityQuery argument that was passed down from the domain context to come up with the result synchronously. Then it puts together a custom IAsyncResult object that packages up the results with a structure that will be passed to the End method when it gets called by the domain context. That call is triggered by invoking the callback object that was passed into the BeginQueryCore method, which is done through a Post call on the SynchronizationContext to make sure it gets dispatched to the UI thread if needed. The TestAsyncResult class is just a simple data structure that contains the normal members of IAsyncResult plus some extra properties to package up the different data structures needed to dispatch results from the Begin method to the End method for the domain client operations. You can see the full code of the TestDomainClient and the TestAsyncResult class in the download code for this article.

The EndQueryCore method then just takes the values that were packaged up on the custom IAsyncResult object and uses them to populate the QueryCompletedResult object that is returned to the domain context.

Testing a View Model Command Handler for a Query

With code like that in place, you can then start writing unit test on top of your view model code that makes a Load call. For example, consider this simple command handler in the TasksViewModel class:

 internal void OnSearchByDate(object param)
 {
     _Context.Tasks.Clear();
    EntityQuery<Task> query = _Context.GetTasksQuery();
     LoadOperation<Task> loadOp = _Context.Load(
       query.Where(t => t.StartDate >= LowerSearchDate && t.StartDate <= UpperSearchDate));
 }

There is not much logic here to warrant a unit test in terms of the view model code itself, but you might want to write one that verifies that after executing a search, the Tasks collection exposed by your view model contains the results of that search, and also verify that it looks like it executed the query by filtering on the StartDate property of the tasks.

The unit test code to do so looks like the following:

 [TestMethod]
 [Asynchronous]
 public void OnSearchByDate_RepopulatesTasksByStartDate()
 {
     // Arrange ---------------------------------
     bool loadExecuted = false; 
     var domainClientStub = new Mock<TestDomainClient>(); 
     domainClientStub.Setup(dc => dc.Query(It.Is<EntityQuery>(query => query.EntityType == typeof(Task))))
         .Callback(() => loadExecuted = true) // used to verify it was called
         .Returns(() => new Entity[] // dummy data
         { 
             new Task { TaskId = 42, TaskName = "Task 1" },
             new Task { TaskId = 43, TaskName = "Task 2" }
         }.AsQueryable()
         );
  
     // Act -------------------------------------
     TasksViewModel viewModel = new TasksViewModel(domainClientStub.Object);
     viewModel.OnSearchByDate(null);
   
     // Assert ---------------------------------
     Assert.IsTrue(loadExecuted); // did it execute?
             
     EnqueueConditional(() => 
         viewModel.Tasks != null && 
         viewModel.Tasks.Count() == 2); // Did we get the right results
             
     domainClientStub.Verify(dc => 
         dc.Query(It.Is<EntityQuery>(query => 
             query.Query.ToString().Contains("StartDate") // Did it query by the right stuff
     )));
             
     EnqueueCallback(() => // Did it populate the view model
         {
             Assert.AreEqual("Task 1", viewModel.Tasks.ToList()[0].TaskName);
             Assert.AreEqual("Task 2", viewModel.Tasks.ToList()[1].TaskName);
         });
  
     EnqueueTestComplete();
 }

So the first thing you might notice is that it took about 30 lines of actual test code to exercise 4 lines of actual view model code. That is not going to make you or your manager happy. Even though this is only exercising 4 lines of your code, it is exercising a whole lot more than that under the covers in the RIA Services stack. That probably raises the question of whether you should be writing tests that are really just testing that the RIA Services code is doing what it should. Generally the answer to that would be no. But if you want to have a test to verify that your view model properly executes a search, this is what you are going to have to go through.

If the method were as simple as the one I am showing above, I’m not sure I would be able to convince myself that the test coverage is worth it. However, if you have a more complicated interaction with the data after executing the search, including some post processing of the results with surrounding conditional logic or computation on the client side, then you should probably be testing that. The same test pattern shown above would cover you for making sure you are in control of the query part of it in your unit test, and then you would just add whatever verifications are needed to make sure the real logic of your view model is correct. However, using such a simple chunk of view model code here lets us focus on what is needed to do the testing in general without getting mired down in the actual logic verification itself.

Let me walk through the code above line by line to make sure it is clear what all the ceremony is about. The first thing the test does is to create a mock implementation of the TestDomainClient base class using the MOQ framework. It then uses MOQ’s API to express an “expectation” of how that mock object will behave. Specifically, lines 8-15 basically say the following: “I expect that under the covers of the method call I am about to make, my mock domain client object will have its Query method called. I expect that it will be called with an EntityQuery for Task objects. When it gets called, I want to set a flag (loadExecuted) so that I can verify that it was called. Additionally, I need that method to return these two instances of Task objects as the resulting entity collection when it completes.” That is probably the most complicated part of the test itself, and most of the complexity just has to do with understanding the lambda-based syntax of saying that in code to MOQ.

After that, the code is done with the “Arrange” phase of the Arrange-Act-Assert pattern of unit testing and is ready to execute the target method, which it does in a straightforward call to the view model object. Note that the code uses manual dependency injection to pass the mock domain client to the view model. This required adding an internal constructor to accommodate that and some clean up of the main constructor’s code to factor out the common construction pieces (i.e. setting up the command and initializing member variables) so that it could be called from both the default constructor that uses the default constructor of TasksDomainContext and the following one that accomodates dependency injection in the unit tests.

The injection constructor looks like this in the view model:

 internal TasksViewModel(DomainClient client)
 {
     _Context = new TasksDomainContext(client);
     CommonConstruction();
 }

You can see that the magic hook here is that there is an overloaded constructor on the domain context that takes a DomainClient instance. This gives me the path I need to inject my mock instance under the covers of the real TasksDomainContext that is being used by the view model.

In the Assert phase of the test, I am actually checking four things. Technically this is a bit of a style violation for unit tests – a unit test should test one outcome. In this test I am trying to validate is that the query ran and returned the expected outcome. To ensure that, I am checking it in several ways to show things you could do. The first thing in line 22 is just verifying the flag got set that tells us the mock Query method ran. The next in lines 24-26 is an EnqueueConditional call to wait until the Tasks collection of my view model has been populated. Because the Load happens asynchronously, this is essential because the synchronous call chain of calling the search method will complete, and then the thread dispatching happens to marshal the results back to the UI thread and then the Tasks will show up in the collection. EnqueueConditional allows you to wait in your test and the test framework will basically sample that condition for a short period of time to see if it happens. If it doesn’t happen after the framework knows or assumes it should have been called (based on black magic and voodoo that the test framework is doing), then it gives up and fails your test so it doesn’t block forever. If you get past that conditional without a failure, it means that condition was met, which is an implicit assertion of its own.

After that,lines 28-31 use MOQ to verify that some of the low level parameters (specifically the query expression) that got passed to the mock contained what I expected – confirming that it does look like it was filtering on StartDate. I could get more fancy with that to really check that it was filtering on upper and lower bounds, but that gets a lot messier. Finally at the end of the test I verify that the tasks in my view model collection are the ones returned from the query, using the EnqueueCallback method from the Silverlight Unit Test framework so that the UI can pump any messages in between if necessary.

Now admittedly this is all very artificial because the real work of a query goes on on the server, so this is just going to return whatever we tell the mock to return. But it should at least give you a sense of how to go about providing a mock domain client and being able to specify its behavior and verify that it was called as expected.

Testing a View Model Command Handler for SaveChanges

Now let me show an example of how to handle testing that the Save Changes command on the view model works correctly. The view model command handler method looks like this:

 internal void OnSaveChanges(object param)
 {
     _Context.SubmitChanges(OnSaveCompleted, null);
 }

Again, not enough here that I would normally write a unit test for, but what if you did want to verify that your changes were getting sent to the server at the right time if there were some surrounding conditional logic determining when that call should happen. You would still need to be able to mock out the call to the server and verify if it happened in your unit test.

Here is a test that verifies that when we call OnSaveChanges on the view model, that the server gets called with Submit:

 [TestMethod]
 public void SaveChanges_CallsServerToSubmitChanges()
 {
     // Arrange ---------------------------------
     bool submitExecuted = false;
     var domainClientStub = new Mock<TestDomainClient>();
     // Need to use the domain context directly to add an item to the collection
     TasksDomainContext testContext = new TasksDomainContext(domainClientStub.Object);
     Task newTask = new Task { TaskId = 33, TaskName = "Test" };
     testContext.Tasks.Add(newTask);
     // Get the associated change set to mock the call
     EntityChangeSet resultSet = testContext.EntityContainer.GetChanges();
     domainClientStub.Setup(dc => dc.Submit(It.Is<EntityChangeSet>(ecs => ecs.AddedEntities.Count == 1)))
         .Callback(()=>submitExecuted = true)
         .Returns(resultSet.GetChangeSetEntries());
  
     // Act -------------------------------------
     TasksViewModel viewModel = new TasksViewModel(testContext);
     viewModel.OnSaveChanges(null);
  
     // Assert ---------------------------------
     Assert.IsTrue(submitExecuted);
     domainClientStub.Verify(dc => dc.Submit(It.Is<EntityChangeSet>(ecs => ecs.AddedEntities.Count == 1)));
 }

In this case, you need to access the domain context directly in the test because you need to provide an EntityChangeSet to the mock to return. The only way to get one of those (because of internal constuctors and ReadOnlyCollections exposed from it) is to use the GetChanges method on an EntityCollection, which is tucked away under your DomainContext. So in this case I added another test constructor that lets me inject the domain context after populating it with its mock domain client and accessing its EntityCollection. Feels like a little too much intimacy to me, but seemed to be the only way I could find to get the test written.

The final test in the sample code deals with another piece of nastiness that the view model was doing that I had highlighted in an earlier article – popping message boxes and child windows directly. When an error occurs on while sending changes to the server, I wanted to alert the user of the problem. So the quick and dirty approach from an earlier article was to pop a message box from the view model when the asynchronous completion of SubmitChanges happened and the error was detected. In a clean view model world, the view model just manages state, it does not directly render things itself.

So in this version I used a little chunk of functionality from the new Prism 4 release which helps address this. I’ll be writing some articles on Prism 4 in the near future, but you can take a look at this post for a quick summary of the feature I am using, called interaction requests. This allows the view model to just expose another property of an interface type, and on that interface is an event that the view can handle (with a behavior) to prompt the user. Now the view model is decoupled from the actual rendering which is very important for unit testing, as well as just clean separation of concerns.

The completion code for the SubmitChanges call now looks like this:

 private void OnSaveCompleted(SubmitOperation obj)
 {
     if (obj.HasError)
     {
         SaveFailedNotification.Raise(
             new Notification { 
                 Content = obj.Error.Message, 
                 Title = "Error saving changes" });
         obj.MarkErrorAsHandled();
     }
   
 }

A custom Prism behavior in the view is the normal subscriber to the Raise event on the notification object (of type InteractionRequest<T>) and it takes care of presenting a templatable ChildWindow as a popup – leaving it up to the owner of the view to decide what the right presentation to the user is. Again imagine in a more real application there might be some more view model logic surrounding this that I also want to unit test, and one of the branches I want to verify is that if a server exception comes back, the user gets prompted.

Now I can write a unit test like so to verify that the view model does what it needs to do to prompt the user when a failed SubmitChanges call happens:

 [TestMethod]
 [Asynchronous]
 public void SaveChanges_WithFailure_NotifiesUser()
 {
     // Arrange ---------------------------------
     bool userNotified = false;
     var domainClientStub = new Mock<TestDomainClient>();
     TasksDomainContext testContext = new TasksDomainContext(domainClientStub.Object);
     Task newTask = new Task { TaskId = 33, TaskName = "Test" };
     testContext.Tasks.Add(newTask);
     EntityChangeSet resultSet = testContext.EntityContainer.GetChanges();
     domainClientStub.Setup(dc => dc.Submit(It.Is<EntityChangeSet>(ecs => ecs.AddedEntities.Count == 1)))
         .Throws(new ArgumentException());
  
     // Act -------------------------------------
     TasksViewModel viewModel = new TasksViewModel(testContext);
     viewModel.SaveFailedNotification.Raised += (s, e) => { userNotified = true; };
     EnqueueCallback(() => viewModel.OnSaveChanges(null));
     // Assert ---------------------------------
     EnqueueConditional(() => userNotified);
  
     EnqueueTestComplete();
 }

Since all that really happens at a view model level now to prompt the user is firing the Raised event on the InteractionRequest<T> object, I can easily subscribe to that in a unit test and verify that it gets raised when an exception is thrown from the service call (Submit in this case). Line 12-13 take care of setting the MOQ expectation that when Submit is called, it will throw an ArgumentException.

So hopefully that shows you how you can still use a domain context directly in your view models, but still be able to unit test the code by mocking out the service calls. The downside as you can see is that it is not very clean or easy. But it is at least do-able, and it is also an easily repeatable pattern.

Unit Testing Domain Service Code

On the server side, things don’t really get any easier when it comes to unit testing your domain service class logic. If you use the LinqToEntitiesDomainService class for example, the WCF RIA Services stack is the client of your domain service and does a lot of work in between each individual method call on your domain service class, all as part of a single service operation from the client. Additionally, in that case you will have the Entity Framework ObjectContext class as a direct dependency, which itself is inherently not unit testable since it goes out direct to the database, outside your execution context.

If you have discreet chunks of logic being invoked from your domain service methods (i.e. validation logic that gets executed on each entity for each update or insert), that code can usually be factored out into separate methods or components that get called from the domain service operations and it can often be tested on its own. For example, the TasksDomainService has this method that gets called for validation on an insert or update operation on a task:

 private void ValidateCustomerHours(Task task)
 {
     if (task.CustomerId.HasValue)
     {
         int customerId = task.CustomerId.Value;
         bool timeAllowed = IsTotalTimeAllowed(customerId, task.TotalTime);
         if (!timeAllowed)
             throw new ValidationException(
      "You cannot enter more than 10 hours per billing cycle for this customer.");
     }
 }

It would be a trivial task to write a unit test for a method like this. However, if you start weaving logic into the domain service Insert, Update, and Delete operations themselves, you will have a hard time unit testing them because you would need a means to mock out the calls to the Entity Framework ObjectContext. Again, using a commercial mocking framework like JustMock or TypeMock Isolator you can do that, but with the free open source ones like MOQ or Rhino Mocks, you generally cannot.

The only choice at that point is to start designing all of your domain services as POCO domain services – ones derived from the DomainService base class. This requires you to define your own object model, use the right attributes on your entity properties, and a lot of other concerns that I am going to have to defer until a later article.

If you use POCO domain services, then you can factor your data access logic out to a repository pattern, and mock out the repository in your unit tests of the domain service methods. This walkthrough in the RIA Services documentation demonstrates this approach.

Debugging RIA Services

One of the other challenging things about WCF RIA Services at times is that if things are not working as you expect, it is hard to tell what to do to figure it out. The RIA Services stack is doing a lot of work on your behalf. That is the whole point of using RIA Services – they do the work so you don’t have to. Unfortunately, that means there is a lot of magic going on under the covers of a few simple method calls in your client code.

The first step to being able to understand when problems occur is to understand what is going on under the covers of RIA Services. I don’t think anyone should use pre-built plumbing like RIA Services unless they understand what is happening under the covers. Hopefully this and other articles in this series has helped you understand some of what is happening under the covers a little better. Because so much happens under the covers, for debugging you need to be able to peek under those covers and observe what is going on.

The first and most important tip to debugging RIA Services that I have mentioned before is that you should really handle completion of all async calls through your domain service and have code that checks for the HasError property to be set on the operation argument. If there is an error and you don’t handle it, it will thrown the exception on the client side and it will crash your application. You should have code like this for every completion of every Load, SubmitChanges, and Invoke method call on your domain context:

 private void OnSaveCompleted(SubmitOperation op)
 {
     if (op.HasError)
     {
         // Do whatever is appropriate
        op.MarkErrorAsHandled();
     }
 }

Setting a breakpoint here and inspecting the error information is your first line of defense for figuring out what is wrong. As I have already covered in earlier articles, RIA Services will package up any server processing errors and will raise them here. Since a lot of the server processing happens in the RIA Services stack outside of your domain service method calls, this is often the only place you can set a breakpoint and see the details of those exceptions.

On the server side, another option you have is to override the OnError method from your domain service base class. This will be called when there is an exception server side in the process of handling a query operation or in processing a ChangeSet for updates. Setting a breakpoint there gives you more direct access to the server stack and variables in your domain service instance while it is actually executing.

It is also important at times to be able to inspect and verify exactly what RIA Services is sending back and forth at the wire level. To do this, you have two tools at your disposal that you should get familiar with. The first is Fiddler. This tool lets you inspect the raw HTTP requests and responses on your machine. To use this effectively, you will need to set up your web project that is hosting your Silverlight app and your domain services to run under IIS instead of the ASP.NET Development Server that fires up by default when debugging in Visual Studio. Additionally, it is easier to work with if you set it up so the Silverlight app is launching using your machine name instead of localhost. Setting up and using Fiddler is beyond the scope of this article, but you can find some of the key ways to do so here.

Because RIA Services makes binary encoded HTTP REST calls by default, the payloads of the messages may be a little hard to read. You can usually still see the basic structure of the message in there, but it will be a little cryptic. However, if there are errors calling the service, you will see those much more obviously in Fiddler than you will by trying to analyze the error messages coming out on the client side code. And you can usually see enough there to figure out the basics of what is going on.

If that doesn’t work for you, another important tool is the WCF Service Trace Viewer. Since RIA Services is using WCF to make all the service calls, turning on tracing and message logging on the server side will at least give you full insight into what is happening in the WCF stack on the server, and will also give you a chance to inspect the raw messages before they are binary encoded on the wire. For a quick intro on how to turn on tracing and message logging, check here.

A final important debugging tip is that if you are getting cryptic errors back from your client domain context calls, one of the first things to check is that the service itself is hosted correctly and that the client is able to call it. The quick check for that is that your domain service is automatically hosted by RIA Services by default at the same site your XAP file is hosted. RIA Services automatically goes looking for the service in the root virtual directory or site and tacks on the fully qualified domain service class name, replacing dots with dashes, and adding a .svc file extension. For example, in the sample code, the domain service is TaskManager.Web.TasksDomainService. The default URL where the service gets hosted by WCF is <rootsite>/TaskManager-Web-TasksDomainService.svc. So when debugging on my local machine hosted in IIS, I can fire up a browser and hit http://<mymachinename>/TaskManager.Web.TasksDomainService.svc and I should see a page like the following:

If there is an error, then I know my domain context is not going to succeed in talking to the domain service and can focus on resolving whatever errors show up here. For a great step through of some of the common errors you will get, see this Silverlight TV episode with Saurabh Pant from the RIA Services team.

Summary

Testing and debugging are deep topics, and a lot more could be said about each. Perhaps I’ll expand on some other areas in subsequent articles or blog posts. WCF RIA Services definitely makes it more challenging to unit test your code because to fully leverage what RIA Services can do for you, it tends to get sort of intertwined with your own logic code. So exercising portions of the RIA Services stack as part of your unit tests is necessary, but you still need to be able to mock out the external calls if you want it to be a unit test. This article showed you how you can do that on the client side and gave some pointers on how to deal with it on the server side.

For debugging, you need to be able to see what is going on at the wire level a lot of times, and Fiddler or the WCF Service Trace Viewer can let you do that. Additionally, you have a couple of places to hook errors on the client and server sides that were pointed out.

In the next article in the series, I’ll talk a little about how to structure your RIA Services applications when you get beyond having just a single client project and single service project.

You can download the sample code for this article here.

About the Author

Brian Noyes is Chief Architect of IDesign, a Microsoft Regional Director, and Connected System MVP. He is a frequent top rated speaker at conferences worldwide including Microsoft TechEd, DevConnections, DevTeach, and others. He is the author of Developing Applications with Windows Workflow Foundation, Smart Client Deployment with ClickOnce, and Data Binding in Windows Forms 2.0. Brian got started programming as a hobby while flying F-14 Tomcats in the U.S. Navy, later turning his passion for code into his current career. You can contact Brian through his blog at http://briannoyes.net/ or on twitter @briannoyes.


Subscribe

Comments

  • -_-

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by Matt on Nov 24, 2010 19:08

    Great series! I just read through the whole thing. Sadly I have to agree that RIA Services is not very testable. It's by far my biggest complaint against it. I also hate to admit that how difficult it is to test RIA Services is enough for me to consider adopting different technology altogether. RIA Services team, if you're listening, please address this.

    Anyway, another way to find the URL for your WCF service is to open the generated code file on the client side (it is named something like "MyWebApp.Web.g.cs", and just search for ".svc" in that file. You will find the constructors that create the WebDomainClients, and it will contain a string literal with the URI of the svc.

  • -_-

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by Colin Blair on Jan 06, 2011 17:51
    For unit testing the DomainService code, in a normal LinqToEntitiesDomainService the only call being done to the database will be in the PersistChangeSet method. If you override that method and, during testing, call the AcceptAllChanges method instead of the SaveChanges method you will simulate a successful save to the database. You can start a full test of the full Submit process by calling the Submit method with your own ChangeSet object. Almost over step of the Submit process has an overridable method if you want to unit test the entire chain. There are instructions on the WCF RIA Services wiki on how to setup the DomainService in test.
  • brian.noyes

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by brian.noyes on Jan 07, 2011 01:12
    Nice tip Colin, thanks much!
  • -_-

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by Resad Zacina on Apr 21, 2011 23:35
    This is the best series on RIA Services i have found so far, thank you very much for the great work!
  • -_-

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by Isaac Abraham on May 31, 2011 23:12

    @Colin: Any query you perform in a domain service on the ObjectContext (in a linq-to-entities domain service) will attempt to go to the database. So you need a way to mock the service, only this (apparently) can't be done as the EF Domain Service is closely coupled to the context. So you either go for the repository pattern / use a standard Domain Context and manually control lifetime of the context or use proxy classes. I blog about it here... http://cockneycoder.wordpress.com/2011/05/28/testing-out-the-entity-framework-wcf-ria-services-domain-service.

    Hope that helps someone...

  • -_-

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by Isaac Abraham on May 31, 2011 23:13
    Ehm... that last comment should have read "you need a way to mock the object context".
  • -_-

    RE: WCF RIA Services Part 8 - Testing and Debugging


    posted by Brian Noyes on Jun 01, 2011 00:55

    Abraham,

    Nice post! Thanks for adding the link here for additional approaches to being more testable on the server side.

    Brian

  • Arushi

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by Arushi on Aug 01, 2011 10:09
    The link for the source code does not work!!! Please help it's such a useful article. Thanks.
  • brian.noyes

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by brian.noyes on Aug 01, 2011 15:52

    You can also find the sample code here: http://www.softinsight.com/downloads/Articles/SilverlightShow/TaskManagerPart8.zip 

  • robt

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by robt on Mar 06, 2012 19:58

    Brian,

    Thanks for the article, it is very informative.  I'm having some trouble implementing it and was hoping you could provide some insight.I've created the TestDomainClient and am injecting it into the DomainContext as you describe.  Here is my Mock setup:

    var domainClientMock = new Mock<MockDomainClient>();
    domainClientMock
    .Setup(dc => dc.Query(It.Is<EntityQuery>(query => query.QueryName == "GetCorrespondences")))
        .Callback(() => loadExecuted = true)
        .Returns(() => new Entity[]
    {
    new CorrespondenceDTO{ Id=new Guid(),ProgramName = "name1",Title="title1",Description = "description1"},
    new CorrespondenceDTO{ Id=new Guid(),ProgramName = "name2",Title="title2",Description = "description2"}
    }.AsQueryable()
    }

    You may notice that I had to change the .Setup(...) a bit otherwise it was not finding the query.  It would get into BeginQueryCore and not have a value in results.  It's working now (in so much as it does have the test data in results). I also have my injected values in EndQueryCore (when I step through it).  And now we come to where the wheels fall off for me.  

    When I step out of EndQueryCore and into the ViewModel where I step into the Completed event handler for the LoadOperation the Error property for the LoadOperation is 

    "Load operation failed for query 'GetCorrespondences'. This EntityContainer does not contain an EntitySet of type 'BasicDemo.Web.Models.CorrespondenceDTO'. If the type is external to this EntityContainer, please make sure you’ve called AddReference to establish the external link."

    Just for fun I tried calling AddReference on my DomainContext and passing in the CorrespondenceDTO, but it throws an error stating that it it already there.  Is this enough info for you to even hazard a guess?

  • brian.noyes

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by brian.noyes on Mar 07, 2012 03:54

    So are you saying you tried doing the Mock.Setup call with the entity type instead of the QueryName and it did not work? I can kind of see why you would get the error you are describing by doing the setup that way because now the mock domain client has no information about the types it is exposing. When you setup the mock based on the type of the entity, it implicitly has type information to match against.

    But other than that, I have no idea why checking for your entity type would not work for you, nor exactly why not checking based on entity type would cause what you are seeing without going and doing a lot of digging with Reflector.

    Regards

    Brian

  • robt

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by robt on Mar 07, 2012 08:21

    Brian,

    Thanks for taking the time to get back to me, especially so quickly.  I ended up using Reflector and stepping through the code.  Turns out my real issue was having RIA Services linked in both the silverlight project and in the unit test project.  This was auto generating the same DomainContext classes in two places.  Since everything still compiled I figured it was just an overactive Resharper giving me warnings.  What was actually failing was a comparison deep inside EntityContainer where it was looking through the EntitySet for matching types.  What was in there was a match in name only, since the actual Types were coming from two separate dlls.

    I hope that all makes sense and thanks again for your time.

    Rob


  • brian.noyes

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by brian.noyes on Mar 07, 2012 15:09

    Rob, glad to hear you sorted it out. Thanks for putting the solution here in the comments for others to benefit from. It easy to get bitten with the transparent code gen that happens at build time if you have it happening in two places and pull them together into the same scope. I've done it to myself on several occasions.

  • gnkarthik

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by gnkarthik on Nov 29, 2012 13:56

    Hi Brian,

    This is an amazing article i read for a long time and you deserve all credit for it. However i have a question. I am using LinqToEntitiesDomainContext, not Entites (i.e. Task) but a ComplexType for Update/Insert/Delete. I could MoQ and test the dependency for Select. but i am unable to test the U/I/D. Please find below sample code of what i am trying to achieve and please suggest:

    // Arrange --------------------------------

    bool submitExecuted = false;

    var domainClientStub = new Mock<TestDomainClient>();

     

    domainClientStub.Setup(d => d.Invoke(

     

     

    It.Is<InvokeArgs>(q => q.ReturnType == typeof(void)))).Callback(() => submitExecuted = true)

     .Returns(new InvokeCompletedResult(typeof(void)));

     List<MainPageModel> testData = new List<MainPageModel>();

     testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "G", CustomerId = 0 });

     testData.Add(new MainPageModel() { FirstName = "Fedrick", LastName = "D", CustomerId = 1, IsDelete = true });

     testData.Add(new MainPageModel() { FirstName = "Steve", LastName = "K", CustomerId = 2 });

     // Act -------------------------------------

    MainPageViewModel viewModel = new MainPageViewModel(domainClientStub.Object);

     

    viewModel.CustomerData = testData;

    EnqueueCallback(() => viewModel.DeleteCustomers());

     

     

    //Assert ---------------------------------

     

    EnqueueConditional(() => submitExecuted);

    EnqueueCallback(() =>

    {

     

     

    Assert.AreEqual(2, viewModel.CustomerData.Count);

     

    });

     EnqueueTestComplete();

     

  • brian.noyes

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by brian.noyes on Nov 29, 2012 14:33

    Its hard to say without seeing the whole code, but you are adding 3 items, then calling DeleteCustomers (which I would presume should be clearing the collection) and then you are asserting that the count is 2. Something doesn't add up there.

  • gnkarthik

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by gnkarthik on Nov 29, 2012 20:07

    Hi Brian,

    Thanks for your immediate reply, I am posting my ViewModel, DomainService & Silverlight Test Class code below. Please let me know if you need anything else.

    // View Model Code
    public void DeleteCustomers()
            {
                if (CustomerData.Count > 0)
                {
                    var selectedData = CustomerData.Where(d => d.IsDelete).ToList<MainPageModel>();
                    if (selectedData.Count > 0)
                    {
                        //MessageBoxResult result = MessageBox.Show("You are trying to delete customers. Are you sure?", "", MessageBoxButton.OKCancel);
                        //if (result == MessageBoxResult.OK)
                        //{
                        string cIds = string.Empty;
                        foreach (MainPageModel change in selectedData.Where(d => d.IsDelete))
                        {
                            cIds = string.Concat(cIds, Convert.ToString(change.CustomerId), ",");
                        }
                        cIds = cIds.TrimEnd(',');
                        CDomainContext.DelCustomers(cIds).Completed += new EventHandler(CustomerDelete_Completed);
                        //}
                    }
                    else
                    {
                        //MessageBox.Show("Please select a customer to delete.");
                        //return;
                    }
                }
                else
                {
                    //MessageBox.Show("No data to delete.");
                    //return;
                }
            }
            private void CustomerDelete_Completed(object sender, EventArgs e)
            {
                GetCustomers();
            }
     
    // Test Class Code
     
    [TestMethod, Asynchronous]
            public void Test_DeleteCustomers()
            {           
                // Arrange ---------------------------------
                bool submitExecuted = false;
                var domainClientStub = new Mock<TestDomainClient>();
                 
                domainClientStub.Setup(d => d.Invoke(It.Is<InvokeArgs>(q => q.ReturnType == typeof(void))))
                    .Callback(() => submitExecuted = true)
                    .Returns(new InvokeCompletedResult(typeof(void)));
     
                List<MainPageModel> testData = new List<MainPageModel>();
                testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "G", CustomerId = 0 });
                testData.Add(new MainPageModel() { FirstName = "Fedrick", LastName = "D", CustomerId = 1, IsDelete = true });
                testData.Add(new MainPageModel() { FirstName = "Steve", LastName = "K", CustomerId = 2 });
     
                // Act -------------------------------------
                MainPageViewModel viewModel = new MainPageViewModel(domainClientStub.Object);
                viewModel.CustomerData = testData;
                EnqueueCallback(() => viewModel.DeleteCustomers());
     
                // Assert ---------------------------------
                EnqueueConditional(() => submitExecuted);           
                EnqueueCallback(() =>
                {
                    Assert.AreEqual(2, viewModel.CustomerData.Count);
                });
     
                EnqueueTestComplete();
     
                //List<MainPageModel> testData = new List<MainPageModel>();
                //testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "guntupalli", CustomerId = 0 });
                //testData.Add(new MainPageModel() { FirstName = "jotsna", LastName = "dammavalam", CustomerId = 1, IsDelete = true });
                //testData.Add(new MainPageModel() { FirstName = "sameer", LastName = "gottipati", CustomerId = 2 });
            }
     
    // Domain Service Code
     
    public void DelCustomers(string ids)
            {
                ObjectContext.DeleteCustomers(ids);
            }

     

  • gnkarthik

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by gnkarthik on Nov 29, 2012 20:15

    Hi Brian,

    Thanks for your immediate reply, I am posting my ViewModel, DomainService & Silverlight Test Class code below. Please let me know if you need anything else.

    // View Model Code
    public void DeleteCustomers()
            {
                if (CustomerData.Count > 0)
                {
                    var selectedData = CustomerData.Where(d => d.IsDelete).ToList<MainPageModel>();
                    if (selectedData.Count > 0)
                    {
                        //MessageBoxResult result = MessageBox.Show("You are trying to delete customers. Are you sure?", "", MessageBoxButton.OKCancel);
                        //if (result == MessageBoxResult.OK)
                        //{
                        string cIds = string.Empty;
                        foreach (MainPageModel change in selectedData.Where(d => d.IsDelete))
                        {
                            cIds = string.Concat(cIds, Convert.ToString(change.CustomerId), ",");
                        }
                        cIds = cIds.TrimEnd(',');
                        CDomainContext.DelCustomers(cIds).Completed += new EventHandler(CustomerDelete_Completed);
                        //}
                    }
                    else
                    {
                        //MessageBox.Show("Please select a customer to delete.");
                        //return;
                    }
                }
                else
                {
                    //MessageBox.Show("No data to delete.");
                    //return;
                }
            }
            private void CustomerDelete_Completed(object sender, EventArgs e)
            {
                GetCustomers();
            }
     
    // Test Class Code
     
    [TestMethod, Asynchronous]
            public void Test_DeleteCustomers()
            {           
                // Arrange ---------------------------------
                bool submitExecuted = false;
                var domainClientStub = new Mock<TestDomainClient>();
                 
                domainClientStub.Setup(d => d.Invoke(It.Is<InvokeArgs>(q => q.ReturnType == typeof(void))))
                    .Callback(() => submitExecuted = true)
                    .Returns(new InvokeCompletedResult(typeof(void)));
     
                List<MainPageModel> testData = new List<MainPageModel>();
                testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "G", CustomerId = 0 });
                testData.Add(new MainPageModel() { FirstName = "Fedrick", LastName = "D", CustomerId = 1, IsDelete = true });
                testData.Add(new MainPageModel() { FirstName = "Steve", LastName = "K", CustomerId = 2 });
     
                // Act -------------------------------------
                MainPageViewModel viewModel = new MainPageViewModel(domainClientStub.Object);
                viewModel.CustomerData = testData;
                EnqueueCallback(() => viewModel.DeleteCustomers());
     
                // Assert ---------------------------------
                EnqueueConditional(() => submitExecuted);           
                EnqueueCallback(() =>
                {
                    Assert.AreEqual(2, viewModel.CustomerData.Count);
                });
     
                EnqueueTestComplete();
     
                //List<MainPageModel> testData = new List<MainPageModel>();
                //testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "guntupalli", CustomerId = 0 });
                //testData.Add(new MainPageModel() { FirstName = "jotsna", LastName = "dammavalam", CustomerId = 1, IsDelete = true });
                //testData.Add(new MainPageModel() { FirstName = "sameer", LastName = "gottipati", CustomerId = 2 });
            }
     
    // Domain Service Code
     
    public void DelCustomers(string ids)
            {
                ObjectContext.DeleteCustomers(ids);
            }

     

  • gnkarthik

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by gnkarthik on Nov 29, 2012 20:17

    Hi Brian,

    Thanks for your immediate reply, I am posting my ViewModel, DomainService & Silverlight Test Class code below. Please let me know if you need anything else.

    // View Model Code
    public void DeleteCustomers()
            {
                if (CustomerData.Count > 0)
                {
                    var selectedData = CustomerData.Where(d => d.IsDelete).ToList<MainPageModel>();
                    if (selectedData.Count > 0)
                    {
                        //MessageBoxResult result = MessageBox.Show("You are trying to delete customers. Are you sure?", "", MessageBoxButton.OKCancel);
                        //if (result == MessageBoxResult.OK)
                        //{
                        string cIds = string.Empty;
                        foreach (MainPageModel change in selectedData.Where(d => d.IsDelete))
                        {
                            cIds = string.Concat(cIds, Convert.ToString(change.CustomerId), ",");
                        }
                        cIds = cIds.TrimEnd(',');
                        CDomainContext.DelCustomers(cIds).Completed += new EventHandler(CustomerDelete_Completed);
                        //}
                    }
                    else
                    {
                        //MessageBox.Show("Please select a customer to delete.");
                        //return;
                    }
                }
                else
                {
                    //MessageBox.Show("No data to delete.");
                    //return;
                }
            }
            private void CustomerDelete_Completed(object sender, EventArgs e)
            {
                GetCustomers();
            }
     
    // Test Class Code
     
    [TestMethod, Asynchronous]
            public void Test_DeleteCustomers()
            {           
                // Arrange ---------------------------------
                bool submitExecuted = false;
                var domainClientStub = new Mock<TestDomainClient>();
                 
                domainClientStub.Setup(d => d.Invoke(It.Is<InvokeArgs>(q => q.ReturnType == typeof(void))))
                    .Callback(() => submitExecuted = true)
                    .Returns(new InvokeCompletedResult(typeof(void)));
     
                List<MainPageModel> testData = new List<MainPageModel>();
                testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "G", CustomerId = 0 });
                testData.Add(new MainPageModel() { FirstName = "Fedrick", LastName = "D", CustomerId = 1, IsDelete = true });
                testData.Add(new MainPageModel() { FirstName = "Steve", LastName = "K", CustomerId = 2 });
     
                // Act -------------------------------------
                MainPageViewModel viewModel = new MainPageViewModel(domainClientStub.Object);
                viewModel.CustomerData = testData;
                EnqueueCallback(() => viewModel.DeleteCustomers());
     
                // Assert ---------------------------------
                EnqueueConditional(() => submitExecuted);           
                EnqueueCallback(() =>
                {
                    Assert.AreEqual(2, viewModel.CustomerData.Count);
                });
     
                EnqueueTestComplete();
     
                //List<MainPageModel> testData = new List<MainPageModel>();
                //testData.Add(new MainPageModel() { FirstName = "karthik", LastName = "guntupalli", CustomerId = 0 });
                //testData.Add(new MainPageModel() { FirstName = "jotsna", LastName = "dammavalam", CustomerId = 1, IsDelete = true });
                //testData.Add(new MainPageModel() { FirstName = "sameer", LastName = "gottipati", CustomerId = 2 });
            }
     
    // Domain Service Code
     
    public void DelCustomers(string ids)
            {
                ObjectContext.DeleteCustomers(ids);
            }

     

  • gnkarthik

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by gnkarthik on Dec 13, 2012 19:16

    Hi Brian,

    Do you have any help for me on the above piece of code?

  • brian.noyes

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by brian.noyes on Dec 13, 2012 23:44

    Nothing jumps out from glancing over the code and I'm afraid I don't have time to debug other people's code for free.

  • -_-

    Re: WCF RIA Services Part 8 - Testing and Debugging


    posted by on Dec 14, 2012 04:52
    This article is truly relevant to my study at this moment, and I am really happy I discovered your website. http://merkamovil.com/

Add Comment

Login to comment:
  *      *       

From this series