Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

WCF on Windows Mobile and .NET Compact Framework

I was just listening to Don, Dave and James on the second of the Jumpstart series for the Codemasons’ Guild and the topic of communicating via WCF came up.  Now typically when I build mobile apps I don’t go through all the pain of using WCF, I simply use a regular asmx web service and then use Add Web Reference to add it to my mobile project.  To secure it, I just communicate over SSL. If you do want/need to use WCF on the server side, there are a couple of options to do this.

Before we jump into how you use WCF, let me point out a couple of useful powertoys:

The Power Toys for .NET CF include NetCFSvcUtil.exe which is a device equivalent of SvcUtil.exe and is needed in order generate the WCF proxy.

Firstly, you need to be aware that the .NET CF has some severe limitations when it comes to WCF.  Unfortunately the only binding that is supported (excluding the much over-hyped WCF via Exchange) is basicHttpBinding. For the WCF service you want to consume you need to change it from using the default wsHttpBinding.  This can be done by launching the Tools > WCF Service Configuration Editor from Visual Studio. Open the web.config file for the WCF Service project.  Under Endpoints, adjust the Binding to basicHttpBinding.

image

Save this change and run the WCF Service.

Now to the options…..

1) The first option is to use Add Web Reference.  This is by far the simplest approach as you can click Browse to: Web services in this solution.  Select your service and click Add Reference. 

image

Once you have added the reference you can call your service method the same way you would from a regular desktop application:

localhost.Service1 service = new localhost.Service1();
service.Url = service.Url.Replace("localhost", "192.168.1.2");
return service.GetData(5, true);

Note: You have to change the “localhost” host name to something that can be resolved by the device.  I typically just use the ip address of the development machine.  Clearly for production you will want to specify this in a configuration file or make it a configurable setting within your application.

2) The second option is to use NetCFSvcUtil.exe to generate the appropriate WCF proxy information. Start by opening up the Visual Studio command prompt (Start > Microsoft Visual Studio 2008 > Visual Studio Tools > Visual Studio 2008 Command Prompt) and adding the path to the Compact Framework power toys:

>> set path=%path%;C:\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin

Navigate to the folder where you want the proxy files to be created and then use NetCFSvcUtil.  I figured this would be quite simple but it appears that somewhere between Vista SP1 and SP2 (and there are reports of this problem on Windows 7 too) a bug in NETCFSvcUtil surfaced preventing it from working.

image

As you can see the error message is really helpful:

Error: An error occurred in the tool.
Error: Error in the application.

Currently, there doesn’t seem to be a workaround for this.  Some people have had varied success by changing the parameters and return types of the service methods.  The one strategy I used that appears to work is to use a combination of SvcUtil and NetCFSvcUtil.

>> svcutil.exe c:\temp\WindowsMobileServices\MyDataServices\bin\MyDataServices.dll

Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
Copyright (c) Microsoft Corporation.  All rights reserved.

Generating metadata files...
C:\temp\WindowsMobileServices\WindowsMobileServices\tempuri.org.wsdl
C:\temp\WindowsMobileServices\WindowsMobileServices\tempuri.org.xsd
C:\temp\WindowsMobileServices\WindowsMobileServices\schemas.microsoft.com.2003.1
0.Serialization.xsd

>> netcfsvcutil.exe tempuri.org.wsd tempuri.org.xsd
Microsoft (R) .NET Compact Framework Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.5.0.0]
Copyright (c) Microsoft Corporation.  All rights reserved.

Generating files...
C:\temp\WindowsMobileServices\WindowsMobileServices\tempuri.org.cs
C:\temp\WindowsMobileServices\WindowsMobileServices\CFClientBase.cs

Now, include all the generated file into your mobile project.  You will also need to add references to System.ServiceModel and System.Runtime.Serialization.

In order to call your service method you can now write the following:

var binding = System.ServiceModel.BasicHttpBinding();
var endpoint = System.ServiceModel.EndpointAddress("http://192.168.1.2:6323/Service1.svc");
Service1Client client = new Service1Client(new binding, new endpoint
);
return client.GetData(5);

