Lab - Get Now DOC by liwenting


									Hands-On Lab
Accessing Microsoft Dynamics CRM 2011
using LINQ
Lab version:    1.0.0
Last updated:   11/22/2011

OVERVIEW ................................................................................................................................................... 3

EXERCISE 1: CONNECTING TO THE SERVER ........................................................................................ 4
       Task 1 – Open the existing WPF Application......................................................................................... 4
       Task 2 – Getting Started with the LINQ provider .................................................................................. 5
   Exercise 1 Verification                                                                                                                                  6

EXERCISE 2: WCF LATE BOUND TYPES ................................................................................................. 6
       Task 1 - Open the solution you created in Exercise 1 in Microsoft Visual Studio ................................. 7
       Task 2 - Creating a new Location and room .......................................................................................... 7
       Task 3 - Retrieve a list of Locations ....................................................................................................... 8
       Task 4 - Retrieve Location details and Rooms when Location is selected ............................................ 9
   Exercise 2 Verification                                                                                                                                10

EXERCISE 3: GETTING STARTED WITH EARLY BOUND TYPES ........................................................ 11
       Task 1 - Use Typed Objects to Register a New Machine ..................................................................... 11
   Exercise 3 Verification                                                                                                                                14

EXERCISE 4: JOINS AND PAGING .......................................................................................................... 15
       Task 1 -Create some simulated usage data for the machine .............................................................. 15
       Task 2 – Load the usage log for the machine. ..................................................................................... 18
       Task 3 – Add Paging of the Usage Log Data ........................................................................................ 21
   Exercise 4 Verification                                                                                                                                23

SUMMARY .................................................................................................................................................. 23
Dynamics CRM 2011 introduces a new Language-Integrated Query (LINQ) provider for working with
data. This is in addition to and sits on top of the WCF interface to provide an additional layer of
abstraction and ease of use. The OrganizationServiceContext class implements the LINQ provider. The
LINQ provider can be used for both querying and modifying data. Developers will find the syntax for
building queries with the LINQ provider simpler than the using the QueryExpression or FetchXML is
style of queries. You can work with the new LINQ provider using both early and late bound data types.

Contoso is an independent software vendor (ISV) building solutions that are used by Gyms / Workout
facilities around the world to manage their operations. Contoso has chosen Microsoft Dynamics CRM
2011 as the application platform to build their next generation Fitness Center Management product.
Contoso will be selling this solution to Gym/Workout facilities and will allow them to further customize
it to fit their individual operating needs. Gym facilities will be able to install the appropriate solution
packages into their Dynamics CRM 2011 server in order to realize the benefits of the Fitness Center
Management application.
Contoso’s Fitness Center Management solution is a modular application that allows customers to select
just the modules that they need. During the course of this lab you are going to focus on modifying the
WPF application that will interact with the Fitness Center using the Dynamics CRM 2011 LINQ provider.
In this lab we will modify a Client that runs as a WPF application on the SuperDuperCycle 7000 Exercycle
which is built by a 3rd party, Fabrikam Fitness. Currently this application uses the WCF interface including
the generation of client side types to perform common operations on entities, both custom and existing,
in Dynamics CRM 2011. You are going to modify the application to use the features of the new
Dynamics CRM 2011 LINQ provider.

After completing this lab you will have accomplished the following objectives:

       Understand how to connect using the new LINQ provider
       Understand how to use the late bound types with the LINQ provider
       How to modify data using the LINQ provider

       Use related operations to optimize your interactions with the framework
       How to perform complex joins using the LINQ provider
System Requirements
You must have the following items to complete this lab:
        Microsoft Visual Studio 2010

        Dynamics CRM 2011 - Beta

This Hands-On Lab comprises the following exercises:
    1. Connecting to the server
    2. LINQ Late Bound Types
    3. Generating Client Types for Early Binding
    4. Updating Data using Related Operations

Estimated time to complete this lab: 30 minutes.

