ADO.NET (WCF) Data Services Paging – Where is SetEntitySetPageSize Method?

by James Richards April 01, 2010

When creating an ADO.NET or WCF Data Service, you can set up server based paging of the data by calling the SetEntitySetPageSize method on the config object as documented here and discussed in Scott Hanselman’s recent post on OData.

But after adding a new service to my project I tried to do this and the SetEntitySetPageSize method did not show up in Intellisense.

image

[more]

It turns out that when you create a new service the auto-generated code for the InitializeService method takes an IDataServiceConfiguration interface parameter, but the SetEntitySetPageSize method is defined on the DataServiceConfiguration class. So remove the “I” from the parameter type and try again.

image

 

Try something like config.SetEntitySetPageSize(“*”, 25) to set the page size to 25 entities per request for all entity types.

But there is still a problem. If you compile the project now and run a query against the service you will get the generic error message “The server encountered an error processing the request. See server logs for more details.”

To see a more detailed error message, add the [System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults=true)] attribute to the class and a config.UseVerboseErrors = true line to the InitializeService method.

image

Now when I run the query again I can see the cause of the problem: “Server paging cannot be used when the MaxProtocolVersion of the data service is set to DataServiceProtocolVersion.V1.”

To solve this we need to set the config.DataServiceBehavior.MaxProtocolVersion to V2, also shown in Scott’s post.

And now I have the Server Data Paging working! Here is the final version of the InitializeService method:

image

[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class AdventureWorksService : DataService<AdventureWorksEntities>
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config)
    {
        config.UseVerboseErrors = true;
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
        config.SetEntitySetPageSize("*", 25);
        config.DataServiceBehavior.MaxProtocolVersion =
            System.Data.Services.Common.DataServiceProtocolVersion.V2;
    }
}

I hope this helps someone who’s encountering the same problem.

Special thanks to Phani Raju for answering my question about this on the WCF Data Services MSDN Forum.

Additional References

Tags: , , ,

.NET | WCF

Implementing Model-View-ViewModel with the Bing Maps (Virtual Earth) Silverlight Map Control

by James Richards September 28, 2009

Overview

I’ve been looking at the Bing Maps (Virtual Earth) Silverlight Map Control and thinking about how to use it in a Silverlight application that implements the Model-View-ViewModel pattern. If you’re not familiar with this pattern, check out Shawn Wildermuth’s MSDN Article Model-View-ViewModel in Silverlight 2 Apps.

I’ve created a simple proof of concept application that implements the pattern with two Views: a Map based View and a Data Grid based View. The application displays a map of well known surf spots in the Los Angeles area, along with some basic information about each spot in an accompanying data grid.

image

[more]

There is no live example posted for this one, as Microsoft has not yet released the Silverlight Map Control with a “Go Live” license. But you can download source code from here to try it out on your own system.

Synchronizing Multiple Views with the ViewModel

Both Views work off the same ViewModel and stay synchronized via events that the ViewModel fires back to the Views after user gestures.

image

There are two main types of user gestures where this comes into play:

  1. When the map is first loaded or the user pan/zooms the map, the ViewModel is notified of the new map extent. The ViewModel then loads surf spots that fall within the new Map Extent from a Repository. An event is fired back to both Views and they update themselves to display only the surf spots that were loaded based on the new map extent.
  2. When a user clicks on a surf spot in either the map or the list, the ViewModel is notified of the newly selected surf spot. An event is fired back to both views and they update themselves to display the selected spot. 

In the next section, I’ll take a closer look at the first example.

Loading Surf Spots from the Repository

The xaml file for the SurfSpotsMapView markup contains just a Map with a specified ViewChangeStart event.

SurfSpotsMapView.xaml

<UserControl x:Class="Map.Client.Views.SurfSpotsMapView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:m="clr-namespace:Microsoft.VirtualEarth.MapControl;assembly=Microsoft.VirtualEarth.MapControl"
    mc:Ignorable="d"
    d:DesignWidth="500"
    d:DesignHeight="500">
    <Grid x:Name="LayoutRoot" Background="White">
        <m:Map Name="Map1" ViewChangeStart="Map_ViewChangeStart">          
        </m:Map>
    </Grid>
</UserControl>

The ViewChangeStart event fires whenever the map extent is about to change. This event generally coincides with the beginning of the animation to the new map extent. This provides an excellent opportunity to start loading the surf spots while the map is doing its animation thing.

private void Map_ViewChangeStart(object sender, MapEventArgs e)
{
    MapViewSpecification mvs = Map1.TargetView;
    LocationRect bounds = Map1.GetBoundingRectangle(mvs);
    viewModel.LoadSurfSpots(bounds.North, bounds.West, bounds.South, bounds.East);
}