So, the question is why would you go the second option?  Well if you actually look at the generated code, adding the WCF service using Add Web Reference adds a bunch of unnecessary fields.  When calling the method GetData there is a second parameter called “valueSpecified” which is little more than a flag to indicate if the first parameter was specified or not.  This is not required if you use the second option to generate the proxy information.

Comments (6) -

  • Liem Le

    7/1/2010 8:47:08 PM |

    thanks for this article! it helped!

    in your article you mention "Error: An error occurred in the tool.
    Error: Error in the application.

    Currently, there doesn’t seem to be a workaround for this.  Some people have had varied success by changing the parameters and return types of the service methods."

    so i attempted to remove these lines of code from my wcf.
            //[OperationContract]
            //CompositeType GetDataUsingDataContract(CompositeType composite);
    and

            //public CompositeType GetDataUsingDataContract(CompositeType composite)
            //{
            //    if (composite.BoolValue)
            //    {
            //        composite.StringValue += "Suffix";
            //    }
            //    return composite;
            //}


    then it all worked. hope that helps someone.

  • Liem Le

    7/1/2010 8:47:28 PM |

    thanks for this article! it helped!

    in your article you mention "Error: An error occurred in the tool.
    Error: Error in the application.

    Currently, there doesn’t seem to be a workaround for this.  Some people have had varied success by changing the parameters and return types of the service methods."

    so i attempted to remove these lines of code from my wcf.
            //[OperationContract]
            //CompositeType GetDataUsingDataContract(CompositeType composite);
    and

            //public CompositeType GetDataUsingDataContract(CompositeType composite)
            //{
            //    if (composite.BoolValue)
            //    {
            //        composite.StringValue += "Suffix";
            //    }
            //    return composite;
            //}


    then it all worked. hope that helps someone.

  • Liem Le

    7/1/2010 8:47:37 PM |

    thanks for this article! it helped!

    in your article you mention "Error: An error occurred in the tool.
    Error: Error in the application.

    Currently, there doesn’t seem to be a workaround for this.  Some people have had varied success by changing the parameters and return types of the service methods."

    so i attempted to remove these lines of code from my wcf.
            //[OperationContract]
            //CompositeType GetDataUsingDataContract(CompositeType composite);
    and

            //public CompositeType GetDataUsingDataContract(CompositeType composite)
            //{
            //    if (composite.BoolValue)
            //    {
            //        composite.StringValue += "Suffix";
            //    }
            //    return composite;
            //}


    then it all worked. hope that helps someone.

  • Nike Shox NZ

    8/5/2010 7:26:32 PM |

    Very good site, excellent content, I will recommend to my group of readers in college too, I will leave an input that the key to success is to choose a product that's in demand.

  • iPhone Software Development

    9/30/2010 3:56:38 AM |

    I have read that by using WCF, you can send data as asynchronous messages from one service endpoint to another. A service endpoint can be part of a continuously available service hosted by IIS, or it can be a service hosted in an application. WCF is a flexible platform. Because of this extreme flexibility, WCF is also used in several other Microsoft products. By understanding the basics of WCF, you have an immediate advantage if you also use any of these products.WCF is designed in accordance with service oriented architecture principles to support distributed computing where services are consumed by consumers. Clients can consume multiple services and services can be consumed by multiple clients.

  • new nike

    10/28/2010 11:47:36 PM |

    Great Ifo. Great People. Great Blog. Thank you for all the great sharing that is being done here. And I know a great shoe website<a href="http://www.nikeshoesdance.com">关键词</new nike>

Comments are closed
Change Tracking with SQL Server Compact (LINQ to SQL) on Windows Phone

Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Change Tracking with SQL Server Compact (LINQ to SQL) on Windows Phone

