Rants Tagged with “Silverlight”

<<  <  1  2  3  4  5  6  7  8  9  10  11  +  >  >>  (Total Pages: 26/Total Results: 251)

This Week in Silverlight

Sparkling Client

Erik Mork, Brian Henderson and Kelly White are doing a weekly podcast on what is new in the Silverlight community each week. This week they discuss the upcoming rumors about MIX and the plethora of CodeCamps coming up.  Check it out (and subscribe):

http://www.sparklingclient.com/silverlight_augmented_reality_and_mix_rumors/

Portland Silverlight Users Group

Silverlight Logo

I just got back from the west coast and while there I had the opportunity to speak at the Portland Silverlight Users Group.  It was a Q&A session and I had a lot of fun answering those questions. Other than questions I couldn't share information about, the most common questions were about hwo to implement MVVM.  I decided to do my 45 minute walkthrough of MVVM and built a simple editor using MVVM.  For all of you at the meeting that wanted the source, you can find it here:

http://wildermuth.com/downloads/PortlandGameEditor.zip

I can't wait to be asked back!

The State of Data Access in Silverlight

SilverlightI've said much about my opinion of Silverlight data access. Currently this is Web Services, WCF Data Services and WCF RIA Services. Let's talk about Data Services and RIA Services and how they are related:

WCF Data Services

Data Services are a good story in Silverlight, but only as of version 2.0 of the protocol. For those users of version 1.0, using Data Services was painful as automatic tracking did not exist yet. Data Services can create not only the data contract classes, but a context object that will do tracking and batching for you.  The basic story of how Data Services works is that it takes a context object from a LINQ provider and exposes all the IQueryable endpoints as REST resources that can be queried. This works well in creating a place to execute queries and post/put/delete changes. While Data Services' URI API is another skill that could be learned, the Silverlight (and .NET) client library allows you to just issue LINQ queries to the data service.  In addition, the ability to create client-designed graphs (e.g. embed relationships for eager loading) and navigate relationships to the server presents a good API for a data-centric application today.

The method for connecting to a data service is via a "Add Service Reference..." which means that it is easy to have data services projects that are outside a particular solution file. This is critical for when you are building very large projects.

One of my favorite things about Data Services today is the ability to create tracked projections from the Silverlight client:

var qry = from g in ctx.Games
          where g.ReleaseDate <= cutoffDate
          orderby g.ReleaseDate
          select new 
         {
           Name = g.Name,
           Id = g.Id,
           ReleaseDate = g.ReleaseDate
         };

This projection (which minimizes the data being sent over the wire from a Games entity), can be tracked like any other entity. This is a big boon for Silverlight clients where we want to reduce the surface area of the data being sent over the wire.

WCF Data Services' standard wire protocol is OData (which is a variant of ATOM). This means that tools like PivotTable, code-name Dallas and others can co-mingle data with other systems that are supporting OData like SharePoint, WebSphere, OpenGovernment project and SQL Azure table storage.

Some of the problems with Data Services include the fact that Service Operations are not part of the Silverlight proxy (and are not completely supported by the client library); any validation attributes of model information (like string length) are left on the client; and having to wrap your collections as DataServiceCollections to support tracking.

While I think Data Services still represents a major case for Silverlight usage, it still has a way to go before I am completely satisfied.

WCF RIA Services

WCF RIA Services takes a different approach to data access the resembles a mix of Data Services and Web Services. In RIA Services a server project is set up to expose data across the wire, but instead of Data Services' approach of exposing queryable end-points, it creates methods on the server which can be executed to retrieve, update, insert and delete data. These methods can be crafted to take specific parameters which takes some of the responsibility for what to query out of the hands of the developer (which may be fine in some cases). RIA Services does take model constraints and validation attributes and generate them in the client, which is a big win.  Also, RIA Services is like Data Services in that you can still do basic sorting, filtering and paging via the service.

The way that RIA Services is wired up to a project (via a Shared Code folder that is generated as the server-side project changes) is sexy, but for large projects the need to keep the RIA Services' project in the same solution (or use a project file hack to get it working) becomes an issue.

