Step 5: Create the WCF RIA Services Project

If you haven’t already, read the article this example is based on or start at the beginning of this example.

NOTE: I’m using WCF RIA Services for Silverlight 4 (not the SP1 beta) available at http://www.silverlight.net/getstarted/riaservices/ for this example.

Add a Silverlight Business Application project called CSSCustomerEdit to your solution:

SNAGHTML4f50d71

Copy the connectionStrings element from CSSModel’s App.Config to CSSCustomerEdit.Web’s Web.config (make sure to put it after configSections, which needs to be the first element in configuration):

image

Add references to CSSModel and System.Data.Entity to CSSCustomerEdit.Web’s references:

SNAGHTML4fb6d3d

Set Copy Local to true for the assemblies under System.ServiceModel.DomainServices:

image

In Solution Explorer, right-click on the Services folder and Add | New Item … choose Domain Service Class and call it CSSDomainService (important to end the name in “Service”):

SNAGHTML500ad87

Select all the entities and enable editing for each:

SNAGHTML50eadc6

Now it’s time to implement the actual behavior of this use case. Here’s a nice big old chunk of code for handy copy-and-pasting … read Step 5 in the article to understand it:

[EnableClientAccess()]
public class CSSDomainService : LinqToEntitiesDomainService<CSSModelContainer>
{
    private int myCompany = 1; // TODO: set myCompany during authentication
    public Company GetMyCompany()
    {
        return this.ObjectContext.Companies.Single(c => c.Id.Equals(myCompany));
    }

    public void UpdateCompany(Company currentCompany)
    {
        // TODO: ensure user only edits authorized companies
        this.ObjectContext.Companies.AttachAsModified(currentCompany, this.ChangeSet.GetOriginal(currentCompany));
    }

    public IQueryable<Story> GetMyStories()
    {
        return this.ObjectContext.Stories.Where(s => s.CompanyId.Equals(myCompany));
    }

    public void InsertStory(Story story)
    {
        story.CompanyId = myCompany;
        story.Id = -1; // database will replace with next auto-increment value
        if ((story.EntityState != EntityState.Detached))
        {
            this.ObjectContext.ObjectStateManager.ChangeObjectState(story, EntityState.Added);
        }
        else
        {
            this.ObjectContext.Stories.AddObject(story);
        }
    }

    public void UpdateStory(Story currentStory)
    {
        // TODO: ensure user only edits authorized stories
        this.ObjectContext.Stories.AttachAsModified(currentStory, this.ChangeSet.GetOriginal(currentStory));
    }

    public void DeleteStory(Story story)
    {
        // TODO: ensure user only deletes authorized stories
        if ((story.EntityState == EntityState.Detached))
        {
            this.ObjectContext.Stories.Attach(story);
        }
        this.ObjectContext.Stories.DeleteObject(story);
    }
}

Here’s an image of the class with syntax highlighting:

image

For the UI, I want to copy the Home and About pages (which would presumably have descriptions of the app at some point) and add functionality. The UI is implemented in CSSCustomerEdit. Start by adding references to System.Windows.Controls.Data and System.Windows.Controls.DomainServices. Then right-click on the Views folder, Add | New Item to create a new Silverlight Page named Company (singular – users can only edit their own company). Do the same for Stories (plural):

SNAGHTML52eed17

In ApplicationStrings.resx (under the Assets | Resources folder), add strings for CompanyPageTitle and StoriesPageTitle, then build the solution:

image

Copy the XAML within the Grid element from Home.xaml to Company.xaml and Stories.xaml. Change “Home” appropriately:

image

In MainPage.xaml, copy the Rectangle and the HyperLinkButton that follows and paste it twice after. Change the x:Names of each to be unique and change “About” appropriately:

image

Run it at this point to make sure your copies worked … especially for xaml apps, I like to add functionality bit-by-bit and run it a lot to make sure I didn’t break something.

Editing XAML directly is a bit cumbersome, but the results are very powerful once you get used to it. It might be helpful to download the code sample from the article and look at complete .xaml files.

Open Company.xaml, and add the following three namespaces amongst the other xmlns entries:

xmlns:domainservices="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
xmlns:css="clr-namespace:CSSCustomerEdit.Web.Services" 
xmlns:dataform="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"

Add the following as the first element within the Grid element:

<domainservices:DomainDataSource x:Name="MyData" AutoLoad="True" QueryName="GetMyCompany">
  <domainservices:DomainDataSource.DomainContext>
    <css:CSSDomainContext />
  </domainservices:DomainDataSource.DomainContext>
