Reputation: 13506
Currently I have a C# Silverlight Application That uses the domainservice class and the ADO.Net Entity Framework to communicate with my database. I want to load a child window upon clicking a button with some data that I retrieve from a server-side query to the database.
The first part of this process involves two load operations to load separate data from 2 tables. The next part of the process involves combining those lists of data to display in a listbox.
The problem with this is that the first two asynchronous load operations haven't returned the data by the time the section of code to combine these lists of data is reached, thus result in a null value exception.....
public void LoadAudits(Guid jobID)
{
var context = new InmZenDomainContext();
var imageLoadOperation = context.Load(context.GetImageByIDQuery(jobID));
imageLoadOperation.Completed += (sender3, e3) =>
{
imageList = ((LoadOperation<InmZen.Web.Image>)sender3).Entities.ToList();
};
var auditLoadOperation = context.Load(context.GetAuditByJobIDQuery(jobID));
auditLoadOperation.Completed += (sender2, e2) =>
{
auditList = ((LoadOperation<Audit>)sender2).Entities.ToList();
};
}
IEnumerable<JobImageAudit> jobImageAuditList
= from a in auditList
join ai in imageList
on a.ImageID equals ai.ImageID
select new JobImageAudit
{
JobID = a.JobID,
ImageID = a.ImageID.Value,
CreatedBy = a.CreatedBy,
CreatedDate = a.CreatedDate,
Comment = a.Comment,
LowResUrl = ai.LowResUrl,
};
auditTrailList.ItemsSource = jobImageAuditList;
private void LoadAuditsButton_Click(object sender, RoutedEventArgs e)
{
IEnumerable<JobImageAudit> jobImageAuditList
= from a in auditList
join ai in imageList
on a.ImageID equals ai.ImageID
select new JobImageAudit
{
JobID = a.JobID,
ImageID = a.ImageID.Value,
CreatedBy = a.CreatedBy,
CreatedDate = a.CreatedDate,
Comment = a.Comment,
LowResUrl = ai.LowResUrl,
};
auditTrailList.ItemsSource = jobImageAuditList;
}
Delay the child window displaying somehow? Potentially use DomainDataSource and the Activity Load control?!
Any thoughts, help, solutions, samples comments etc. greatly appreciated.
Upvotes: 1
Views: 1266
Reputation: 106906
First of there is no point in delaying the display of a window. Instead you should design your code to be able to handle asynchronous updates to the data. In this case you have a somewhat interesting situation where you are performing two asynchronous load operations and you are only able to create the data for display when both operations have completed.
One solution to this problem is to move the query where you combine the data to the server side. Then instead of retrieving Image and Audit objects from the server in two separate operations you can retrieve JobImageAudit objects.
Another solution is to create something similar to a view-model for the data you retrieve. Here is a rough sketch to get you started:
public class JobImageAuditViewModel : INotifyPropertyChanged {
IEnumerable<Image> images;
IEnumerable<Audit> audits;
IEnumerable<JobImageAudit> jobImageAudits;
public void GetData() {
this.images = null;
this.audits = null;
this.jobImageAudits = null;
OnPropertyChanged("JobImageAuditList");
// Load images by using GetImageByIDQuery()
// Load audits by using GetAuditByJobIDQuery()
}
void LoadImageCompleted(Object sender, EventArgs e) {
// Store result of query.
this.images = ...
UpdateJobImageAuditList();
}
void LoadAuditCompleted(Object sender, EventArgs e) {
// Store result of query.
this.audits = ...
UpdateJobImageAudits();
}
void UpdateJobImageAudits() {
if (this.images != null && this.jobs != null) {
// Combine images and audits.
this.jobImageAudits = ...
OnPropertyChanged("JobImageAudits");
}
}
public IEnumerable<JobImageAudit> JobImageAudits {
get {
return this.jobImageAudits;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(String propertyName) {
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
You then have to databind auditTrailList.ItemsSource
to JobImageAuditViewModel.JobImageAudits
. You can do this by setting the DataContext
of the ChildWindow
or UserControl
that contains auditTrailList
to an instance of JobImageAuditViewModel
and add this attribute to the auditTrailList
XAML:
ItemsSource="{Binding JobImageAudits}"
Actually the .NET RIA framework is designed to let the client-side generated entitiy classes assume the role of the view-model in an MVVM application. They can be extended on the client side and they support INotifyPropertyChanged
. However, in your case you are using an entity on the client side that doesn't exist on the server side. Combining my first suggestion with data-binding is probably the ultimate solution to your problem.
Upvotes: 1