RIA Services is really built for the RIA Services' DomainContext to be the center of the application (which you can see when you look at demos utilizing their Data Source object that automatically wires up data as XAML elements). While you can write a loosely coupled application, it takes planning and is not the natural design of the API.

Other issue I have is with the design of the Load API where the attempt is made to hide the asynchronicity of the call.  The problem is that when errors happen on the load operation, it is hard to capture the error (other than letting it be cause as a top-level exception) so that most users will need to provide a callback which is just like having a full asynchronous operation. No better than before but the principle of least surprise isn't followed.

Finally, in comparison with Data Services, RIA Services has a lot of the same features but is missing the ability to do projections, client-defined eager loading and loading properties (e.g. relationships).

What does all this mean?

 So here we sit as Silverlight developers and we are being asked by Microsoft to choose of the three major options for data access. The problem is that none of them are perfect by any stretch of the imagination. I would think that Data Services and RIA Services are both great solutions but they are both missing features the others have.  In some ways the generation of these two projects has caused a lot of confusion as to which is the prescribed method for doing data access in Silverlight. It reminds me of the Entity Framework vs. LINQ to SQL debacle.

But where do we go from here?  I'd like to implore Microsoft to fast-track the work needed to provide parity in these products. That means build the underpinnings of both projects from the same underlying platform. That way the features of each platform could be included in the other. The features do not become the discrimator but instead it is the style of data access.  We want and need the missing features on both platforms:

  • "Add Service Reference..." Linkage to support large development projects.
  • Exposure and code-generation of any EDMX constraints and Validation attributes.
  • Projection and tracked minimizing projection support.
  • Full eager loading and navigation support.
  • Operations and Endpoints have the same level of support in the generated code.
  • Obvious and simple communication APIs to prevent the library from surprising us with error propagation.

...One More Thing

I also have the request that for the next development cycle that we provide secondary methods for communicating validation information than just validation attributes.  Validation attributes cause our server-side POCO classes to be not really POCO (since they have to have a reference to the validation assembly).  As well as most cases require us to use the painful and hacky MetadataClass method of creating a class to *only* hold these attributes. You don't have to get rid of the attributes, just let us feed them to the model in other ways (open the hooks, we'll decide how to communicate it; whether it be a DSL, XML file, database, etc.).

Your Job

As a reader of this blog, I'd like to ask you a favor.  Respond to this blog entry. I want to make sure my frustration with the two data access stacks is not just my annoyance but is causing confusion and pain to you my readers. If you agree, just comment (it can be just saying "I agree") so I can make sure the Data team at Microsoft understands how it impacts Silverlight (and others) development efforts.  Thanks!

 

Help Me Figure Out What to Write

I am going to be writing a book on Silverlight Architecture.  I'd like to get my readers/followers to help me figure out what is most important to write about. Please take the following survey if you don't mind:

http://www.survsoft.com/esurv.php?s=27432&k=12845-0-28970

Handing Events with Care?

SilverlightThe way that events are handled in Silverlight occassionally surprises people.  For the uninitiated there are two types of events in Silverlight, direct and routed. Essentially direct events are events that one one type of element can fire and do not support any type of bubbling.  The MediaEnded event on the MediaElement is a good example of this. The other type of event is a routed event.  In this type of event, the event is bubbled through the visual tree. In Silverlight, the way it works is exactly the opposite of what you might expect from Win32 programming (e.g. WinForms, VB6, MFC, etc.) Routed event bubble from the most deeply nested element to the shallowest element.  For example, when a MouseLeftButtonUp event is fired (mouse and keyboard events are routed events), the item directly under the mouse gets the event first, then its parent and so on:

 