Exercise 1: Connecting to the server
In this exercise we will modify the existing Windows Presentation Foundation (WPF) client application
that connects to Dynamics CRM 2011 to create an instance of OrganizationServiceContext.
OrganizationServiceContext and implements the LINQ provider in Dynamics CRM 2011.

Task 1 – Open the existing WPF Application
In this task, you will prepare the WPF application that you will use for this Lab. The following steps will
walk you through opening an existing solution file provided with the lab. If you did the WCF lab then
you will recognize the application you built as part of the lab.
    1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010 |
       Microsoft Visual Studio 2010.
        2. Open the solution file CRM2011WCFCycleControler.sln that is located in the Source\Ex1-
           ConnectWithLINQ\Begin folder of this lab. This is a pre-built WPF application from the WCF
Task 2 – Getting Started with the LINQ provider
The OrganizationServiceContext class implements the LINQ provider in Dynamics CRM 2011. You can
use that class directly using late binding style of development. Similar to the WCF lab, when you run the
code generation an organization specific context can be generated also. The generated class has the
advantage of having properties for each of the entities in your data model making it easier to leverage
intellisense. It also has methods that are custom to each of the entities for doing things like adding new
records. Getting started using either technique is very similar so in this task we will add an instance of
both so we can see the difference later. In this task you will create an instance of
OrganizationServiceContext and ContosoGymServiceContext (the generated one) passing it the existing
instance of the IOrganizationService. If you recall, IOrganizationService is the WCF interface and the
LINQ provider wraps around that to provide another layer of abstraction.
    1. Inside of the MainWindow class locate the OrgService private property.
    2. Insert the following properties below the OrgService property.

        //Generic Organization Context used for Late Binding LINQ
        private OrganizationServiceContext OrgContext { get; set; }

        //Generated Organization Context used for Early Binding LINQ
        private ContosoGymServiceContext GymOrgContext { get; set; }

    3. Locate the btnConnect_Click method and inside it find the line just before where
       WhoAmIRequest is defined. Insert the following code to create a new instance of the service
       contexts using the OrgService instance that was just created.

        //Get a new instance of the Late Binding Organization Context
        this.OrgContext = new OrganizationServiceContext(OrgService);

        //Get a new instance of the Early Binding Organization Context
        this.GymOrgContext = new ContosoGymServiceContext(OrgService);

         Note: Since this is a WPF application, we have kept these properties at the class level.
         Depending on the application style you are building, you should ensure they are scoped
         appropriately. Reference the SDK for any specific guidance on how to dispose of these
         instances depending on your scenario.

    4. While you can still use the OrgService (IOrganizationService) to call the Execute method the
       OrganizationServiceContext class also provides a similar method. Sometimes it is easier to use
       that one so you don’t have to keep around references to both instances. Locate the following
       line in the btnConnect_Click method and replace the following line:
        Figure 1
        Replacing OrgService with OrgContext

        var response = OrgContext.Execute(req) as WhoAmIResponse;

Exercise 1 Verification
In order to verify that you have correctly performed all steps of Exercise 1, proceed as follows:

Verification 1
In this verification, you will run the WPF application and attempt to connect to the server.
    5. Hit the F5 button to start the application.

         Note: You might get a message that the solution file is read-only it is ok to overwrite.

    6. Click the Discover button in the application to attempt to retrieve a list of the organizations
    7. Select Contoso7 from the combo box list of organizations
    8. Click the Connect button
    9. You should see a message box showing the Guid for the currently connected user.

Exercise 2: WCF Late Bound Types
 Important: In this Exercise navigate to http://crm/Contoso7

In this exercise you will use the LINQ provider to access the Dynamics CRM data using late bound types.
Late bound types allow you to work with the data in CRM without having classes representing each data
entity compiled into your project. ISVs often use late bound types when working with entities that they
can't anticipate at the time they build their code. For example, an ISV building a utility that works with
any entity could utilize this capability.
This technique can also be helpful to support situations where a user customizes an entity after you
have shipped your code but you still need to access the new custom attributes. In prior versions of
Dynamic CRM using late bound types via the DynamicEntity class was the only viable technique. With
Dynamics CRM 2011 the early bound types also inherit from the Entity class so it is possible and typically
more desirable to use the early bound types for the attributes you know at compile time and drop to the
Attributes keyed collection for those that you don't.