In the Map_ViewChangeStart event, the map’s TargetView property contains the MapViewSpecification of the map extent that the map will be animating (panning or zooming) towards. The method examines the bounding rectangle of the target MapViewSpecification and asks the SurfSpotsViewModel to load the surf spots within that map extent via the LoadSurfSpots method.

Notice that the method just asks the ViewModel to load the surf spots, but it doesn’t expect an immediate response. Later, when the ViewModel has finished loading the data, it fires off an event telling both Views to refresh themselves with the new data. This allows for data to be loaded asynchronously, typically from a remote web service.

The SurfSpotsMapView class has a ViewModel property setter where we wire up the events that are expected to be fired from the ViewModel. This is called when the View is first created:

public SurfSpotsViewModel ViewModel
{
    get { return viewModel; }
    set
    {
        viewModel = value;

        // Wire up the View Model
        viewModel.SurfSpotsLoaded += new EventHandler(SurfSpotsLoadedHandler);
        viewModel.SurfSpotsLoadError += new EventHandler(SurfSpotsLoadErrorHandler);
        viewModel.SelectedSurfSpotChanged += new EventHandler(SelectedSurfSpotChangedHandler);
    }
}

Like the ViewModel, the repository is also asynchronous in nature and the ViewModel’s LoadSurfSpots method simply delegates to an analogous method on the repository. Consider the following ISurfSpotsRespository interface:

public interface ISurfSpotRepository
{
    // Load all surf spots
    void LoadSurfSpots();

    // Load surf spots within the specified extent
    void LoadSurfSpots(double north, double west, double south, double east);

    // Event to fire when surf spots have been loaded
    event EventHandler<SurfSpotEventArgs> SurfSpotLoadingComplete;

    // Event to fire when there is an error loading surf spots
    event EventHandler<SurfSpotErrorEventArgs> SurfSpotLoadingError;
}

When the repository has finished loading the data, it fires the SurfSpotLoadingComplete event back to the ViewModel, which in turn fires its own event back to any listening Views.

To keep the example simple, ISurfSpotRespository is implemented as a mock in-memory repository called InMemorySurfSpotRepository. See the sample code download for the complete implementation. Note that a real web application would most likely load the data from a remote web service. See Johannes Kebeck’s Blog Post Database Connections with the Virtual Earth Silverlight MapControl CTP for a good example of how to do that.

Once the data loading is complete, the SurfSpotsLoadedHandler method of the SurfSpotsMapView class handles adding the new surf spots to the map:

private void SurfSpotsLoadedHandler(object sender, EventArgs e)
{
    surfSpotsLayer.RemoveAllChildren();
    foreach (SurfSpot s in viewModel.SurfSpots)
    {
        Ellipse point = new Ellipse()
        {
            Width = 10,
            Height = 10,
            Fill = new SolidColorBrush(Colors.Red),
            Opacity = 0.65,
            Tag = s
        };
        point.MouseLeftButtonDown += new MouseButtonEventHandler(
            SurfSpotPoint_MouseLeftButtonDown);
        MapLayer.SetMapPosition(point, new Location(s.Latitude, s.Longitude));
        ToolTipService.SetToolTip(point, s.Name);
        surfSpotsLayer.Children.Add(point);
    }
}

In the SurfSpotsListView, keeping the list updated is even easier. The SurfSpots property of the ViewModel is an ObservableCollection<SurfSpot>, so we just need to set the ItemsSource property of inner DataGrid and it will keep itself updated automatically. This happens in the ViewModel property setter for the SurfSpotsListView:

public SurfSpotsViewModel ViewModel
{
    get { return viewModel; }
    set
    {
        viewModel = value;

        // Wire up the View Model
        viewModel.SelectedSurfSpotChanged += new EventHandler(SelectedSurfSpotChangedHandler);
        viewModel.SurfSpotsLoaded += new EventHandler(SurfSpotsLoaded);
        viewModel.SurfSpotsLoadError += new EventHandler(SurfSpotsLoadErrorHandler);

        // Set the items source for the data grid. Since it is an
        // ObservableCollection<> we don't need to handle the SurfSpotsLoaded
        // event. The grid view will automatically keep itself updated.
        DataGrid1.ItemsSource = viewModel.SurfSpots;
    }
}

Unit Tests

Of course, one of the main reasons for using the Model-View-ViewModel pattern is testability. I’ve included a simple unit test of the View Model class in the Tests project to illustrate the concept. This test is modeled after the tests found in Shawn Wildermuth’s sample code. I didn’t create a full array of tests, as it wasn’t necessary for the proof of concept. I’ll leave that as an exercise for the reader.

Wrap Up

