in
Forums
Blogs
DevExpress.com
Client Center
Support Center
DevExpress Channel

The One With

Silverlight and Data Access using XPO

It used to be that in any thin client application that you build, you would spent most time worrying about how to bring your data to the client and then how to properly submit your changes back. These days, as the data access frameworks and the ORM solutions matured, you can actually afford to think about your application, your business logic etc... instead of constantly tinkering with the DA plumbing.

Such challenges still exist, to a certain degree, for young platforms like Silverlight. But you'd be happy to know that XPO is making it to Silverlight. In fact, you can already find some bits in your v8.2 install :).

I am going to show you how to use them... [If you are new to XPO I suggest reading more about it here http://www.devexpress.com/Products/NET/ORM/ and also check out Gary's Blog for some XPO related goodies...]

Warning: These are pre-release preview bits for (DXperience 8.2.4) and are subject to change. 

Developing an HTTP Data Layer

To accomplish the flow illustrated above, we will need an IDataStore that will work over HTTP.

public interface IDataStore {
AutoCreateOption AutoCreateOption { get; }
ModificationResult ModifyData(params ModificationStatement[] dmlStatements);
SelectedData SelectData(params SelectStatement[] selects);
UpdateSchemaResult UpdateSchema(bool dontCreateIfFirstTableNotExist, params DBTable[] tables);
}

On the server side, we will expose it as a WCF Web Service.

[XmlSerializerFormat]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceContract(Namespace = "")]
public class Gateway {
static IDataStore s_dataStore;

static Gateway() {
s_dataStore = XpoDefault.GetConnectionProvider(
MSSqlConnectionProvider.GetConnectionString("<COMPUTER_NAME>", "<DATABASE_NAME>"),
AutoCreateOption.DatabaseAndSchema);
}

[OperationContract]
public AutoCreateOption GetAutoCreateOption() {
return s_dataStore.AutoCreateOption;
}

[OperationContract]
public SelectedData SelectData(SelectStatement[] selects) {
if (selects != null && selects.Length > 0 && selects[0].TableName == "XPObjectType") {
return new SelectedData(new SelectStatementResult());
}
return s_dataStore.SelectData(selects);
}

[OperationContract]
public UpdateSchemaResult UpdateSchema(bool dontCreateIfFirstTableNotExist,
DBTable[] tables) {
return s_dataStore.UpdateSchema(dontCreateIfFirstTableNotExist, tables);
}

[OperationContract]
public ModificationResult ModifyData(ModificationStatement[] statements) {
return s_dataStore.ModifyData(statements);
}
}

 

And on the client side we will create a wrapper for it.

The contract:

[XmlSerializerFormat]
[ServiceContractAttribute(Namespace = "")]
public interface IGateway {
[OperationContractAttribute(AsyncPattern = true,
Action = "urn:Gateway/GetAutoCreateOption", ReplyAction = "urn:Gateway/GetAutoCreateOptionResponse")]
IAsyncResult BeginGetAutoCreateOption(AsyncCallback callback, object asyncState);
AutoCreateOption EndGetAutoCreateOption(IAsyncResult result);

[OperationContractAttribute(AsyncPattern = true,
Action = "urn:Gateway/SelectData", ReplyAction = "urn:Gateway/SelectDataResponse")]
IAsyncResult BeginSelectData(SelectStatement[] selects, AsyncCallback callback, object asyncState);
SelectedData EndSelectData(IAsyncResult result);

[OperationContract(AsyncPattern = true,
Action = "urn:Gateway/UpdateSchema", ReplyAction = "urn:Gateway/UpdateSchemaResponse")]
IAsyncResult BeginUpdateSchema(bool dontCreateIfFirstTableNotExist, DBTable[] tables, AsyncCallback callback, object asyncState);
UpdateSchemaResult EndUpdateSchema(IAsyncResult result);

[OperationContract(AsyncPattern = true,
Action = "urn:Gateway/ModifyData", ReplyAction = "urn:Gateway/ModifyDataResponse")]
IAsyncResult BeginModifyData(ModificationStatement[] statements, AsyncCallback callback, object asyncState);
ModificationResult EndModifyData(IAsyncResult result);
}

And the transport channel:

public class Gateway : ClientBase<IGateway>, IGateway, IDataStore {
#region IDataStore Members
...
public SelectedData SelectData(params SelectStatement[] selects) {
if (this._dispatcher.CheckAccess()) {
throw new InvalidOperationException();
}

var _ar = BeginSelectData(selects, null, null);

if (!_ar.IsCompleted) {
_ar.AsyncWaitHandle.WaitOne();
}

return EndSelectData(_ar);
}
...
#endregion

#region IGateway Memebers
...
public IAsyncResult BeginSelectData(SelectStatement[] selects, AsyncCallback callback, object asyncState) {
return base.Channel.BeginSelectData(selects, callback, asyncState);
}

public SelectedData EndSelectData(IAsyncResult result) {
return base.Channel.EndSelectData(result);
}
...
#endregion

#region ClientChannel
private class ClientChannel : ChannelBase<IGateway>, IGateway {
...
public IAsyncResult BeginSelectData(SelectStatement[] selects, AsyncCallback callback, object asyncState) {
object[] _args = new object[1];
_args[0] = selects;
IAsyncResult _result = base.BeginInvoke("SelectData", _args, callback, asyncState);
return _result;
}
public SelectedData EndSelectData(IAsyncResult result) {
object[] _args = new object[0];
SelectedData _result = (SelectedData)base.EndInvoke("SelectData", _args, result);
return _result;
}
...
}
#endregion
}

