Welcome to MSDN Blogs Sign in | Join | Help

SPG Continuous Integration builds not run on instance of SharePoint

Our Continuous Integration builds are run on a build server that has the necessary SharePoint assemblies, but with no installed instance of SharePoint.

Licensing issues aside, this causes an interesting problem when unit tests are developed on dev boxes that have SharePoint installed. Several services found in the SPG Partner Portal application access SPFarm.Local. Developers often fail to mock out this dependency in unit tests as this property is always available on boxes that have SharePoint installed. These unit tests ran fine on dev boxes, but fail to run on the build server and it took some time to figure out why.

As part of good unit testing, remember to mock out any of your code’s dependent objects that could potentially cause problems with your tests. Here is a simple example of how we use Typemock Isolator to mock the SPFarm.Local property.

 

SPFarm fakeSPFarm = Isolate.Fake.Instance<SPFarm>();
Isolate.WhenCalled(()=>SPFarm.Local).WillReturn(fakeSPFarm);

//Arrange
//Act
//Assert
Posted by fcheung | 2 Comments

Using Typemock Isolator for Integration Tests

Yeah, I know. Typemock Isolator is great for unit testing against API’s that are hard to mock, but this last iteration, I found a new use for Typemock Isolator.

I worked with our test team to develop a set of integration tests that are run by MSTest and simulate running our components in the context of a specific web app. Usually in SharePoint, this is done as “over the glass” tests that exercise the components app via some UI because it is difficult to setup the appropriate SFarm/SPWebApplication/SPSite/SPWeb environment for integration tests. This is especially difficult if the code you are testing internally access SPFarm.Local or SPContext.Current.

Let’s assume you have already constructed an SFarm/SPWebApplication/SPSite/SPWeb environment. The trick now is to direct calls to SPFarm.Local and SPContext.Current to your environment. This is where Typemock Isolator comes in.

