Archive

Posts Tagged ‘LINQ’

Using WCF RIA Services to allow projections that support Lookups – Part II

August 30, 2011 Leave a comment

Now that we are fairly comfortable with the idea of using a generic class to act as a property bag, we can now take this implementation a little further and provide some cool advancements.

One area where I have found this to be a useful solution is dealing with my reporting architecture. Here is a screen in my application that uses this advancement. I basically have a dynamic report driver screen. It allows me to evaluate any uploaded report and provide parameters if necessary.

Running the report gives us the following screen:

We use ComponentOne for our reporting because they have a very nice and mature independent report designer that allows our clients to define their reports without the need for Visual Studio. Typically our user base is not IT or technically advanced and trying to teach them to use Visual Studio for just doing reports doesn’t make sense. The nice thing about the reports is that they are just like Microsoft Reporting Services, in that the definition of the report is just XML. The end-user creates the report and then uses my application to upload the report definition to the database where it is stored. This makes for a very beefy record and can really bog down your application if you want to test your reports. The one catch with reporting is that they can also have parameters. This calls for a data structure that supports one-to-many.

Let’s look quickly at the two tables involved in handling reporting.

AS you can see I have a reporting table and a report parameter table. If I wanted to use the approach that I used from my last post, I wouldn’t be able to accomplish this. We will need to modify the data object to support this type of hierarchy.

public class ResultDTO
{
    [Key]
    public int Value { get; set; }
    public string Name { get; set; }
    public string FriendlyName { get; set; }
    public int ParentValue { get; set; }
};

public class ResultWithCollectionDTO
{
    [Key]
    public int Value { get; set; }
    public string Name { get; set; }
    public string FriendlyName { get; set; }
    public int ScreenId { get; set; }
    [Include]
    [Association("ResultWithCollectionDTO_ResultDTO", "Value", "ParentValue")]
    [Composition]
    public IEnumerable<ResultDTO> Children { get; set; }
};

If we look at the ResultDTO object, only one change has been made. It now has a int property labeled, “ParentValue”. Although not required from an ObjectOriented perspective, this is required from a WCF RIA Services navigation perspective.

Let’s look at the newly introduced table. We see that it has the same properties as the ResultDTO as well as an int property labeled, “ScreenId”. This is necessary because the underlying architecture has an identifier for each screen and depending on what screen you are on you could have one or more reports associated with it. By having this metadata I can then automatically show a default report for a screen or group reports together with the same ScreenId and use that grouping for hydrating a ComboBox for selecting which report to render.

The last property is what gives us the hierarchy we need to support parent child relationships. Using WCF RIA Services and eager loading the whole object is what I am trying to avoid by providing this solution. Because the report table has the report XML definition, it is extremely large and I don’t want to download this information when all I am trying to do is bind to a report generically. This property is just an IEnumerable collection of ResultDTO objects. I have to have the following attributes in order for WCF RIA Services to honor it correctly. The “Include” attribute basically tells WCF RIA Services to eagerly load this collection of child objects. The “Association” attribute requires a name for the relationship, the parent value and the associated child value. Finally, we have the “Composition” attribute which indicates that this member represents an association taht is part of a compositional hierarchy.

Now let’s turn our attention to how we would write a query to take advantage of this new generic model.

[Query()]
public IQueryable<ResultWithCollectionDTO> ReturnRSReportDTOIncludingParameters()
{
    var resultSet = this.ObjectContext.rs_r_Report
        .Include("rs_rp_ReportParameter")
        .OrderBy(x => x.rs_r_FriendlyName);
    var result = from c in resultSet
                 select new ResultWithCollectionDTO()
                 {
                     Value = c.rs_r_ReportIdent,
                     Name = c.rs_r_ReportName,
                     FriendlyName = c.rs_r_FriendlyName,
                     ScreenId = c.rs_r_ScreenId ?? 0,
                     Children = (from d in c.rs_rp_ReportParameter
                                 select new ResultDTO()
                                 {
                                     ParentValue = c.rs_r_ReportIdent,
                                     Value = d.rs_rp_ReportParameterIdent,
                                     Name = d.rs_rp_ParameterName,
                                     FriendlyName = d.rs_rp_DefaultValue
                                 })
                 };
    return result;
}

As you can see, this query very similar to the one we already used in the previous post but we need to do some projection shaping so that the data fits into our newly defined models. We first need to ensure that the data from our ObjectContext eagerly loads our report parameters with our report object. Next, we just go through the steps of shaping the ResultWithCollectionDTO object and then perform a sub select statement for the report parameters. We make sure that we also bring in the report identity as the “ParentValue” property.

You would think that we are done but I need to do one more thing if my screen that I show your earlier is to work. When we create our models, WCF RIA Services will treat them as read-only unless we provide a little more implementation. This means that if I try to “TwoWay” databind to my parameters, an exception will be thrown. I want to be able to set values for the parameters but I don’t care about saving this since I am really treating these objects as non-persisted. It is only on the client-side that I care about binding and sending these values to my report engine.

In order to get this accomplished, you provide the following in your DomainService:

[Delete]
public void DeleteResultDTO(ResultDTO dto)
{
    // Do nothing....
}
[Insert]
public void InsertResultDTO(ResultDTO dto)
{
    // Do nothing....
}
[Update]
public void UpdateResultDTO(ResultDTO dto)
{
    // Do nothing....
}