This post has presented a proof of concept sample application using the Model-View-ViewModel pattern with the Microsoft Bing Maps (Virtual Earth) Silverlight Map Control CTP. ViewModel synchronization with multiple Views is demonstrated in the sample. While we covered some of the implementation details in this post, there is more to be found in the downloadable sample – so be sure to check it out!

If you would be interested in a follow up post covering some of the additional implementation details, please let me know in the comments.

Hope this helps!

Resources

ArcGIS Server WebADF: Adjusting the Zoom Scale for Find Address Task Results

by James Richards July 02, 2009

Overview

When working with the Find Address Task in the ArcGIS Server WebADF, the default zoom scale that is displayed when the user zooms to a found address might not be what you want. This article discusses how to change it using the ZoomToPointFactor property. [more]

Background

Let’s assume that you have a WebADF application with some Map Layers and a Find Address Task that is configured to use a Geocoding Service. This could be an application that you created from scratch in Visual Studio, or one that you created with ArcGIS Server Manager.

After entering an address:

image

The results are shown in the Task Results panel. From there you can right click on an address and choose Zoom To:

image

This will zoom the map to an area centered on the address point. The problem is that scale to which the map is zoomed might not be what you want. Often it’s zoomed out too far.

Solution

To adjust the zoom scale to your liking, add the ZoomToPointFactor property to the esri:TaskResults element on the ASP.NET Page hosting the application.

If you created the application with ArcGIS Server Manager using the default options, this would be found in the Default.aspx file in the C:\inetpub\wwwroot\[YourApplicationName] directory.

Open this file and search for the esri:TaskResults tag, then add the ZoomToPointFactor property and give it a numeric value. In the example below, I’ve set the property to “1000”.

image ArcGIS Server will divide the width and height of the map by this value when zooming to a point. For this example, the zoom area will be 1/1000th of the full extent. The larger the ZoomToPointFactor, the further in that the map will zoom. You will need to experiment to find the ratio that works best, as the full extent can vary greatly depending on the data in your application.

Also note that if you have any cached map services in your application, ArcGIS Server will snap to the cache scale that is closest to the calculated map extent. Consequently, you might not see the map extent change if you don’t modify the ZoomToPointFactor property by a large enough value to jump to another scale.

Resources

Tags: , , ,

.NET | ArcGIS Server | How To | Planet GS | WebADF

Reduce ArcGIS Server Boilerplate Code By Implementing IDisposable

by James Richards April 19, 2009

When programming with ArcObjects and ArcGIS Server, code often follows a common pattern of connecting to ArcGIS Server, getting hold of an IServerContext, doing some work, and releasing the context. This leads to a lot of unnecessarily repeated setup and teardown code, usually taking a form similar to the following example:

IServerContext context = null;

try

{

    IGISServerConnection conn = new GISServerConnectionClass()

        as IGISServerConnection;

 

    // Connect as ASPNET or NETWORK SERVICE.

    // Get optional host setting from web.config,

    // or use localhost if not found.

    string host = ConfigurationManager.AppSettings["AGSHost"];

    if (String.IsNullOrEmpty(host))

    {

        host = "localhost";

    }

 

    // Make connection

    conn.Connect(host);

    IServerObjectManager som = conn.ServerObjectManager;

    context = som.CreateServerContext("MyServer", "MapServer");

   

    // Do some stuff with the server context

}

finally

{

    if (context != null)

    {

        context.ReleaseContext();

    }

}

In this example, there is a lot of boilerplate happening just to get to the server context and do something interesting. Seeing this kind of code repeated throughout an ArcGIS Server project made me a little queasy as it violates the DRY Principle, so that got me thinking about how to eliminate it. [more]

I decided to create a wrapper class that handles making the connection and implements IServerContext and IDisposable. Inspiration for this idea comes from the .NET SqlConnection class, which also implements IDisposable and can therefore be instantiated inside of a using block to automatically close the connection and release any resources when the using block goes out of scope. (See this David Hayden post for a basic discussion of why this is a good thing.) Proper implementation of IDisposable is known as the Dispose Pattern and is documented both here and in Cwalina and Adams excellent Framework Design Guidelines.

With this new class, we can reduce the boilerplate code block to the following:

using (ArcGisServerContext context = new ArcGisServerContext("MyServer",

    ArcGisServiceType.MapServer))

{

    // Do some stuff with the server context

}

Notice also the use of an Enum to indicate the server type, thus eliminating one of the error prone hard coded strings as well. (In a real application, I would also retrieve the value “MyServer” from a config or resource file.) The Enum is defined like so:

public enum ArcGisServiceType

{

    MapServer,

    GeocodeServer,

    GlobeServer,

    GPServer,

    GeoDataServer,

    GeometryServer,

    ImageServer,

}

and the ArcGISServerContext class is implemented as follows:

using System;

using System.Configuration;