In this exercise, we are going to build a query to retrieve all the Gym locations. Additionally, you will use
a new feature of the LINQ provider to retrieve the Room records that are related to each Gym. So you
can see how the LINQ provider differs from the WCF interface we will be converting the WCF code from
the WCF lab to use the LINQ provider.

Task 1 - Open the solution you created in Exercise 1 in Microsoft Visual Studio
In this task, you are going to open the solution you created in Exercise 1. If you did not complete
Exercise 1 you can open the completed solution from E:\TrainingKit\Labs\LINQ\Sources\Ex1-
ConnectWithLINQ\End\CRM2011WCFCycleControler.sln. The code from Exercise 1 will get an instance
of the OrganizationServiceContext and set the OrgContext class scoped property.

Task 2 - Creating a new Location and room
In this task, we are going to use the organization context (OrganizationServiceContext) instead of the
organization service (IOrganizationService) to create a new Location record and an associated Room
record. When we did this with WCF it demonstrated how a new feature of the WCF Interface that
allows creating associated records in a single call to the service. Now with the LINQ provide we will see
how you can accomplish the same thing with slightly different syntax.
    1. Locate the btnCreateLocationRoom_Click method in MainWindow.xaml.cs. This method sets up
       an Entity class for Location and Room. This same setup works for the LINQ provider as well.
       Locate and comment out the line that calls this.OrgService.Create(location1). Insert the
       following code following that commented out line.


          Note: The AddObject method tells the OrganizationServiceContext to track this object. Unlike
          calling the Create method on IOrganizationService the AddObject method does not actually
          create the data record. In fact, it doesn’t even interact with the server. Its sole job is to attach
          the object to the context as a new record.
    2. In order to actually save the record on the server it is necessary to call the SaveChanges method
       on the OrganizationServiceContext. When called, the SaveChanges method will cause any
       modified data being tracked by the context to be sent to the server. Insert the following code
       after the AddObject inserted in the previous step.


          Note: In this example similar to WCF this is done within a single interaction with the server and
          as a single transaction. However, If we had added another Location or any other non-related
          data, multiple interactions would be done with the server (using multiple unrelated

Task 3 - Retrieve a list of Locations
In this task, you will convert form using a QueryExpression to using a LINQ query to retrieve all of the
Locations and populate a ListBox. In our example, we will simply retrieve all the Locations.

    1. Locate the btnRefreshLocations_Click method in the code behind. Comment out the existing
       code – leave it there so you can compare the difference to the new query.

    2. Insert the following code into the btnRefreshLocations_Click method. This code now does a
       LINQ query using the OrgContext. CreateQuery returns an IQueryable of account records. The
       Select shapes the results into an anonymous type that has Name and LocationID properties.
       Alternatively, the query could have specified select loc instead which would have caused all
       attributes to be returned. Using specific columns is the same as specifying a list of columns on
       the QueryExpression.
        var query = from loc in OrgContext.CreateQuery("account")
                    select new
                        Name = loc["name"],

        listLocations.ItemsSource = query.ToList();
         Note: Notice that there is no specific call to the server. The “query” simply defines the LINQ
         query but doesn’t cause a call to the server. The call happens behind the scene when the
         ToList() is called on the query. This would also happen if you triggered the enumeration in any
         other way as well.

Task 4 - Retrieve Location details and Rooms when Location is selected
In this task, we are going to retrieve the location details and the associated rooms when the selection
changes on the Location Listbox. Using a new feature of the API we are going to retrieve the Location
and the related Room records using a single call to the server.
    1. Locate the listLocations_SelectionChanged method in the code behind file for MainWindow
       and comment out the existing WCF code that is in the method. Don’t just delete it, keep it
       around so we can compare the differences.
    2. Insert the following line back into the method – this is still needed to get the selected ID of the
        Guid locationID = (Guid)listLocations.SelectedValue;

    3. Instead of the QueryExpression again we are going to replace it with a LINQ query. This time we
       are adding a where statement to qualify that we only want records matching the Location ID.
       Additionally, we are sorting by the name of the room. Similar to last time we are shaping the
       results into an anonymous type that has only a couple properties we need.

        var roomQuery = from r in OrgContext.CreateQuery("contoso_room")
                   where r.GetAttributeValue<Guid>("contoso_locationid") == locationID
                   orderby r["contoso_name"]
                   select new
                       Name = r["contoso_name"],
                       RoomID = r["contoso_roomid"]

        listRooms.ItemsSource = roomQuery;

    4. Next, we want to retrieve the location so we can get location address to display. So far we have
       used the LINQ query syntax. C# introduced this syntax as part of C# 3.0. The runtime however
        does not understand query expressions so it is translated to method calls at compilation time.
        It is possible then to also use the method syntax with the LINQ provider as well. The method
        style can be more compact for simple queries. Insert the following method style query into the
        method following the query we just added for rooms.

          var location = OrgContext.CreateQuery("account")
             .Where(a => ((Guid)a["accountid"]) == locationID).FirstOrDefault();

          if (location != null)
              LocationAddress.Text = location["address1_line1"].ToString();

Exercise 2 Verification
In order to verify that you have correctly performed all steps of Exercise 2, proceed as follows:

       1. Start the application by pressing F5 from within Visual Studio.
       2. Click on the Discover button to retrieve all the organizations configured on the server.
       3. Choose Contoso7 from the list of available organizations
       4. Click the Connect button to cause the application to connect to the service and retrieve the
          ID of the user.
       5. You should now see a message box showing the ID of the current user.
       6. In the Location Name textbox input a value of "Fitness Now - Lab Test"
       7. In the Room Name textbox input a value of "Room #1 - Lab Test"
       8. Click on the Create button
       9. You should now see a message box displaying the ID of the location you just created.
       10. Next, load the Locations list box by clicking the Refresh button
       11. You should now see a list of locations in the list box.
       12. Select the Fitness Now - Lab Test location that you just created
       13. The right list box of Rooms should now show Room#1 - Lab Test and the Address Detail at the
           bottom of the application should be populated with the street information.
Exercise 3: Getting Started with Early
Bound Types
 Important: In this Exercise navigate to http://crm/Contoso7

The new WCF interface provided by Dynamics CRM 2011 is no longer dynamically generated as the
ASMX web service WSDL was in prior versions. It turned out that generating all those data types slowed
down the performance of CRM. Dynamics CRM 2011 takes a different approach by using a separate
utility that will generate the typed classes based on data retrieved from the Metadata service. These
typed classes inherit from the same Entity class you worked with in the prior exercise. In fact you can
still work with the typed classes in the same way you worked with the Entity object if needed allowing a
combination of both early bound and late bound techniques to be used in the same piece of code.

The LINQ provider also works with the same generated typed classes. The one thing that is not
automatically there for you is the typed service context. The typed service context is a generated class
that inherits from OrganizationServiceContext and has properties for each entity in CRM. They methods
are named <EntityName>Set. This property is of type IQueryable<EntityName> and internally calls
CreateQuery when referenced just like we did in the prior exercise. The difference is you can simply use
Visual Studio Intellisense to look up the names instead of cryptic strings. In order for this to occur you
must specify the /servicecontextName option when you run CrmSvcUtil.

Task 1 - Use Typed Objects to Register a New Machine
In this task, you are going use the generated classes to create a new exercise equipment record.
       1. In the code behind for MainWindow.xaml.cs locate the btnRegisterMachine_Click event
          handler and replace the line with OrgService.Create with the following code. Notice in this
          example we are using the generated methods specific to each entity. In this case, we are
          calling AddTocontoso_exerciseequipmentSet.



       2. Next we want to Update the service status of the machine to ensure it’s in service. Of course
          we could have set that during the create, but that wouldn’t let you do an Update. Service
          status is an OptionSet, which is the new name for a Picklist. The first step we need to do is
    locate the values for the OptionSet. You can do that by going to the Field on the
    contoso_exerciseequipment entity and looking up the values as you can see below.

         Figure 2
         List of OptionSet Values
3. Clicking on each of the items you can see the value for the option. The starting number for
   OptionSet values are determined by the Publisher prefix. It generates a default value that
   you can modify if you desire.

         Figure 3
         Value for Need Service
4. Currently, the code generator does not create enum types for OptionSets. So insert the
   following manually created enum into MainWindow.xaml.cs.
         public enum ExerciseEquipmentServiceStatus
            InService = 100000000,
            NeedsService = 100000001,
            OutOfService = 100000002

          Note: These values can also be retrieved using the Metadata API messages. Using this
          approach you could also hook into the extensions provided by crmsvcutil and have it
          generate the OptionSet enum as part of its code generation.

5. Insert the following line of code to set the value of service status after the MessageBox that
   prints the ID of the machine that was registered.

         newMachine.contoso_ServiceStatus =
             new OptionSetValue((int)ExerciseEquipmentServiceStatus.InService);
6. The LINQ provider requires an explicit method call to UpdateObject to notify the service
   context that the object has been modified. Insert the following line after the setting of the
   status to execute the UpdateObject call.


7. If you were try to run the application right now you would get the following error when
   UpdateObject was called.

         Figure 4
         Error after UpdateObject
8. The above error occurs because after the SaveChanges is called the instance is no longer
   tracked. To accommodate for that we would first need to call Attach to indicate we want the
   service context to track the object instance. Insert the following line just before the call to


9. Ideally, that should be all you need to do but it turns out if you ran it now, you would see a
   slightly different error. The following error that you would get indicates that the EntityState
   is not valid for Attach.

         Figure 5
         Error trying to Attach
       10. This error appears because we just created the entity. The CRM team is looking into clearing
           the EntityState so that the object can be re-attached without any other action, but currently
           you must null the state first. Add the following line just before the call to Attach and it will
           clear the EntityState.

                 newMachine.EntityState = null;

       11. If you ran it now, you would get further, but still get an error because the Tasks that we
           inserted with the new machine are still part of the RelatedEntities on the object instance.

                 Figure 6
                 Error from related tasks
       12. Insert the following line after the setting of EntityState. This will clear the RelatedEntities
           collection since we aren’t needing them for the next operation.


Exercise 3 Verification
In order to verify that you have correctly performed all steps of Exercise 3, proceed as follows:

Verification 3
In this verification, you will run the application and attempt to create a new exercise equipment record
and the associated tasks.
    1. Press F5 to run the application.
    2. Click the Discover button to populate the list of organizations.
    3. Select Contoso7 from the list of organizations and press the Connect button
    4. On the Register Machine tab, click the Register Button.
    5. You should see a message box when the create method is completed showing the ID of the new
    6. From the CRM Web Client, Navigate to http://crm/contoso7 and browse to the Equipment
       Management section and click on the Exercise Equipment menu item.
    7. You should see the new equipment record that you just created.
    8. Open the new record and view the Activities for the record and you should see the four
       activities we specified as part of the create operation.

Exercise 4: Joins and Paging
 Important: In this Exercise navigate to http://crm/Contoso7

Developers can build queries of varying level of complexity. The OrganizationServiceContext supports
building queries that include join and paging. In this exercise, you will be adding support to the
application for viewing the usage log of the machine.
Task 1 -Create some simulated usage data for the machine
In this task, we are going to use the OrganizationServiceContext to create some simulated usage data so
we can display it in the subsequent task.
      1. Open MainWindow.xaml and locate the last </TabItem> and insert the following XAML which
         will add a tab for the usage log. It contains a button to create and load the data, a combo
         box to select a trainer and a DataGrid for display of the usage log..
         <TabItem Header="Usage Log">
                           <StackPanel x:Name="gridPanel" Orientation="Vertical">
                               <StackPanel Orientation="Horizontal">
                                   <Button Content="Create Usage Data" Height="23"
       Name="btnCreateUsage" Width="107" Click="btnCreateUsage_Click" />
                                   <Button x:Name="btnLoadUsage" Content="Load Usage"
       Margin="10,0,0,0" Click="btnLoadUsage_Click" Width="100"></Button>
                                   <TextBlock Margin="10,0,2,0"
       VerticalAlignment="Center">Trainer: </TextBlock>
                                   <ComboBox x:Name="cmbTrainers"
       SelectionChanged="cmbTrainers_SelectionChanged" DisplayMemberPath="Text"
       SelectedValuePath="Value" Width="150" />
                               <ScrollViewer VerticalAlignment="Stretch"
       HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" Height="200">
                                   <DataGrid x:Name="usageList"></DataGrid>

2.   In the MainWindow.xaml.cs file, insert the following method shell that will be the event
     handler when the user clicks the Create Usage Data button. .

          private void btnCreateUsage_Click(object sender, RoutedEventArgs e)


3. As part of building the usage log data, we will be querying gym members and trainers. To
   make the code cleaner insert the following enum into the MainWindow.xaml.cs class to
   define the values for the OptionSet on person for type. By default, crmsvcutil does not
   generate enums for the OptionSet values. You can, via an interface, implement custom logic
   to automate the generation or you can hand create the enums as we are doing in this
          enum PersonType
             GymMember = 100000000,
             Trainer = 100000001

4. Insert the following code into the btnCreateUsage_Click method to query the Exercise
   Equipment for the machine named “SuperDuperCycle1”. Notice we are only interested in
   the first one in case there are duplicates.

         var equip = (from eq in GymOrgContext.contoso_exerciseequipmentSet
           where eq.contoso_name.Equals("SuperDuperCycle1")
           select eq).FirstOrDefault();

              Note: The SingleOrDefault method could have also been used in place of
              FirstOrDefault but it will throw an exception if there is more than one record found.
5. Insert the following query after the equipment query. This query will look for contacts that
   have a type of Trainer.
          var trainers = (from m in GymOrgContext.ContactSet
                  where m.contoso_Type.Value.Equals((int)PersonType.Trainer)
                  select m);

6. Insert the following query after the trainer query. In this query we are filtering contacts on
   type GymMember. Additionally, we are using the Take method to indicate we are only
   interested in retrieving the first 10 records matching the criteria. The Take method is added
   to the LINQ expression and processed as part of the server query.

         var members = (from m in GymOrgContext.ContactSet
          where m.contoso_Type.Value.Equals((int)PersonType.GymMember)
          select m).Take(10);

7. Insert the following code after the members query. This code will loop through each of the
   members and call the CreateMemberUsageWorkouts method. The method will create
   workout records for each member.
               Random rnd = new Random();

               foreach (Contact member in members)
                   CreateMemberUsageWorkouts(equip, trainers, rnd, member);

8. The CreateMemberUsageWorkouts method doesn’t exist yet. We are going to use a new
   feature of Visual Studio 2010 that will allow us to generate a shell of that method from
   usage. Click on the method name in the code and press ctrl-. (control – period). This will
   show the Generate method option which you should click with the mouse as you can see in
   the following image.
                Figure 7
                Generate from Usage

       9. In the CreateMemberUsageWorkouts method we need to build out the code to create a
          new workout instance. Insert the following code inside the method.
        //Create workouts for each user
        contoso_workout newWorkout = new contoso_workout();

        var trainer = trainers.Skip(rnd.Next(0,
        newWorkout.contoso_fitnesscentermemberid = new
        EntityReference(Contact.EntityLogicalName, member.Id);
        newWorkout.contoso_personaltrainerid = new
        EntityReference(Contact.EntityLogicalName, trainer.Id);
        newWorkout.contoso_name = String.Format("{0}-{1}-{2}", equip.contoso_name,
        member.FullName, trainer.FullName);


        //Now create executed exercises for a specific machine for the workout
        contoso_executedexercise newExercise = new contoso_executedexercise();
        newExercise.contoso_exerciseequipmentid = new
        EntityReference(contoso_exerciseequipment.EntityLogicalName, equip.Id);
        newExercise.contoso_workoutid = new
        EntityReference(contoso_workout.EntityLogicalName, newWorkout.Id);
        newExercise.contoso_name = String.Format("{0}-{1:MMddyy}",
        newWorkout.contoso_name, System.DateTime.Now);


Task 2 – Load the usage log for the machine.
In this task, we are going to query the usage data created in the prior task to show the usage data of the
1. In the MainWindow.xaml.cs add the following method shells for the button handler and the
   combo box selection change.
          private void btnLoadUsage_Click(object sender, RoutedEventArgs e)


          private void cmbTrainers_SelectionChanged(object sender, RoutedEventArgs e)


2.    Insert the following code into the btnLoadUsage_Click method to query and retrieve the
     exercise equipment machine record. We will use this to get the ID that later will be used to
     filter the usage log.
         var equip = (from eq in GymOrgContext.contoso_exerciseequipmentSet
                where eq.contoso_name.Equals("SuperDuperCycle1")
                select eq).FirstOrDefault();

              Note: The above query returns the full exercise equipment object, for our purposes
              you could also change this to return just the ID by altering the select to just be select
              eq.Id. In this case the equip type would be an EntityReference and not a full object.

3. Insert the following code to start building the query of usage after the equipment query. At
   this point we are going to just insert the first part of the query. To retrieve usage we are
   going to query against the executed exercises.
          var usageQuery = (from ee in GymOrgContext.contoso_executedexerciseSet

4. Add the following Join statement to the query you added in the previous step. This join is
   between the workout and the executed exercise.
       join workout in GymOrgContext.contoso_workoutSet on ee.contoso_workoutid.Id
 equals workout.contoso_workoutId.Value
          Note: It is important to notice that the equals operator is used instead of = that is
          required when working with Joins.

5. Add the following Join statement to the query following the workout join you added in the
   previous step. This join is between the workout and contact to locate the gym member.
         join mem in GymOrgContext.ContactSet on
              workout.contoso_fitnesscentermemberid.Id equals mem.ContactId

6. Add the following Join statement to the query following the workout join you added in the
   previous step. This join is between the workout and contact to locate the personal trainer.
        join trainer in GymOrgContext.ContactSet on
           workout.contoso_personaltrainerid.Id equals trainer.ContactId

7. Add the following Where statement to filter the executed exercises on the particular
   machine we are trying to show.

        where ((ee.contoso_exerciseequipmentid != null) &&
             (ee.contoso_exerciseequipmentid.Id == equip.Id))

          Note: If you wanted to also add more filtering on the trainer or the gym member
          attributes you can add additional where clauses to the query. If you simply added the
          criteria to the existing where clause it would generate an error. Each different entity
          criteria must be specified on its own where clause. You can have multiple where
          clauses though.

8. In this example, we are going to use the following UsageLogDetail class to hold the retrieved
   usage data. Insert the following class into MainWindow.xaml.cs inside the namespace but
   outside the MainWindow class. If you want you can create and place the class in a separate
   UsageLogDetail.cs file.

         internal class UsageLogDetail
            public string Member { get; set; }
            public string Trainer { get; set; }
            public DateTime DateUsed { get; set; }
            public Guid TrainerID { get; set; }

       9. To finish off the query add the following select to project the results into the shape of the
          UsageLogDetail class.
               select new UsageLogDetail
                 Member = mem != null ? mem.FullName : "No Member",
                 TrainerID = trainer != null ? trainer.ContactId.Value : new System.Guid(),
                 Trainer = trainer != null ? trainer.FullName : "No Trainer",
                 DateUsed = ee.CreatedOn.Value

       10. Later we are going to add paging so add the following class level variable to hold the results.
                private List<UsageLogDetail> _usageList = new List<UsageLogDetail>();

       11. Save off the results of the query to a results variable then add that to the class level property.
           Additionally, we are going to clear the ItemsSource on the DataGrid and set it to the current
           values in _usageList
                       var results = usageQuery.ToList();


                      usageList.ItemsSource = null;
                      usageList.ItemsSource = _usageList;

       12. At this point you can try to run the application, connect to CRM using Contoso7 and proceed
           to the Usage Log tab and try the application. To do that simply click on the Create Usage
           data to generate some test data and then click the View Usage Log button to do the query.

Task 3 – Add Paging of the Usage Log Data
In this task, we are going to modify the application to use progressive paging to retrieve the usage data.
We will start by displaying the first few records and only retrieve additional as requested by the user. To
retrieve the additional data we are going to use Skip and Take to specify what portion of the data should
be retrieved. Using these methods the query expression is altered prior to execution and only the
appropriate records are returned from the server.
       1. In the MainWindow.xaml.cs add the following class level variable to indicate there are more
                 private bool _MoreRecords = true;
                 int _PageSize = 15;

                  Note: Using the ServiceContext there isn’t a way to get the total records back as part
                  of the original query. So instead of using paging where we tell them page 1 of 10 we
                  are going to use progressive paging where we retrieve more data as requested and let
                  them know when there is not any more to get. Alternatively, you could do a query to
                  simply retrieve a count of the records but that would add additional overhead.

       2.   Insert the following code into the btnLoadUsage_Click method prior to the equipment query.
            This will check if there are more records and warn the user when no more are available.
               if (!_MoreRecords)
                   MessageBox.Show("No More Records");

       3.    Next, we need to modify the query expression. The most obvious way to do this would be
            to continue to add on to the query we built. Instead of doing that we wanted to
            demonstrate that you could further compose the query. Using the method style we are
            going to add on paging to the query. Insert the following line that alters the usage query
            right after the existing query.
              //The 'Skip' value must be a multiple of the 'Take/Top' value.
              usageQuery = usageQuery.Skip(_usageList.Count).Take(_PageSize);

                  Note: Make sure the Skip value is a multiple of the PageSize passed to the Take. For
                  example if PageSize is 10, You can Skip 40, but Not 41 or you will receive an error. In
                  addition, Skip must be specified before Take.

       4. Page the following check after the results are retrieved to see how many records are
          returned. If less than the full PageSize was returned we have reached the end of the records.
                 if (results.Count < _PageSize)
                    _MoreRecords = false;

Exercise 4 Verification
In order to verify that you have correctly performed all steps of Exercise 4, proceed as follows:

Verification 4
In this verification, you are going to make sure that the new diagnostic entity has been created and you
can log a simulated error message
    1.   Press F5 to run the application
    2. Click the Discover button to populate the list of organizations
    3. Select Contoso7 from the list of organizations and press the Connect button
    4. On the Usage Log tab click the Create Usage Data button.
    5. After that process completes click the Load Usage Data button to load the log, click again to
       load more rows until you receive the No More Records message box.

In this lab you have experienced using the new Microsoft Dynamics CRM 201 LINQ Provider. The LINQ
provider allows developers to specify queries in a more natrual syntax when compared to the
QueryExpression style that had been previosuly used. In addition to querying, the LINQ provider
provides a Data Context style interaction for data modification. The developer will make one or more
changes to data that are tracked by the Data Context and then commited to the server only when
SaveChanges is called. In this Lab you worked with the LINQ provider using both early bound typed
objects and late bound dynamic objects where the schema didn’t have to be known ahead of time.

To top