[Delete]
public void DeleteResultWithCollectionDTO(ResultWithCollectionDTO dto)
{
    // Do nothing....
}
[Insert]
public void InsertResultWithCollectionDTO(ResultWithCollectionDTO dto)
{
    // Do nothing....
}
[Update]
public void UpdateResultWithCollectionDTO(ResultWithCollectionDTO dto)
{
    // Do nothing....
}

I typically put this code in a partial class so as I won’t lose it when I need to update my DomainService. Now that I have the Delete, Insert, and Update methods in place, my databinding on the client-side will work just fine.

Again, remember that you will only need to do this once or twice depending on how you want to implement your generic objects. The nice thing about this is you have full access to your complete object but you also have access to a “lite” version that you can use for your lookups. Couple this with the built-in data paging and filtering that you get for free from WCF RIA Services and you have a pretty flexible architecture.

Hope this helps…

Using WCF RIA Services to allow projections that support Lookups

August 26, 2011 Leave a comment

WCF RIA Services is quite powerful and provides a lot of flexibility. You can pretty much perform all of your persistence operations and server side processing using WCF RIA Services without really needing to create a separate service.

One aspect that seems to be a little more difficult is to provide a light version of a table for lookups. What I mean by lookups is simply the bare amount of data necessary to hydrate a ComboBox or a ListBox. Typically you don’t need to bring back your whole table or object just to provide a selector. In some cases, your tables may even have other data that is really meaningless to the user and is just for tracking purposes such as metadata that describe who made the last modification and when.

Now that we know what the problem statement is, how can we go about solving this in a manner that is generic enough to handle most any ComboBox or ListBox.

Introducing the ResultDTO object.

public class ResultDTO
{
    [Key]
    public int Value { get; set; }
    public string Name { get; set; }
    public string FriendlyName { get; set; }
};

With this simple class, I can now do projections that will allow me to reshape larger objects to this smaller object and only bring back the information that I need. Typically I find that I have several tables that are very wide and I only need just key information when displaying them as a ComboBox. I don’t want to bring down all the columns per record since that would be inefficient but I still want to be able to databind using the key from the underlying table and some label in my ComboBox or ListBox.

Let’s review the class that I have presented above. I first need to define a key so that WCF RIA Services can function properly. This Value property is used as the unique key to the underlying table that I am representing. Next I have two string properties: Name and FriendlyName. You don’t need both but I sometimes like the option to bind to one or the other depending on how the text is formatted in the database.

Okay, now let’s look at how you would expose a query via WCF RIA Services that reshapes the table into our new ResultDTO object:

public IQueryable<ResultDTO> ReturnReportDTO()
{
    var result = from c in this.ObjectContext.rs_r_Report
          select new ResultDTO()
          {
              Value = c.rs_r_ReportIdent,
              Name = c.rs_r_ReportName,
              FriendlyName = c.rs_r_FriendlyName
          };
    return result;
}

As you can see, I am simply doing a projection that is creating a new ResultDTO object from the underlying rs_r_Report object. This pattern allows us to basically project any object into our ResultDTO object and only bring back the items that we need.

By using this pattern you can now have a full and lite version of your objects so that you can support full data editing and also read-only lookups.

In the next post I will describe how we can take this a step further and create a custom object that has a nested collection and still get WCF RIA Services to allow us to work with it.
Hope this helps!

Entity Framework, LINQ, and WCF RIA Services ordering tip

I thought I would share the following scenario with the hope that you can learn from my mistakes and do better. I have a screen that loads an account record. Based on that account, a hierarchical tree is loaded with supporting information in the bottom portion of the screen. The following is a screen shot of the screen with some really bizarre ordering in the tree:

If we look at the query that was used to load the hierarchy, we see the following:

[Query()]
public IQueryable ReturnBillsAndReturnsByOrganizationId(int organizationId)
{
	return this.ObjectContext.Organization.Where((c) => c.OrganizationId == organizationId)
	    .OrderBy(x => x.SubsystemName)
	    .OrderBy(x => x.BillType)
	    .OrderByDescending(x => x.FiscalYear)
	    .OrderByDescending(x => x.FiscalPeriod);
}

What I am trying to do is sort the tree in the following order:
Subsystem Name (ASC), BillType (ASC), Fiscal Year (DESC), Fiscal Period (DESC). Perhaps on first glance the syntax looks correct but if we refer to the documentation we learn some important pieces of information:

MSDN Queryable.OrderBy

MSDN Queryable.ThenBy

If we review the documentation, we will see that anytime we call OrderBy or OrderByDescending we get an IOrderedQueryable object. This means that every time we call OrderBy or OrderByDescending we are re-creating the IOrderedQueryable object. Clearly, this is not what we want to do.

If we review the documentation for ThenBy, we see that it requires a parameter of type IOrderedQueryable as the type of object to perform the operation. It then returns a now fully sorted object type of IOrderedQueryable. This is exactly what we are looking for. This works for both the ThenBy and ThenByDescending operations.

Thus it is necessary that our query looks like this:

[Query()]
public IQueryable ReturnBillsAndReturnsByOrganizationId(int organizationId)
{
	return this.ObjectContext.Organization.Where((c) => c.OrganizationId == organizationId)
		.OrderBy(x => x.SubsystemName)
		.ThenBy(x => x.BillType)
		.ThenByDescending(x => x.FiscalYear)
		.ThenByDescending(x => x.FiscalPeriod);
}

Finally, with this change, our tree is now in the correct order and all is good with the universe:

It is very easy to assume that the logic of the syntax for the ordering is correct when trying to force it to work like T-SQL. This isn’t always the case and we must be careful or our results will be entirely not what we are looking for.

Hope this helps….

Follow

Get every new post delivered to your Inbox.

Join 211 other followers