As you are probably aware the Mango update for Windows Phone (ie Windows Phone OS 7.1) will include support for LINQ to SQL. According to Microsoft you shouldn’t need to know or care that this is SQL Server Compact (SQL CE) since they’ve limited access to the database to a set of managed wrappers, namely LINQ to SQL. Unfortunately this only represents a subset of the true capabilities of SQL CE. One of the sorely missing features is of course synchronization, as there is no support for Merge Replication, RDA or Sync Framework. In fact, even doing your own synchronization logic is crippled by the fact that you can’t tap into the native change tracking that SQL CE offers….. well, not using a supported set of APIs.

If we work on the assumption that what ships with Windows Phone OS 7.1 is actually SQL CE v3.5+ (to indicate that there have been some changes to the database engine to support Windows Phone but that the majority of core SQL CE functionality is there), then we can assume that the change tracking functionality, which is part of the SQL CE db engine, is available in the underlying database engine. It’s just not accessible via the managed interface (ie LINQ to SQL). However, if you’ve ever taken a look at change tracking in SQL CE you’d have noticed that all the information relating to  change tacking is maintained in private tables (__sysOCSTrackedObjects, __sysOCSDeletedRows and __sysTxCommitSequence) and private columns in the tracked tables (__sysChangeTxBsn. __sysInsertTxBsn, __sysTrackingContext). So in theory if change tracking was enabled (we’ll cover that in a minute) all we need to do in order to retrieve this information is to map these tables and columns. For the purpose of this post I’m just going to map the __sysChangeTxBsn column in the Person table. The bulk of this code was generated using the SqlMetal tool, although you might also want to try out Erik’s SQL Server Compact Toolbox . The bolded code was manually added to map the __sysChangeTxBsn column (the property ChangeTx is read only as you don’t want to modify this directly).

[global::System.Data.Linq.Mapping.TableAttribute()]
public partial class Person : INotifyPropertyChanging, INotifyPropertyChanged
{    
    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
   
    private System.Guid _Id;
   private string _Name;

    private int? _ChangeTx;

    #region Extensibility Method Definitions
    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnIdChanging(System.Guid value);
    partial void OnIdChanged();
    partial void OnNameChanging(string value);
    partial void OnNameChanged();
    #endregion
   
    public Person() {
        OnCreated();
    }
   
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Id", DbType="UniqueIdentifier NOT NULL", IsPrimaryKey=true)]
    public System.Guid Id {
        get {
            return this._Id;
        }
        set {
            if ((this._Id != value)) {
                this.OnIdChanging(value);
                this.SendPropertyChanging();
                this._Id = value;
                this.SendPropertyChanged("Id");
                this.OnIdChanged();
            }
        }
    }
   