Full implementation of the Gateway proxy can be downloaded here: Gateway.cs

 

Preparing your Silverlight Application

In your Silverlight project add references to the following assemblies:

  • DevExpress.Data.v8.2.SL
  • DevExpress.Xpo.v8.2.SL
  • System.Xml.Serialization
  • System.ServiceModel

Add the Gateway.cs and include a config. file for the gateway web service:

<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_Gateway" maxBufferSize="65536"
maxReceivedMessageSize="65536">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:50691/Gateway.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_Gateway" contract="DevExpress.Xpo.Xtras.IGateway"
name="BasicHttpBinding_Gateway" />
</client>
</system.serviceModel>
</configuration>

 

Working with Data

Now that all the prop. work is out of the way, we can start executing some queries. Suppose you are building an auto showroom application and you want to retrieve all the BMWs from the table inventory.

[OptimisticLocking(false)]
public class Inventory : PersistentBase {
Guid _id;
[Key(AutoGenerate = true)]
public Guid ID {
get { return _id; }
set { SetPropertyValue("ID", ref _id, value); }
}

string _make;
public string Make {
get { return _make; }
set { SetPropertyValue("Make", ref _make, value); }
}

string _model;
public string Model {
get { return _model; }
set { SetPropertyValue("Model", ref _model, value); }
}

public Inventory(Session session)
: base(session) {
}
}
 

UnitOfWork unitOfWork = new UnitOfWork(new SimpleDataLayer(_gateway));

XPQuery<Inventory> inventory = new XPQuery<Inventory>(unitOfWork);
var query = from t in inventory
where t.Make == "BMW"
select t;

var list = query.ToList<Inventory>();

// Do stuff with the list... bind it to a grid etc...

 

Where _gateway is an instance of our DevExpress.Xpo.Xtras.Gateway : IDataStore initialized somewhere during startup or page load.

Modifying or creating a new record is also done naturally via the Inventory class.

using (UnitOfWork u = new UnitOfWork(new SimpleDataLayer(this._gateway))) {
var o = new Inventory(u);
o.Make = "Audi";
u.Save(o);
u.CommitChanges();
}

One very important thing to remember here is that all operations that require a web service call need to be performed asynchronously. This is enforced by the Silverlight runtime [and it's good thing]. So always invoke your calls from a worker thread, for example:

ThreadPool.QueueUserWorkItem(delegate(object state) {

UnitOfWork unitOfWork = new UnitOfWork(new SimpleDataLayer(_gateway));

XPQuery<Inventory> inventory = new XPQuery<Inventory>(unitOfWork);
var query = from t in inventory
where t.Make == "BMW"
select t;

var list = query.ToList<Inventory>();

Dispatcher.BeginInvoke(() => {
agDataGrid.DataSource = list;
});

});

Download Sample Project

Cheers,

Azret

Published Sep 04 2008, 01:16 PM by Azret Botash (Developer Express)
Filed under: ,
Technorati tags: Silverlight, XPO

Comments

 

Alex Hoffman said:

ahha! glad to see I was right :)

... expressapp.blogspot.com/.../xpo-for-silverlight.html

Thanks for yet another really great technical post Azret - I find all of yours particularly useful and interesting.

September 4, 2008 8:52 PM
 

ccchai said:

Errr....a sample project please....still don't understand where should I put code snippets inside my VS projects....

September 5, 2008 1:32 AM
 

Silverlight news for September 5, 2008 said:

Pingback from  Silverlight news for September 5, 2008

September 5, 2008 4:59 AM
 

Azret Botash (Developer Express) said:

Hello ccchai,

I have provided a link to a sample project at the bottom of the post... Hope it will get  you started...

Cheers,

Azret

September 5, 2008 2:55 PM
 

Wicky Hu said:

Hello,

I'm new to XPO and SilverLight. I tried the sample and have some questions:

1) Does this approach expose database to client side? If yes,  is there any way to protect the gateway?

2) When I click Refresh button, I notice that there are four times post back to server, and 7 times when click New button. Is this designed behavior or just problem in sample project?

Thanks

Wicky

September 6, 2008 10:20 AM
 

Robert Thomas said:

How might this work with XAF.  My concern is the statement about using background threads.  All the info I could find on background threads from the XAF team said NO, we aren't doing that.  Any suggestions on how to adapt to XAF?  Thanks.

September 8, 2008 9:34 AM
 

Community Blogs said:

Jordan Knight hooking up the Browser back/forward buttons, Cameron Albert with "Joust", Andy

September 8, 2008 1:54 PM
 

Azret Botash (Developer Express) said:

Hello Wicky Hu,

1) It depends on what you mean by expose... There is no direct connection made to the database server and everything is goes through a proxy web service... Protecting the gateway can be done in several ways, some auth. logic for example... I will post some in the near future please stay tuned..

2) It is a problem in the sample, that can be fixed by caching the values of UpdateSchemaResult and AutoCreateOptions for example...

September 9, 2008 12:57 PM
 

Azret Botash (Developer Express) said:

Hello Robert,

This is Silverlight specific... as the example is meant to introduce a subset of XPO on the Silverlight platform. This is unrelated to XAF...

Thanks

Azret

September 9, 2008 1:03 PM
 

The One With said:

By now, those of you who know me personally or through the forums and blogs know that I love Silverlight

December 2, 2008 12:56 AM

Leave a Comment

(required)  
(optional)
(required)  
Verification code: Required
   
Add
Copyright © 1998-2010 Developer Express Inc.
ALL RIGHTS RESERVED