Any control along the way can tell the routed event that it is handled which stops the bubbling from happening. While this is generally good practice (so everyone doesn't need to know about something if someone has done something about it).  Many of the standard controls handle events that they need. For example, the Button class handles the MouseLeftButtonUp routed event.  But what happens when you want to be notified even if it has been handled? Luckily there is a way.

The trick is to use the AddHandler method on UIElement.  For example, consider this XAML:

<StackPanel x:Name="LayoutRoot"
            Background="White">
  <Button Height="25"
          Width="100"
          Content="Click Me!"
          x:Name="clickButton"/>
  <Button Height="25"
          Width="100"
          Content="AddHandler"
          x:Name="addButton" />
</StackPanel>

When we handle the MouseLeftButtonUp event on the LayoutRoot, we can click on the clickButton and the event never fires.  But if we click outside the button, it works. To be able to handle 'handled' events, you have to use the UIElement.AddHandler method.  This method takes a routed event, a delegate of the correct type and an optional argument to specify whether the handler should be called for 'handled' events:

LayoutRoot.AddHandler(
   // Must be a RoutedEvent
  UIElement.MouseLeftButtonUpEvent,       
  
  // Specify the right Handler
  new MouseButtonEventHandler((o, args) => 
  { 
    MessageBox.Show("Worked even though it was handled!"); 
  }), 
  
  // Respond to "Handled" events
  true);                                   

Once the AddHandler is added, the click on the button will also call the MouseLeftButtonUp event specified in AddHandler.  Grab the code and take a look yourself! (NOTE: This is a SL4/VS2010 example but the same code would work in Silverlight 3)

http://wildermuth.com/downloads/HandledWithCare.zip

Speaking at MIX10!

MIX10

I am happy to announce that one of my entries in the  MIX 10's Open Call for content was selected by the community to be delivered at the event. The entry that was choosen is:

"RIA Services and MVVM: It Can Happen!"

In this session I'll be showing how to architect a Silverlight application using RIA Services. If you're planning on building with RIA Services, stop by and see how I think it should be done.

Are you coming?

 

Vote for My Sessions at MIX '10

Vote for MIX Sessions

If you would like to hear me blather on at MIX about how to create Silverlight applications, feel free to go visit the MIX site and vote for my sessions.  I submitted seven talks so feel free to pick your favorites.  You'll have the opportunity to vote for five of your favorite sessions.  Remember, even if you don't think you can go, these are the sessions that will be recorded and available after MIX...so everyone gets a chance to vote!

You can see my sessions by visiting:

http://visitmix.com/opencallvote/?query=Shawn%20Wildermuth

 

 

Architecting SL4 Apps with RIA Services, MEF and MVVM - Part 4 (of 3)

Silverlight Tour

Welcome the part 4 of my three-part series on architecting with RIA Services. In the last part of the series, I thought I was done with the example and some of my readers challenged me to help them understand how to handle Add/Delete scenarios. Since I was at it, I figured I should show paging and IsDirty scenarios as well, I decided to make a part four. 

Remember this example is based on my current thoughts, its not dogma. I will change my mind at times and learn from the community (as has even happened during this series).  Hopefully this example can help you think about how the patterns match your current business problems. No tithe, no sermon, no damnation....I promise ;)

Supporting IsDirty