using ESRI.ArcGIS.Server;

public class ArcGisServerContext : IServerContext, IDisposable

{

    #region Fields

    private IServerContext serverContext;

    #endregion

 

    #region Constructors()

    // Here you can add overloaded constructors to take additional

    // arguments such as login credentials - Or you could alter the

    // constructor to use an ArcGIS Server Identity from web.config.

 

    // This example just connects as the current user, which will be

    // ASPNET or NETWORK SERVICE if running under IIS on Wind XP or 2003.

    public ArcGisServerContext(string serviceName,

        ArcGisServiceType serviceType)

    {

        IGISServerConnection conn = new GISServerConnectionClass()

            as IGISServerConnection;

 

        // Get optional host setting from web.config,

        // or use localhost if not found.

        string host = ConfigurationManager.AppSettings["AGSHost"];

        if (String.IsNullOrEmpty(host))

        {

            host = "localhost";

        }

 

        // Make connection and cache IServerContext

        conn.Connect(host);

        IServerObjectManager som = conn.ServerObjectManager;

        serverContext = som.CreateServerContext(serviceName,

            serviceType.ToString());

    }

    #endregion

 

    #region IServerContext Implementation

    // These methods just pass through to the cached server context

 

    public object CreateObject(string clsid)

    {

        return serverContext.CreateObject(clsid);

    }

 

    public object GetObject(string name)

    {

        return serverContext.GetObject(name);

    }

 

    public object LoadObject(string str)

    {

        return serverContext.LoadObject(str);

    }

 

    public void ReleaseContext()

    {

        serverContext.ReleaseContext();

    }

 

    public void Remove(string name)

    {

        serverContext.Remove(name);

    }

 

    public void RemoveAll()

    {

        serverContext.RemoveAll();

    }

 

    public string SaveObject(object obj)

    {

        return serverContext.SaveObject(obj);

    }

 

    public IServerObject ServerObject

    {

        get { return serverContext.ServerObject; }

    }

 

    public void SetObject(string name, object obj)

    {

        serverContext.SetObject(name, obj);

    }

    #endregion

 

    #region IDisposable Implementation

    // Implementation of the Dispose Pattern

 

    public void Dispose()

    {

        Dispose(true);

        GC.SuppressFinalize(this);

    }

 

    protected virtual void Dispose(bool disposing)

    {

        // Release the server context

        if (disposing)

        {

            if (serverContext != null)

            {

                serverContext.ReleaseContext();

            }

        }

    }

    #endregion

}

 

It’s really pretty simple, and it sure is nice not to have to copy / paste or keep re-typing the same boilerplate code over and over.

Hope this helps!

Tags: , , ,

.NET | ArcGIS Server | Planet GS

How To Use URL Rewriting for Extensionless WCF Svc Services with Helicon Tech ISAPI Rewrite

by James Richards February 02, 2009

Recently I was working on a WCF Service and wanted to serve it up with extensionless URLs. I downloaded and installed ISAPI Rewrite (Lite version) but struggled to come up with the correct regex expression to get it working.

I found many good blog postings and clues from Scott Gu, Scott Hanselman, and Jeff Atwood, and a thread on Stack Overflow, but none of these answered this specific question. As Scott Hanselman noted, URL rewriting can seem like "freaking voodoo".

So I moseyed on over to the Helicon Tech support forum and [more] asked the question. Anton (who I assume is one of the good folks at Helicon Tech) swiftly and correctly answered the question with the following example:

RewriteBase /
RewriteRule ^MyProject/MyService/products(.*)?$ MyProject/MyService.svc/products$1 [NC,L]

You can follow the complete thread here.

I hope this helps!

Tags: , , , , ,

.NET | ASP.NET | How To | REST | WCF

Powered by BlogEngine.NET 1.6.0.0
Theme by Mads Kristensen | Modified by Mooglegiant
Creative Commons License This work is licensed under a Creative Commons Attribution 3.0 United States License.

Welcome

James Richards

Hi, I'm James Richards the CTO and co-founder of Artisan Global LLC. We make location-aware mobile apps with maps. I'm the author of QuakeFeed and I helped launch Zaarly at LASW Feb 2011. I also enjoy surfing, snowboarding, golfing, yoga, and music. I love my family: Linda, Sequoya and our cats Remy and Twiggy. Thanks for stopping by, I hope you find something helpful here.

Subscribe by RSS   Follow me on Twitter   Connect on Facebook   View my profile on LinkedIn


Amazon Associates

Some of my posts may contain Amazon Associates links. I only include these links when it makes sense within the context of the article. If you are in the market for one of these items, please consider clicking on an affiliate link to make your purchase. You still get the same great deal from Amazon and it will help me pay for hosting and bandwidth. Thanks!