Wednesday, August 3, 2011

Introducing MVVM and customizing the UI by role

First we will add a View-Model layer behind the home page, and customize the UI based on the role of the logged in user.

Authentication data is provided by several ASP.NET tables.

The Web.config file in the project contains various configuration parameters, two if which are:
  • roleManager
  • authentication
Added details for membership, roles, and authentication to Web.config file. Had to change all names which referred to SLEventManager, to Eveneter. Not sure if this was really needed.

Adding a property for the dbName and connection string to Web.config

Done adding everything. Now when I run the application, I get an error message:

--------------------------------------------------------------
Error 1 The "CreateRiaClientFilesTask" task failed unexpectedly.
System.Web.HttpException (0x80004005): Sections must only appear once per config file. See the help topic for exceptions. (C:\Documents and Settings\pshah\my documents\visual studio 2010\Projects\Eventer\Eventer.Web\web.config line 53) ---> System.Configuration.ConfigurationErrorsException: Sections must only appear once per config file. See the help topic for exceptions. (C:\Documents and Settings\pshah\my documents\visual studio 2010\Projects\Eventer\Eventer.Web\web.config line 53)
at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags, PolicyLevel policyLevel, Exception appDomainCreationException)
at System.Web.Compilation.ClientBuildManager.EnsureHostCreated()
at System.Web.Compilation.ClientBuildManager.CreateObject(Type type, Boolean failIfExists)
at Microsoft.ServiceModel.DomainServices.Tools.CreateRiaClientFilesTask.CreateSharedTypeService(ClientBuildManager clientBuildManager, IEnumerable`1 serverAssemblies, ILogger logger)
at Microsoft.ServiceModel.DomainServices.Tools.CreateRiaClientFilesTask.GenerateClientProxies()
at Microsoft.ServiceModel.DomainServices.Tools.CreateRiaClientFilesTask.ExecuteInternal()
at Microsoft.ServiceModel.DomainServices.Tools.RiaClientFilesTask.Execute()
at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask, Boolean& taskResult) Eventer

--------------------------------------------------------------

This does not make any sense. The said configuration element is not even present in my file, and there is definitely nothing like this at line 53.

Going to try doing a Google search. Looks like this problem was happening because the 'profile' element was specified multiple times. Nothing in the error message said that. Bad ... very bad.

Fixed that error and running the test. I am able to load the login form, but cannot login with credentials specified in the tutorial. Since I do not get a connection error, I am assuming that there is no problem with the database connection. Perhaps the data does not exist? I opened the data table from the database configuration view, but that shows me the schema, and not the data.

Since I cannot login with the provided credentials, I will register a new user. I was able to do that successfully, and could also login with that user. I will use this account for the time being.

Create the Register For Event Functionality:
I added the StackPanel (containing the Register for Event functionality) after the current StackPanel as suggested in the tutorial. But I get some compilation errors. Instead of using that code, I added a StackPanel, within which I added GridPanel, and within that I added two buttons - one for registration and one for deregistration.

The tutorial says, that now we need to add a new class for the View Model, called ViewModelBase. However, it does not say which folder this class should be put in. Also the suggested shortcut SHIFT-ALT-C, does not add a new class in VisualStudio2010, with Silverlight 4.0.

We need to create the base class ViewModelBase, because when an attribute changes in the ViewModel, the UI will have to be updated, This is usually done by implementing INotifyPropertyChanged. We provide a default implementation for this interface in the ViewModel base class.

I created ViewModelBase and also put the code specified in the tutorial in that class. However, I totally o not understand the code. A lot of questions come to mind, perhaps some of them will be answered as I move ahead in the tutorial.

Next I created the ViewModel folder which will contain specific ViewModel classes. Specific ViewModel classes are created in correspondence to specific view XAML files. I am not sure why we did not create ViewModelBase in the ViewModel folder.

Created a class called HomeViewModel.cs in ViewModels folder,a nd made it extend ViewModelBase.cs

THERE ARE WAY TOO MANY THINGS THAT DO NOT WORK AS EXPECTED IN THIS COURSE, WHICH IS A FIT INFURIATING FOR A BEGINNER. I AM GOING TO CREATE ANOTHER COURSE WHICH WILL HOPEFULLY HAVE MORE THINGS WORKING AS THEY SHOULD.





Introducing the View Model

So far we have used data forms provided by WCF RIA services to talk to the data. This is fine for small applications or for certain data objects, but once the application grows, the state we want to show the user may not be in 1 - 1 correspondence with the database tables.

We also do not want to show all the data views to all users, we would like to control the access of what a user does in the system.

Putting all our data access code in the XAML code behind, is not good from the perspective of testability.

For all the above reasons, we usually add a layer between the View (XAML and it's code behind), and the model (domain service classes), to perform all the the above tasks. This layer is the View-Model layer.

Monday, July 25, 2011

Creating a track and session hierarchy

In this exercise, we want the user to be able to view/edit tracks for the events. Since each event can have multiple tracks, this exercise will show us how to deal with master-detail relationships.

First, the tutorial says that our default applications, DomainService only returns a list of Events and not the Entities which are related to the Event. We could get it to do a lazy fetch, but they suggest that we replicate the GetEvents() method and refactor it so that it returns the Tracks and Talks also.

I created a new method in EventManagerDomainServices.cs, which will return the Tracks and Talks for the events. This method is for the EntityFramework. BY adding this method we are enhancing the Entity Framework's functionality.

public IQueryable GetEventsWithTracksAndTalks()
this.ObjectContext.Events.Include("EventTracks.Talks");
}

We will make our Event DataGrid bind to this Query.

&<riaControls:DomainDataSource
    AutoLoad="True"
 &;nbsp;  d:DesignData="{d:DesignInstance my:Event, CreateList=true}"
    Height="0"
    LoadedData="eventDomainDataSource_LoadedData"
    name="eventDomainDataSource"
    QueryName="GetEventsWithTracksAndTalks"
    Width="0" Margin="0,0,320,480">
  <riaControls:DomainDataSource.DomainContext>
    <my1:EventManagerDomainContext />
  </riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>

However, this is not enough. We have enhanced the Entity Framework, but we also need to tell RIA services to include this data. By default RIA services is very restrictive, and will not include the data.

[Include]
EntityCollection EventTracks { get; set; }


Adding the include annotation to the EventTracks property will ensure that the data comes from RIA Services.

Now we need to select Event.EventTracks from data sources, and drop it on the view next to the Event details grid. In Silverlight when we select a dependant property, it will automatically be bound to the selected master record.

What this means is that we will only see related Event Tracks, depending on the Event which is selected.

Creating New Events in the Application

In this exercise, we will add the functionality to create new events in the application. I think we should be able to reuse a good amount of code we wrote to edit events.

First, we will add another button to the main page (Home.xaml) for creating new events.

I opened Home.xaml, selected a button from the Toolbox, and dropped it into the view for Home.xaml. Next, I repositioned the button, and changed a few properties, such as the text, name, etc.

Next, I created the click handler, in which I added code to create an event with some placeholders, and then navigate to the edit page to edit it with actual data. I do not like this way of doing things. What if the user does not put in actual data immediately. Then we will have an application with a bunch of phantom events. Why can we not straight away ask the user for details, and then save the event only when the use creates the details.

Anyways, got the application working till now. I am able to create new events. However, from a usability perspective, when we save an event, at the moment we do not get any notification. At least we should take the user back to the HomePage so they can view the updated list. Implemented this feature by invoking NavigationService.Navigate in the click handler of the 'Save' button.

The lab page for the assignment explains why we create a new domain context object instead of re-using the one we already have (similar to the the domainDataSource object we used in editEvent... though I do not know what scope this object is available in, and how/(if at all) it is shared among the views).


Silverlight Assembly related problems keep on recurring

I am again getting the assembly related errors. Specifically it seems that it cannot generate the client side code for the Silverlight application. The error I am getting is:

CreateRiaClientFilesTask task failed unexpectedly. The reason is that it could not load some file from the assembly. Or at least that is what I think it is, because I get a FileNotFoundException somewhere in the call stack. I am not quite sure what an assembly is. Is it a file, or a dll, or a bunch of files? A bit of reading also seems to suggest that the real problem could be something else, which is not being reported correctly.

Anyways, I think the first thing I should do is turn on FusionLogging (here's how), which logs assembly related errors.

I opened the VisualStudio SDK CMD prompt, and types fslogvw.exe, which opened up the 'Assembly Binding log viewer'. However, the log viewer does not show me any error messages, whereas VS2010 does have the same errors. Someone on StackOverflow mentioned that it might help if we setup a custom path for logging. Great... I s now have a custom path for logging. Setting a custom log location got it to work. Gosh this is really crazy. I had no idea Microsoft products were so buggy.

Now, very interestingly, once I had the logging working, and I rebuild the application (for the nth time today), it just started working. I seriously do not know what to make out of all this. I am trying hard not to curse :-)

I guess I will leave this issue as it is for now, and revisit it when it crops up again.

Wednesday, July 20, 2011

Editing Entities

I am now watching the 2nd video in module 2, which describes how to edit Entities. Completed watching the video, but I will not post the timeline or notes right now. I want to do the lab, and then re-watch the video (will post the timeline then).

Moving on to the lab:
First we need to create an EditEvent page. Created the EditEvent.xaml page using the Silverlight Page template. This template also created EditEvent.xaml.cs, a C# file (which I suppose is the client code referred to in the video).

Next, I opened the Data Source window, selected Event (selected 'Details' from the drop down in front of 'Event'), and dragged the control onto the EditEvent page.

Next, I created an 'Edit' button, which we will click to edit the selected Event. The Button got created in the center of the screen under the datagrid control. I am unable to move it by dragging and dropping... I know there has to be a way to do this. Plan 2 is to try to re-position it by specifying location parameters for the button in the XAML file. I think I am unable to move the button because it is in a StackPanel. If it were in a Grid, then I would have been able to move it. However, the DataGrid control (IIRC) needs to be in a Panel container, so we may have to add a Grid to the Stack Panel and then add the Button inside the Grid.

I then added a click handler to the button, so we can Navigate to the EditEvent page. This page is given a query string which has EventID=id, where id is the id of the selected Event.

The application runs, but I realized that whichever button I click, the EditEvent page shows the same event for editing. This is because the EditEvent page still does not do anything with the EventID we passed it. We need to implement the method 'OnNavigatedTo'. In this method we will get the EventID from the url and then create a filter which will determine which record to show.

// Executes when the user navigates to this page.


protected override void OnNavigatedTo(NavigationEventArgs e)
{
string eventId = NavigationContext.QueryString["EventID"];
eventDomainDataSource.FilterDescriptors.Add(
new FilterDescriptor
{
PropertyPath = "EventID",
Operator = FilterOperator.IsEqualTo,
Value = eventId
});
}



I got the edit screens, and then added a Save button. The Save button's event handler calls submitChanges on the DomainDataService



eventDomainDataSource.SubmitChanges();



This is very interesting. I read that the client part of the domain data service, keeps track of which properties on all the Entities have changed. When we call the above method, it will save all changed Entities. So RIA services manages all the batching. I guess this batch might be run in a transaction, or perhaps we can configure it to run either way...


Got the basic infrastructure in place. I am able to select an Event, click on the Edit button, and the Edit Event page shows up. However, when I edit the Event details, and click on 'Save' the Event does not get saved.

I put a breakpoint in the Event handler code to find out what is happening, and it seems that an Exception is being thrown. (sidenote: Since the compiler did not force me to catch the exception, C# either does not have checked exceptions, or this particular Exception is a runtime Exception). This happened because I had not checked the 'enable editing' checkbox while creating the ADO Data Model. This problem can be resolved by creating placeholder methods in the domain service class for basic CRUD operations.

However, since I wanted to practice doing the project again, I deleted the project and recreated it, this time being careful to select the 'enable editing' checkbox.

Everything is working fine, however I keep running into the 'code generation' errors. Not quite sure why...


Tuesday, July 19, 2011

Add data bindings and domain context

Opened the 'data sources' view, and selected 'Event', and dragged it into the main view area of Home.xaml .

I opened the data sources view, selected 'Event' and dragged it into the main area of Home.xaml . Then I resized the control to make it larger, and tried to test it by right clicking the project from the solution view, and selecting 'view in web browser'.

This did bring up the browser, and started initializing the Silverlight plugin, but stalled at 100%. Then I tried doing a CTRL-F5 on the project, and that worked just fine.

I am not sure why the former option did not work. Asked on the course forum.

Now I am able to view event data in the browser from the application.