using(SPSite spSite = new SPSite("[URL to integration test SPSite instance]")
{
    SPContext spContext = SPContext.GetContext(spSite);
    Isolate.WhenCalled(()=>SPContext.Current).WillReturn(spContext);

    //Arrange
    //Act
    //Assert
}

The preceding code illustrates how you can redirect calls to SPContext.Current to an SPContext of your chosing. You can also redirect calls to SPFarm.Local using the following code:

using(SPSite spSite = new SPSite("[URL to integration test SPSite instance]")
{
    
    Isolate.WhenCalled(()=>SPFarm.Local).WillReturn(spSite.WebApplication.Farm);

    //Arrange
    //Act
    //Assert
}

This is necessary if the SFarm/SPWebApplication/SPSite/SPWeb environment is not on the same box that is running the integration tests.

Posted by fcheung | 1 Comments

What happened to my SPContext?

This last iteration, we ran an interesting issue when services that we request through the SharePointServiceLocator depend on SPContext.Current. This seemed like a reasonable dependency assumption. We consume these services in feature receivers. They work fine when activating the feature via the SharePoint web UI, but don’t work when activating the feature via STSADM –o activatefeature command. Who knew SPContext isn’t available in this case? Not I. This is another example of the complexity of SharePoint development.

Luckily, if you’re on a SharePoint/WSS instance SPFarm.Local is always available, even if SPContext is not. We refactored the SharePointLogger, EventLogLogger and TraceLogger to ensure they don’t depend on SPContext. This forced us into an artificial restriction. We wanted the EventLogLogger to pull the name of the event source to write to from the current SPWebApplication’s property bag. In order to determine the current SPWebApplication we started with SPContext. We believe this to be the appropriate level to set the event source name since it is very likely that different web applications in the farm would like to use different event log event sources. The SharePointServiceLocator is also “safe” to use in feature receivers because its only external dependency is on a property bag setting on SPFarm.

I know that we can get to the SPWebApplication from the feature receiver’s SPFeatureReceiverProperties, but then we need to refactor services to sometimes get their context from SPContext and other times allow it to be passed in via property. Hopefully that service isn’t used before the necessary property is set. Messy… Let me know if anyone out there has a recommendation on how to solve this problem.

Posted by fcheung | 0 Comments

Stack Overflow when building up SiteMap structure

In iteration 10, we wrote HierarchicalConfigSiteMapProvider that builds up a tree of sitemap nodes based on xml retrieved from the hierarchical ConfigManager. Being a newbie with SiteMapProviders, I overwrote BuildSiteMap calling a simple recursive method that traverses the xml and builds corresponding sitemap nodes. All of a sudden I get a stack overflow exception. No problem, I thought. Probably a bug in my recursive method. Hmmm… Looks fine. :|

Turns out, I was calling siteMapNode.ChildNodes.Add(myNewSiteMapNode). This didn’t seem so crazy at the time. The problem is that the ChildNodes property actually calls GetChildNodes, which calls BuildSiteMap… They tell two friends, and they tell two friends. Boom. Stack Overflow. Tess Ferrandez blogs about this also but this issue it is worth mentioning again since I’m sure lots more people will run into this.

The solution: Instead of using the ChildNodes property, you should use SiteMapProvider.AddNode. This is also documented in this article on SiteMapProviders.

Posted by fcheung | 2 Comments

Granularity of SharePoint Features and Solutions

In our SPG v1 guidance we discussed packaging site elements into features. This is particularly important if you are using the Visual Studio extensions for WSS. We found that whenever you add a site element into a VSeWSS project, a new feature is created for the site element. This lead to an excessive number of features that were too fine grained. Our guidance is to combine features into chunks of functionality that are generally self contained. You may still have some feature dependencies, but far fewer than if you had each site element in its own feature. You should expect to have just a few features in a WSP. But that leads to the next question: how granular should I package my WSPs?

I would imagine that if you are not using VSeWSS, you are likely to have just a few WSPs. Your decisions around how to organize your WSP likely depend upon release schedule and the need to run side by side (e.g. worflows). Now if you are using VSeWSS, you have another consideration. VSeWSS allows for right-click deployment and F5 debugging. This is a great feature, but what happens when you have a ton of web scoped features that are meant to be activated on different SPWebs? Same goes for features at other scopes. Right-click deploy (and F5 debugging) will deploy the WSP and activate all the features on the web URL specified in the “Debug –> Start browser with URL” value in the project’s properties. This is a great feature but if you don’t optimize your WSPs for this experience, you’ll have features activated in places you didn’t expect.

In our SPG v2 guidance, we are considering what our guidance should be in this area. Perhaps if you are using VSeWSS, you should optimize your WSPs for right-click deploy. In this case, you may have one WSP per SPWeb with unique functionality. But what happens when you have a site collection scope feature you want to deploy to many of these webs but these webs live in their separate site collections? Should the site scoped feature live in its own WSP? Hmmm…

Perhaps if you are not using VSeWSS you should optimize based on release schedule and need to run side-by-side. Not sure yet. If you have an opinion, please comment on this blog.

Posted by fcheung | 1 Comments

SPG v2 Iteration 10 Drop

Iteration 10 is complete and the drop can be downloaded here.

In iteration 10 we mainly concentrated on refactoring, code cleanup and reusable assets. We will continue refactoring and scrubbing the code in the remaining iterations but please take a look at what we've done so far and provide us feedback.

  • Hierarchical configuration: the ability to set a value on an element in the site map and have that value accessible by child elements in the site map. (ConfigManager.cs)
  • Global Navigation driven by config (HierarchicalConfigSiteMapProvider.cs)
  • Generic Service locator (ServiceLocator.cs)
  • Applied V1 theme and use HierarchicalConfigSiteMapProvider in global navigation
  • Subsite creation workflow activities (Microsoft.Practices.SPG.SubSiteCreation.Workflow project)

Hierarchical Configuration:

Not to be confused with Hierarchical Object Store which uses SPPersistedObject to build a hierarchy of elements to store configuration data, our notion of “Hierarchical Configuration” describes the ability to retrieve a configuration setting and not know where in the hierarchy that information was persisted. The information could be set at the SPFarm and therefore be available to all code running in that scope. Same goes for the SPWebApplication, SPSite, and SPWeb.

We also support the ability to over-ride a value set higher in the hierarchy. For example, a config setting could be persisted in the SPWebApplication, thus making it available to all SPSites and SPWebs underneath it. However, you could also persist a setting using the same key in an SPSite or SPWeb.

We implemented “Hierarchical Configuration” with Microsoft.Practices.SPG.Common.Configuration.ConfigManager and the various implementations of IPropertyBag (SPFarmPropertyBag, SPWebAppPropertyBag, SPSitePropertyBag, and SPWebPropertyBag).

For an example of how we utilize Hierarchical Configuration, take a look at the Contoso.PSS.GlobalNavigation project. In WebAppFeatureReceiver we use ConfigManager to persist the sitemap xml into the SPWebApplication.

configManager.SetByKey("Microsoft.Practices.SPG.Common.Navigation.NavigationXml", Resources.DefaultSiteMapXml, webApp);

This value is retrieved by the HierarchicalConfigSiteMapProvider:

string siteMapXml = configManager.GetByKey<string>("Microsoft.Practices.SPG.Common.Navigation.NavigationXml");

I’ll go into more detail on the HierarchicalConfigSiteMapProvider later, but you can see from this example that a config setting is persisted in the WebApp and then retrieved by our business logic. The ConfigManager.GetByKey method starts at the current SPWeb and queries the property bag with the key. If the key is not found, the SPWeb’s parent SPSite is queried. If the key is not found in the SPSite, the parent SPWebApplication is queried, and so forth up to the SPFarm.

Global Navigation through HierarchicalConfigSiteMapProvider

In a previous iteration, we implemented global navigation through deploying modifications to layouts.sitemap and merging those modifications by calling SPWebService.ApplyApplicationContentToLocalServer() or the corresponding STSADM command copyappbincontent. This approach mainly demonstrated what could be done out of the box to configure navigation in SharePoint. We then used the out-of-the-box XmlSiteMapProvider which is typically already configured in web.config to read from layouts.sitemap. The downside to this approach was that stsadm –o copyappbincontent or SPWebService.ApplyApplicationContentToLocalServer() would need to be executed on every web front end in the farm to update all layouts.sitemap files. In order to move to an approach that is easier to deploy across a farm, we went with a custom site map provider.

The HierarchicalConfigSiteMapProvider extends StaticSiteMapProvider and loads site map nodes on initialization, thus this custom site map provider is appropriate for cases where navigation changes very infrequently.

As mentioned above, the HierarchicalConfigSiteMapProvider leverages Hierarchical Configuration and retrieves sitemap xml from the ConfigManager using the key: “Microsoft.Practices.SPG.Common.Navigation.NavigationXml“. When first initialized, the HierarchicalConfigSiteMapProvider creates a sitemap structure based on the sitemap xml retrieved from the ConfigManager.

The Contoso.PSS.GlobalNavigation WebAppFeatureReceiver adds the appropriate nodes to web.config, saves Contoso PartnerPortal specific sitemap xml to the SPWebApplication, and adds a delegate control that will get picked up by default.master.

Generic Service Locator

In SPG v1, the ServiceLocator could self initialize by knowing about Contoso services that would be necessary for the application to run. In this drop, we’ve made the ServiceLocator much more generic. Now, feature receivers can register their own service implementations, adding to or potentially over-writing previous service registrations.

There are many examples throughout the Contoso.PSS reference implementation for how to use the ServiceLocator to get an instance of a service. This functionality has changed little since SPG v1. However take a look at the Contoso.PSS.Portal project’s WebFeatureReceiver FeatureInstalled method for an example of how this feature is registering type mappings using ServiceLocatorConfig.

Applying V1 Theme

In a previous iteration, we implemented a custom look and feel via custom masterpage and CSS. We decided to use the ContosoTheme feature we developed in SPG v1 (Contoso.RI.UpdateTheme) instead of the custom masterpage and CSS. One advantage of using a theme is that system and application pages can also be branded.

SubSite Creation Workflow Activities

In a previous iteration, we implemented the subsite creation process but realized that the process of creating subsites may need to be customized or extended. This iteration, we refactored the subsite creation process into several workflow activities (Microsoft.Practices.SPG.SubSiteCreation.Workflow). We also provide a workflow that stitches these workflow activities together (SubSiteCreation.cs). The workflow activities are:

  • ResolveSiteTemplate: This activity determines the appropriate site template to use when the sub site is created.
  • CreateSubSite: This activity creates the top level or root site based on the business event type (e.g. /Incidents or /OrderExceptions). This activity then uses the site template resolved previously and creates a site based on this site template and locates the subsite under the top level site. This activity then sets an identifier on the site that relates the subsite to the business event and value (e.g. IncidentId=123 or OrderExceptionId=321).
  • SynchronizeStatus: This activity sets the status of the business event onto the newly created subsite.

The Microsoft.Practices.SPG.SubSiteCreation SharePoint solution project packages the above worflow and activities into features.

Posted by fcheung | 1 Comments

SharePoint Development Guidance v2

We are now full speed a head working on v2 of the p&p SharePoint Guidance. Version 1 focused on issues common to both WSS and MOSS development like unit testing, deployment, and upgrading. For version 2, we’re looking at the following topics:

  • Performance and scalability
    • MOSS caching
    • ASP.NET cache
    • SharePoint session state
  • Data and services
    • Consuming WCF services
    • BDC integration
    • Exposing custom services from SharePoint app
  • Content driven apps
    • ALM

We’ll also be using Visual Studion extensions for WSS version 1.3, which among other features, has command line support which should greatly simplify our need to generate a WSP from source code on the build server.

Look for code drops and other details at http://www.codeplex.com/spg

Posted by fcheung | 0 Comments

Sometimes Typemock Isolator can be too powerful

A colleague made the comment regarding Typemock Isolator: “but be aware that having such a powerful mocking facility may reduce the effectiveness of your unit tests as design aids”.

I’ve used Typemock Isolator and it allows you to test your business logic even if it is poorly factored. For example, let’s say you have some legacy code that creates an instance of a dependant service using the “new” operator.

public class MyClass
{
    //Constructor
    public void MyClass()
    {
        this.dependantServiceA = new DependantServiceA();
        ...
    }
}

Typemock Isolator will allow you to mock out the call to the dependant service’s constructor. This is a really handy feature but should be used in conjunction with aggressively refactoring the code, passing in the dependant service through an interface instead of having a hard coded dependency on the concrete implementation.

public class MyClass
{
    //Constructor
    public void MyClass(IDepdantServiceA dependantServiceA)
    {
        this.dependantServiceA = dependantServiceA;
        ...
    }
}

If your code is being used in a framework that requires your class to have a default constructor, you can use a service locator to get access to dependant services.

public class MyClass
{
    //Constructor
    public void MyClass()
    {
        this.dependantServiceA = ServiceLocator.Get<IDependantServiceA>();
        ...
    }
}

The moral to this story is… Just because you can unit test your code with Typemock Isolator doesn’t mean that your code is well written and well factored.

Posted by fcheung | 2 Comments

Trying to use Unit Tests as Integration Tests

We shipped v1 of the SharePoint Guidance with a good set of unit tests. These unit tests paid for themselves several times over each time we refactored the code base. As an agile developer, this made me very happy.

After we shipped, my project manager, wanted a set of integration tests that could be used to stress test the application, so he asked me if we could repurpose our unit tests to also run as integration tests. Crazy idea eh?

Unit tests are great to assert the behavior of granular pieces of functionality. Optimally, unit tests are written so that only the granular piece of functionality is tested. Dependant services and systems are replaced with stubs or mocks. Unit tests written this way are short and readable. The code’s functionality is clear by reading the unit test. However since dependant systems and services are not actually called in these unit tests, they don’t work very well as integration tests.

Here is an example unit test:

[TestMethod]
[ExpectedException(typeof(SPException), "The Enrollment Deadline Date can not be before today's date.\r\n")]
public void AddingCourseWithInvalidEnrollmentDateCancelsWithError()
{
    //Setup our mock so that our enrollment date is invalid
    TrainingCourse course = new TrainingCourse
    {
        Title = "My Title",
        Code = "12345678",
        EnrollmentDate = DateTime.Today.AddDays(-1),
        StartDate = DateTime.Today.AddDays(1),
        EndDate = DateTime.Today.AddDays(2),
        Cost = 100
    };
    SPWeb web = CreateMockSPWeb();

    TrainingCourseRepository repository = new TrainingCourseRepository();
    repository.Add(course, web);
}

If we wanted to leverage this code as an integration test, we would need to modify the CreateMockSPWeb method to return a mock when run as a unit test, and return a real SPWeb from a live instance of SharePoint when run as an integration test. Sounds pretty simple, until you realize all the setup and teardown code that needs to be written. There is a point where you add so much code to your unit tests, making them less readable and maintainable, that it makes more sense to just write a separate set of integration tests. We quickly reached this point and decided to invest in writing a set of integration tests.

Here is an example integration test that asserts the same functionality as the unit test above:

[TestMethod]
[ExpectedException(typeof(SPException), "The Enrollment Deadline Date can not be before today's date.\r\n")]
public void AddingCourseWithInvalidEnrollmentDateCancelsWithError()
{
    //Setup our mock so that our enrollment date is invalid
    TrainingCourse course = new TrainingCourse
    {
        Title = "My Title",
        Code = "12345678",
        EnrollmentDate = DateTime.Today.AddDays(-1),
        StartDate = DateTime.Today.AddDays(1),
        EndDate = DateTime.Today.AddDays(2),
        Cost = 100
    };

    using (SPSite site = new SPSite(siteUrl))
    {
        using (SPWeb web = site.AllWebs[webId])
        {
            TrainingCourseRepository repository = new TrainingCourseRepository();
            repository.Add(course, web);
        }
    }
}

We shipped several integration test projects together with our Build Verification Tests. Please note that integration tests are typically written by developers to validate that their code integrates correctly with other sub systems. Integration tests run “below the glass”, executing code using the API. BVT’s on the other hand, are typically written by testers and run “above the glass”, executing code through the application’s UI.

We

Posted by fcheung | 0 Comments

SHIPPED!!! SharePoint Guidance

We’ve completed version 1 of the patterns & practices SharePoint Guidance. The major stories we covered in this release are:

  • Architectural decisions about patterns, feature factoring, and packaging.
  • Design tradeoffs for common decisions many developers encounter, such as when to use SharePoint lists or a database to store information.
  • Implementation examples that are demonstrated in the Training Management application and in the QuickStarts.
  • How to design for testability, create unit tests, and run continuous integration.
  • How to set up different environments including the development, build, test, staging, and production environments.
  • How to manage the application life cycle through development, test, deployment, and upgrading.
  • Team-based intranet application development.

For those eXtreme Programmers out there, check out the guidance on Unit Testing. If you’re working on a SharePoint application that needs some updates and modifications, be sure to check out the guidance on Upgrading the Training Management Application.

Big thanks goes to our friends from Avanade, Charles Cho, Alex Nichols and Mike Chorey. Also, special thanks to Rob Bogue for keeping us honest. Great job Team!!!

Posted by fcheung | 1 Comments

Typemock ships Isolator for SharePoint

Our friends at Typemock have made it much more affordable to unit test SharePoint applications. With an introductory price of $99, they have shipped Isolator for SharePoint which is basically the same the full Isolator product except that it will only "fake" APIs that are part of the SharePoint API.

 http://www.typemock.com/unit-testing-Microsoft-SharePoint.html 

Thanks Eli and Roy!

 

Unit Testing SharePoint 2007 Applications

When developing SharePoint applications, it is common to develop custom:

  • ASP.NET Web Forms
  • WebParts
  • Event Receivers
  • Workflow coding activities

One of the first things we noticed when trying to unit test our business logic is that our code will heavily relied on elements of the SharePoint API.

  • SPWeb
  • SPItemEventProperties
  • SPList

A common approach to making code more testable is to identify and isolate services that a class needs to do its work, treat them as external dependencies, and passed them into the class. If these dependencies are passed in instead of being internally constructed, we have the opportunity to pass in a substitute.

By controlling the behavior of the substitute we can force specific branches of our code to be executed. This process of creating controlled substitutes for dependent services is referred to as mocking (http://en.wikipedia.org/wiki/Mock_Object).

It is often very advantageous to use a mock instead of the real service. The classic example is where our business logic handles a “Database Full” exception thrown by a dependent service. We obviously wouldn’t want to simulate that by filling up the service’s database. Rather, we would like to have our mock service simply throw the exception on demand. Unit testing our business logic is basically validating how our code interacts with dependent services. By using mocks in place of the real implementations, we can effectively simulate any condition and examine our code's response to those conditions.

So what’s the problem? Why is it so hard to unit test SharePoint applications?

Let’s treat elements of the SharePoint API such as SPWeb and SPList as dependencies that we want to mock. When we try creating a mock of any of these elements we quickly run into a few challenges.

  • Interfaces are rarely used: If our code that depends on an SPWeb could instead depend on an ISPWeb interface (assuming that SPWeb implemented ISPWeb), we could easily create a mock implementation of ISPWeb.  We could pass our mock into our code instead of an SPWeb. We could define the exact behavior of the mock. However, since SharePoint elements such as SPWeb, SPList, and SPItemEventProperties don’t implement public interfaces, this is not an option.
  • Sealed classes: The SharePoint elements mentioned above are also sealed. Another mocking technique is to extend the type you want to mock overriding all the base class’s methods. Instances of the mock type can be substituted for the instances of the base type. However, since many SharePoint elements including the ones mentioned above are sealed, this technique is also not an option.
  • Internal Constructor: Even if there were no behavior in our dependent service that we need to override, we still would need to create an instance of it. Again, many SharePoint elements including the ones mentioned above have internal constructors so we are not expected to new up our instances, but rather get them for calling the SharePoint API.
  • Collections with no Add method: Many of the collections provided by SharePoint such as SPItemEventDataCollection do not have a public Add method. This makes it difficult to mock if we can’t populate an instance of this collection with a controlled set of values.

Bottom Line… the SharePoint API is Hard To Mock!!!

Unit Testing Options

  1. Status Quo: Hard dependency on a SharePoint instance. Many people who try to unit test their SharePoint applications simply accept the SharePoint dependencies and run their unit tests against a live instance of SharePoint.
    • One challenge with this approach is that these “unit” tests are really more like integration tests in that the success of the test depends upon many systems functioning and configured correctly. For example, if there were a problem with the SharePoint database, your unit tests would likely fail, even if the tests are not related to persistence.
    • Another challenge with this approach is performance. Unit tests can provide the developer with timely feedback. If the unit tests have to run against a live instance of SharePoint, they likely will take considerably longer to run and development may be slowed as a result. (Best case scenario is to have the entire suite of unit tests run after every check-in, and to not make another check-in until failing tests have been resolved. This makes it clear what modification introduced a problem.)
    • If unit tests are run against a live instance of SharePoint, there is considerable work that needs to be done to reset the environment to a known state so that further test runs will perform as expected. If this work fails, tests will fail unexpectedly.
  2. Wrapper/Façade: Another option is to provide an abstraction to the SharePoint API that can be mocked. A wrapper is an abstraction that provides a mirror of the SharePoint API. A façade is an abstraction that simplifies the SharePoint API. Either approach takes a lot of work and may be very difficult to do well.
  3. Typemock Isolator: Typemock Isolator provides the ability to create a mock instance of type, even if it is sealed and internally constructed.
    We have successfully built unit tests leveraging Typemock Isolator with over 80% code coverage of our TrainingManagement reference implementation business logic.

Strategies on Unit Testing SharePoint Applications

  1. Model-View-Presenter to test web forms
    The MVC and MVP patterns we created to help separate business logic from view logic. The main reason for separating the business logic from the view logic is to improve testability.
  2. Use repositories to encapsulate access to SPList instances
    We created abstractions to the Training Course and Registration SharePoint lists following the Repository pattern.  We then refactored our business logic to use the TrainingCourseRepository and RegistrationRepository instead of going through the SharePoint API to perform create, read, update & delete operations. Our unit tests simply use mock versions of the repositories thus reducing the need mock the SharePoint API.
  3. Use Typemock to mock calls to SharePoint API
    Even though the repositories reduced the need to mock the SharePoint API, there are many SharePoint elements besides SPList that need to be mocked. Typemock Isolator is used for mocking dependencies that are difficult to mock.
Posted by fcheung | 10 Comments

SHIPPED!!! Composite Application Guidance for WPF June 2008

Here is the landing page:
http://msdn.microsoft.com/en-us/library/cc707819.aspx

Here is the download page:
http://www.microsoft.com/downloads/details.aspx?FamilyId=6DD3D0C1-D5B4-453B-B827-98E162E1BD8D&displaylang=en

I didn't notice how much hard work went into this project because I was having so much fun. Pair programming with the likes of Bob, Julian, Ezequiel, Brian, and Adam was an amazing experience.

Thanks to Bob Brumfield for his outstanding technical leadership. Many of the refinements of the of how we implemented patterns such as MVP were lead by Bob.

Thanks to Glenn Block for organizing a highly involved advisory board and for standing up for the needs of the customer.

Thanks to Blaine Wastell for cracking the whip, keeping us on schedule and keeping our backlog filled and prioritized.

Thanks to our awesome WPF experts: Brian Noyes and Adam Calderon who taught me the difference between a Panel control and a hole in the wall. :)

Thanks to our fabulous test team: Larry Brader, Prasad Paluri, Arun Subramonian Namboothiri, & Gokul Janardhanan for keeping us honest, helping driving quality, and for automating all the acceptance tests.

Thanks to the awesome developers from Southworks: Julian Dominguez & Ezequiel Jadib. I want to thank Julian specifically for his hard work and patience. I learned so much sitting between Julian and Bob, I will forever be in their debt.

Last but not least is our wonderful documentation team: Nelly Delgado, , Mariano Szklanny, and Diego Poza. Thank you for helping make sense out of our random thoughts and chicken scratchings.

Thank you all for your time and hard work!

As for myself, I'll be moving on to another project and another technology. I'll be working with Blaine to help put together some much needed guidance on developing applications using SharePoint 2007 technologies.

Posted by fcheung | 1 Comments

ActiveAware Views and Commands

It occurred to me after a recent advisory board call that we haven't done a good job of communicating the scenarios that prompted the development of ActiveAware views and commands. Let me describe this functionality in terms of the ability to "Save All" versus "Save Active".

Save All:

The ability to "Save All" describes saving ALL elements of an application that have registered as "saveable". My application could have orders, customers, inventory, and other entities that could be edited and therefore need to be saved. Let's say my application allows me to edit multiple orders, customers, and inventory items simultaneously. If I were to invoke the SaveAll command, I would expect that every edited order, customer, and inventory item gets saved. In addition, since WPF commands provide a mechanism for commands to communicate enablement from the command handler to the invoker, I would also like my SaveAll command invokers to be enabled only if every open order, customer, and inventory item is in a state that can be saved.

Save Active:

Let's say my application has a tab control where I add many views. I've opened several orders, customers and inventory items, each with its own tab. Only one tab can be selected at a time. If I were to invoke the Save command, I would expect that only the selected order, customer, or inventory item would respond. In addition, I would like only the selected item to participate in the enablement of the Save command invoker(s). Selecting a tab for an item that is failing validation and therefore not saveable results in disabling the Save command invoker(s). Selecting a tab for an item that is in a saveable state results in the Save command invoker(s) being enabled.

The Save Active scenario may be solved using WPF RoutedCommands. However, as I've mentioned in a previous blog, we stayed away from RoutedCommands in favor of a more direct method of command invoker to handler routing. (One scenario that we support is the ability to have command handlers live in controllers, presenters, or presentation models. In the case of a controller providing a command handler, there may be no UI element that could provide a CommandBinding. Also, we may want to invoke the Save or SaveAll commands on items that does not necessarily have keyboard focus.)

Let's get back to Save Active. What we really need is to have container controls such as selectors communicate to us which of its child views is selected so that we can relay that information to the selected view's presentation model. The selected view's presentation model owns has handlers for the Save command. In the case where the selected view is actually a composite view, each of the child views would also need to receive the notification.

We discussed the name "ActiveAware" several times. We also considered names using "Selected" and other verbs. Finally we accepted that the initial act of selecting a view in a selector control (e.g. TabControl) allows us to know which of the selector's direct children is selected. After that, it made more sense to "Activate" rather than "Select" the child views/presenters/commands.

Posted by fcheung | 0 Comments

Poor man's UI Composition

I've consistently pushed for simplicity on the Prism project. This was evident when we worked on our first feature: UI composition (View from one module is composed of view(s) from other module(s)). In CAB, we developed Workspaces to provide this functionality. However WPF provided its own containment model. Being able to add arbitrary content to UI elements is core to WPF. Before we develop an elaborate solution for gluing cross module UI elements together, it was important to see what WPF provided out of the box.

As I mentioned in a previous post, modules can share services instances using a service locator. If we generalize this a bit, we can use the service locator to share other things such as views, models, and presenters/presentation models. Take a look at the StockTraderRI. The Market module provides implementations of ITrendLinePresenter and ITrendLineView. The Position module has a PositionSummaryView and corresponding PositionSummaryPresentationModel. The PositionSummaryView is composed of several elements, one being a trend line chart view. The trick here is that the Position module doesn't have an implementation of ITrendLineView, instead it relies on the Market module to provide this functionality.

MarketModule.cs:

protected void RegisterViewsAndServices()
{
    _container.RegisterType<IMarketHistoryService, MarketHistoryService>();
    _container.RegisterType<IMarketFeedService, MarketFeedService>(new ContainerControlledLifetimeManager());
    _container.RegisterType<ITrendLineView, TrendLineView>();
    _container.RegisterType<ITrendLinePresenter, TrendLinePresenter>();
}

This does add a dependency. The module that provides ITrendLinePresenter and ITrendLineView (MarketModule) needs to be loaded prior to the consumer (PositionModule). This does not mean that the PositionModule depends on the MarketModule specifically, but that the PositionModule depends on module(s) that provide implementations of dependent services/views/etc. The PositionSummaryPresentationModel consumes the TrendLinePresenter through constructor injection. All of the constructor parameters are treated as dependencies by the dependency injection container and are resolved by the container and then passed into the constructor.

public PositionSummaryPresentationModel(IPositionSummaryView view, IAccountPositionService accountPositionService
                                , IMarketFeedService marketFeedSvc
                                , IMarketHistoryService marketHistorySvc
                                , ITrendLinePresenter trendLinePresenter
                                , IOrdersController ordersController
                                , IEventAggregator eventAggregator)
        {
        View = view;
...
        View.Model = this;
        _trendLinePresenter = trendLinePresenter;
        View.ShowTrendLine(trendLinePresenter.View);

        //Initially show the FAKEINDEX
        trendLinePresenter.OnTickerSymbolSelected("FAKEINDEX");
...
        }

The PositionSummaryPresentationModel's constructor takes an IPositionSummaryView, sets itself into the Model property of the view, and then sets the trendLinePresenter's view into the IPositionSummaryView. The PositionSummaryPresentationModel can then call methods on the trendLinePresenter such as providing its initial state.

Basically what you have here is 1) basic cross module communication and 2) a module composing its view by consuming views/presenters/presentation models from other modules. This is often referred to as "Pull" composition, where resources are being pulled and aggregated together. The "composer" uses the service locater to discover resources and then assembles them together into a reasonable UI. This method does not rely on anything other than WPF's built in composition model and a service locater. Later, I'll talk about how we support a "Push" style composition with Regions.

Posted by fcheung | 1 Comments
More Posts Next page »
 
Page view tracker