To be able to support some functionality (like using it in some Commands' CanExecute call), I wanted to test the model for whether it had saveable entities.  My first try was to ask the RIA Services' context HasChanges property:

public bool HasChanges
{
  get { return Context.HasChanges; }
}

This works *but* the problem is that this property tests to see if the context has any changes.  Why does this matter?  I realized that as I made additional queries that the context was holding on to every object I retrieved. This means that if I changed an object and switched the Genre, the object was still in memory unchanged. More importantly, the size of the cache in the context was growing every time.  I didn't want this so I changed the way I was handling the returned values.

The context has a collection of EntitySet objects.  Each EntitySet is for a particular data type.  I wanted to change the behavior to only have a single set of games at any particular time. This way my call to HasChanges was only testing the current set of games, not every game I've ever loaded.  To do this, I made one small change:

void PerformGameQuery()
{
  // Clear the games so we don't consume a lot of memory
  Context.Games.Clear();

  ...
}

This allows us to remove games we no longer care about.

Server Paging

We should start with the paging story since that affects the design of the add/delete functionality. In RIA Services paging is pretty simple.  Since the query to the server can contain some very simple LINQ expressions, we can use the Skip and Take expressions to shape the result (Where and OrderBy are also supported). For example if we want to get the second page where the page size is ten records:

// Generate the query with paging
var qry = Context.GetGamesByGenreQuery("Shooters")
                .Skip(10)
                .Take(10);
Context.Load<T>(qry, OnGamesLoadedComplete);

The Skip expression tells RIA Services to look at the result (on the server) and skip those records; The Take expression tells RIA Services to limit the number of results returned (like a TOP clause in SQL).  Using them together allows us to page. But I like the hide the paging in the Model.  Sure the ViewModels will need to know about the paging, but they shouldn't control the size or method of paging. To achieve this, I've added a couple of properties to my model:

public class GamesModel : IGamesModel
{
  private int _currentGamesPage = 0;
  private readonly int GAMESPAGESIZE = 15; 
  private string _lastGenre;
  
  ...

Using these parts of the model, I can handle the size and current page. One problem with this design though is that its emparting state into the Model.  But since our Model already has the data context which holds onto references to returned elements, I think this is an acceptable solution.

The paging then becomes pretty simple, when GetGamesByGenre is called, we get the first page (and reset the paging mechanism):

public void GetGamesByGenreAsync(string genre)
{
  _currentGamesPage = 0;
  _lastGenre = genre;

  // Generate the query with paging
  var qry = Context.GetGamesByGenreQuery(_lastGenre)
                  .Skip(_currentGamesPage * GAMESPAGESIZE)
                  .Take(GAMESPAGESIZE);
  ... 

When the initial call to GetGamesByGenreAsync happens, we reset the paging and store the last genre (so we can use it when we get subsequent pages). In the query, we calculate the page and execute that query. When NextPage/PrevPage are called, we use this same data to calculate the subsequent pages:

public void GetPrevPageGamesAsync()
{
  if (_currentGamesPage > 0)
  {
    _currentGamesPage--;
    
    // Generate the query with paging
    var qry = Context.GetGamesByGenreQuery(_lastGenre)
                     .Skip(_currentGamesPage * GAMESPAGESIZE)
                     .Take(GAMESPAGESIZE);  
  }
}

public void GetNextPageGamesAsync()
{
  _currentGamesPage++;
  
  // Generate the query with paging
  var qry = Context.GetGamesByGenreQuery(_lastGenre)
                   .Skip(_currentGamesPage * GAMESPAGESIZE)
                   .Take(GAMESPAGESIZE);
}

Once the model supports this, the ViewModel can use it via a Command:

RelayCommand _prevPageCommand = null;

public RelayCommand PreviousPage 
{
  get
  {
    if (_prevPageCommand == null)
    {
      _prevPageCommand = new RelayCommand(
        () => _model.GetPrevPageGamesAsync(),
        () => Games != null && Games.Count() > 0);
    }
    return _prevPageCommand;
  }
}

private RelayCommand _nextPageCommand = null;

public RelayCommand NextPage
{
  get
  {
    if (_nextPageCommand == null)
    {
      _nextPageCommand = new RelayCommand(
        () => _model.GetNextPageGamesAsync(),
        () => Games != null && Games.Count() > 0);
    }
    return _nextPageCommand;
  }
}

Then these commands can be data bound to controls (in my case buttons). Typically I am immediately asked about how to know when you've reached the end of paging.  This is a little wierd because you could handle it by determining if the number returned is less than a full page but because it *can* be the last page and return the full number of the page you can't rely on it.  The other solution is to determine it on the server, but in most cases (as the server results can change in a Transactional System), I just try and get the next page and if the number of results equals zero, I move the page counter back one and reload the results.  Its not as efficent but it is only non-efficient in an edge case (returning exactly the page size as the last page.  In the Model I handle this like so:

else if (r.Entities.Count() == 0 && _currentGamesPage > 0)
{
  // If the page returned no results we
  // reached the end of a page edge
  // (important since we're not getting
  // full result counts from the server)
  GetPrevPageGamesAsync();
}

Dealing with Add/Delete

While I differ from the RIA teams view, I like to isolate the RIA Service layer inside the Model completely. That means that the model can be responsible for the actual adding removing of the items. But this represents a problem.  In our earlier parts to this series, I returned the results of the loading query directly to the ViewModel to use as it's data.  The datatype we returned was an IEnumerable<T> object.  Ordinarily this works but since we're supporting adding and removing, the IEnumerable<T> object doesn't support that. So I dug a little deeper to see if the actual returning object supported ICollection or ICollection<T>.  Nope.  In fact, the underlying object is a ReadOnlyCollection<T>. Yup, read-only.  So our original assumption to return the results wasn't helpful  Instead, I changed this to return the EntitySet directly from the RIA Services' Context object:

// Returning the raw Games collection since the
// entity results are a ReadOnly collection
evt(this, new EntityResultsArgs<Game>(Context.Games));

Since the underlying type that the EntityResultsHandler is expecting is still IEnumerable<T> we could do one of two things, either change the event to allow a richer object (e.g. ICollection) or let the model to be reponsible for Adding/Removing of objects.  I chose the latter:

public class GamesModel : IGamesModel
{
  ...
  
  public Game AddNewGame()
  {
    var g = new Game()
      {
        GameID = 0,
        Name = "*TODO*"
      };
    Context.Games.Add(g);
    return g;
  }

  public void DeleteGame(Game g)
  {
    Context.Games.Remove(g);
  }

  ...
}

I don't prefer this solution.  I would prefer to have a monitored ObservableCollection<T> that is watched for add/delete operations but since RIA Services doesn't work that way, this work around is acceptable.

To allow for any of the views to issue these commands, I chose to use another application message like we dicussed in prior parts of this series.  In this case, while the model is doing the real work of adding and removing, there is still some work to be done once the game is added or deleted.  For example here is the event for adding an item:

AppMessages.AddNewGameMessage.Register(this,
  ignore =>
  {
    CurrentGame = _model.AddNewGame();
  });

In the MVVM Light implementation of their Messenger, they don't allow for no actual data being sent with the message, so I just use an object and in the lambda I named it ignore so you could tell that it was a dummy piece of data. Inside the message handler, you can see I am calling the model to add the new game, but then setting the current game to be the new game (so that it can be immediately edited).  All of this happens in the GamesListViewModel so by setting the new game as the CurrentGame we get both that the ListBox in the view is selected as well as a message is being sent out (that the GameEditViewModel listens for) that a new CurrentGame has been selected which allows it to be edited.  Again, the model is responsible for the record-keeping, but the view model continues to work as expected to communicate with the actual view.

The delete message is the same:

AppMessages.DeleteCurrentGameMessage.Register(this,
  ignore => 
    {
      _model.DeleteGame(CurrentGame);
      CurrentGame = null;
    });

In this case we tell the model to delete the game and mark the current game as a null reference which prevents the editing in the edit view as well as deselects the item from the ListBox in the List view.  This pattern should allow you to continue separating your concerns between the different layers.

You can get the final version of the example here:

http://wildermuth.com/downloads/riaxboxgames.zip

XBoxGames Databases for SQL Server 2005 and 2008

Silverlight Logo

As many of you who've been following know, I use a simple database of XBox game data as my example database. I usually remember to include it on the server project but in some examples its been forgotten. In other cases I've shipped with a 2008 version of the database instead of the 2005 version. To address this, I've uploaded .zip'd versions of both the 2005 and 2008 databases for anyone to use for any reason. This includes my RIA Services sample (which is using a SQL Server 2008 version) or my MVVM MSDN article sample which also uses a 2008 version. They include a MDF and a LDF.  If you have trouble attaching them to an existing database, try deleting the LDF file.  Go grab there here:

 Any questions/problems, post a comment!

My Interview about Silverlight at Sweden's OreDev

Silverlight Logo

While in Sweden for OreDev, Microsoft pulled me aside to do a quick interview about Silverlight, Data and the reported death of WPF. Daniel Mellgaard Frost does a great job of asking me the tough questions about Silverlight. Note that this interview happened before Silverlight 4 was announced so some of my answers would be different today.