</domainservices:DomainDataSource>

This sort of thing always looks odd to me in the declarative syntax of XAML, but ultimately it creates a bindable data set called MyData that calls the GetMyCompany query I defined earlier in CSSDomainService (magically “linked” to CSSDomainContext by WCF RIA Services).

Replace the second TextBlock with a DataForm. You can get something interesting with as little as this:

<dataform:DataForm x:Name="CompanyDetails" CurrentItem="{Binding ElementName=MyData, Path=Data.CurrentItem}"></dataform:DataForm>

which binds a DataForm to the MyData element we created above. However, after much tinkering with attribute values (IntelliSense is very helpful here), I settled on the following, which includes an EditTemplate and an EditEnded event:

<dataform:DataForm x:Name="CompanyDetails"
        Header="Company Details"
        AutoGenerateFields="False"
        AutoEdit="True"
        AutoCommit="False"
        CommandButtonsVisibility="Commit,Cancel"
        CommitButtonContent="Save"
        IsEnabled="True"
        IsReadOnly="False"
        CurrentItem="{Binding ElementName=MyData, Path=Data.CurrentItem}"
        EditEnded="CompanyDetails_EditEnded"
        Margin="0,12,0,0">
                    <dataform:DataForm.EditTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <dataform:DataField Label="Name: ">
                                    <TextBox Text="{Binding Name, Mode=TwoWay}" />
                                </dataform:DataField>
                                <dataform:DataField Label="Country: ">
                                    <TextBox Text="{Binding Country, Mode=TwoWay}" />
                                </dataform:DataField>
                            </StackPanel>
                        </DataTemplate>
                    </dataform:DataForm.EditTemplate>
                </dataform:DataForm>

Here’s an image of the entire Company.xaml file with syntax highlighting (and key parts circled):

image

Before you can run this, you’ll need to right click on CompanyDetails_EditEnded and Navigate to Event Handler. In that method, add the following to make your edits persistent:

if (MyData.HasChanges)
{
    MyData.SubmitChanges();
}

Stories is similar but adds a DataGrid above the DataForm. this requires an additional namespace entry:

xmlns:datagrid="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

The domain context is the same as above, but change GetMyCompany to GetMyStories.

I replaced the second TextBlock with a DataGrid followed by a DataForm:

<datagrid:DataGrid x:Name="StoryGrid" Height="120" ItemsSource="{Binding ElementName=MyData, Path=Data}" AutoGenerateColumns="False">
    <datagrid:DataGrid.Columns>
        <datagrid:DataGridTextColumn Header="Headline" Binding="{Binding Headline}" />
    </datagrid:DataGrid.Columns>
</datagrid:DataGrid>

<dataform:DataForm x:Name="StoryDetails" Header="Story Details" AutoGenerateFields="False" AutoEdit="True"
    AutoCommit="False" CommandButtonsVisibility="All" CommitButtonContent="Save" IsEnabled="True"
    IsReadOnly="False" ItemsSource="{Binding ElementName=MyData, Path=Data}"
    EditEnded="StoryDetails_EditEnded" Margin="0,12,0,0">
    <dataform:DataForm.EditTemplate>
        <DataTemplate>
            <StackPanel>
                <dataform:DataField Label="Headline: ">
                    <TextBox Text="{Binding Headline, Mode=TwoWay}" />
                </dataform:DataField>
                <dataform:DataField Label="Detail: ">
                    <TextBox Text="{Binding Detail, Mode=TwoWay}"  TextWrapping="Wrap" MinHeight="160" AcceptsReturn="True" />
                </dataform:DataField>
            </StackPanel>
        </DataTemplate>
    </dataform:DataForm.EditTemplate>
</dataform:DataForm>

Here is an image of the entire Stories.xaml with syntax highlighting:

image

Rename CSSCustomerEditTestPage.aspx to Default.aspx so you don’t have an ugly URL.

And the final result in action:

SNAGHTML5aed48b

Publish to Azure

Again, I’m deviating from the article here and sharing a single Azure Services project across all examples. So in solution explorer, go to the CSSService project and delete CSSPublic as the associated web role, then add CSSCustomerEdit.Web:

image

Publish to Azure as in step 3; verify that your app works in the cloud, then stop AND DELETE the web role in the Windows Azure account portal so that you don’t incur charges.

Continue to next step – running all three use cases in a single web role

Leave a Reply

Your email address will not be published. Required fields are marked *