    [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", DbType="NVarChar(100)")]
    public string Name {
        get {
            return this._Name;
        }
        set {
            if ((this._Name != value))
            {
                this.OnNameChanging(value);
                this.SendPropertyChanging();
                this._Name = value;
                this.SendPropertyChanged("Name");
                this.OnNameChanged();
            }
        }
    }

   [global::System.Data.Linq.Mapping.ColumnAttribute(
                 
Storage = "_ChangeTx",
                  Name="__sysChangeTxBsn", 
                  DbType = "bigint")]
    public int? ChangeTx {
        get {
            return this._ChangeTx;
        }
    }

    public event PropertyChangingEventHandler PropertyChanging; 
    public event PropertyChangedEventHandler PropertyChanged;
   
    protected virtual void SendPropertyChanging() {
        if ((this.PropertyChanging != null)) {
            this.PropertyChanging(this, emptyChangingEventArgs);
        }
    }
   
    protected virtual void SendPropertyChanged(String propertyName) {
        if ((this.PropertyChanged != null)) {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Now that you’ve mapped this column you actually need to enable change tracking. As I’ve indicated previously there is no way to do this via LINQ to SQL, which means that if you want to enable change tracking you’ll need to create your database on a platform that supports ADO.NET which includes the API to enable change tracking. For me this was a simple Windows Forms application.

Create your SQL Server Compact database using the designer tools in either SQL Management Studio or Visual Studio (my database is called ChangeData.sdf). Next, add this database file to a newly created Windows Forms application and set the Build Action to Content, and make sure that it is copied to the output directory. Add a button to the form and then in the Click event handler add the following code – this opens a connection to the database file in the executing directory and enables change tracking on the Person table.

private void button1_Click(object sender, EventArgs e) {
    var cnstr = "Data Source=" +
                Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\","") +
               
\\ChangeData.sdf;Persist Security Info=False;;

   var connection = new SqlCeConnection(cnstr);
    connection.Open();

    var tracker = new SqlCeChangeTracking(connection);
    tracker.EnableTracking("Person", TrackingKeyType.PrimaryKey, TrackingOptions.All);
}

Run the Windows Forms application and click the button. You’re done, change tracking is enabled on your database file. Copy this database file (make sure you get the one that is in the executing folder for the Windows Forms application) into your Windows Phone application. Again make sure the Build property is set to Content.

Ok, now to write some code against your change tracking database. The first thing we’re going to do is to copy the database that is packaged with the application from the installation folder (appdata, where it’s read only) into isolated storage (isostore, where it’s read-write). The following code then iterates through all the Person entities (I created a few sample records in the database via Visual Studio) and modifies the Name property.

void MainPage_Loaded(object sender, RoutedEventArgs e) {
    CopyReferenceDatabase("ChangeData.sdf", "ChangeData.sdf");
 
   string ReferenceDBConnectionString = "Data Source=isostore:/ChangeData.sdf";
   using (var db = new ChangeData(ReferenceDBConnectionString)) {
        var people = from person in db.Persons
                               select person;
        foreach (var p in people) {
           p.Name = p.Name + " updated";
        }
        db.SubmitChanges();
    }
}

public static void CopyReferenceDatabase(string referenceDatabaseName,
                                                                               string isolatedStorageDatabaseName) {
    var iso = IsolatedStorageFile.GetUserStoreForApplication();
    if (iso.FileExists(isolatedStorageDatabaseName)) return;

    using (var input = Application.GetResourceStream(
            new Uri(referenceDatabaseName, UriKind.Relative)).Stream) {
        using (var output = iso.CreateFile(isolatedStorageDatabaseName)) {
            var readBuffer = new byte[4096];
            int bytesRead;

            while ((bytesRead = input.Read(readBuffer, 0, readBuffer.Length)) > 0) {
                output.Write(readBuffer, 0, bytesRead);
            }
        }
    }
}

Now to see the effects of change tracking. If you set a break point in the for loop you can take a look at the ChangeTx property on the Person entities. If you run the application a few times you’ll see that this value changes each time you update the Person entities.

image

If you want to interrogate the database file further you can export the file from either the emulator or a real device using the Isolated Storage Explorer Tool:

c:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Tools\IsolatedStorageExplorerTool>ISETool.exe ts xd 3abccaef-453b-4345-8f0e-7861873ddbf9 c:\temp\emu_export

The parameters for ISETool.exe are:
ts – Take snapshot (copy entire isolated storage files/folders for the specified application)
xd – Emulator (use de for actual, unlocked, device)
3abccaef-453b-4345-8f0e-7861873ddbf9 – The guid of the specified application. This is the ProductID attribute from the WMAppManifest.xml file for your application.
c:\temp\emu_export – The folder on the host computer that you want to push the snapshot too. I recommend specifying an empty folder.

Once you’ve exported the database file you can then connect to it via Visual Studio or SQL Management Studio. Note that in the designer both the private tables and private columns are hidden. However, if you run a SQL statement (eg select * from Person) you’ll see the private columns.

And that’s essentially it – you’ve got a change tracking enabled database which you can use to synchronize data back to a server. You might like to combine this with OData, in which case you might want to take a look at how I implement change tracking on the server side using WCF Data Services and Entity Framework (http://nicksnettravels.builttoroam.com/post/2010/08/03/OData-Synchronization-with-WCF-Data-Services.aspx).

Comments are closed