Archive for the ‘Data Sources’ Category

ObjectDisposedException EntityFramework

September 21, 2011

I had a using clause around my call to Entity Framework like so:

using (var ctx = new SubstationLoadContext())
{
IRepository repo =
new Repository(ctx);

AreaSummations = repo.GetAreaSummationsWithEquipmentWithReadingUnits();
}

Subsequently while performing a LINQ query on AreaSummations I received the fiendish ObjectDisposedException. Porquoi ?

Some quick Googling told me that since my context variable was being disposed before I performed the query I should force the creation of an in-memory List in order to detach from the database i.e. I should do a .ToList() like so:

AreaSummations = repo.GetAreaSummationsWithEquipmentWithReadingUnits.ToList();

But my repo method was already doing a .ToList(). I was already detached from the database wasn’t I? Nyet.

The Botswanian Programming Legend masquerading as Mark Gravell on Stack Overflow informed me that the issue was that EF was lazily-loading some portion of the object graph and I needed to force an eager load by use of Entity Framework .Include. HUZZAH!

But I was already forcing an eager load wasn’t I ? See…

summations = (from s in ((SubstationLoadContext)repo.Context)
.SubstationGroupSummationTypes
.Include("Equipments.EquipmentReadingUnits")

Mais Non! I was NOT forcing an eager load because I had improperly specified the path to EquipmentReadingUnits. I had only included the path from about half-way up the object graph I needed to include the full path from the root Entity. Like so:
summations = (from s in ((SubstationLoadContext)repo.Context)
.SubstationGroupSummationTypes
.Include("SubstationGroup.Substations.Equipments.EquipmentReadingUnits")

PROPER HUZZAH! The eager load was correctly specified and ObjectDisposedException was banished!

Moral of the Story:

When getting ObjectDisposedException with Entity Framework make sure you have correctly specified the path for your eager loads. And include them if they are not already present. And then you will no longer see this

System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection

GridView Insert with ObjectDataSource: Lessons Learnt

June 14, 2011

Was playing around today with implementing a full CRUD interface to a Database table using GridView and ObjectDataSource. I had a bit of trouble getting Insert functionality to work and here’s what I learnt:

DataObjectTypeName SHOULD BE OMITTED if you are using Insert operations via your ODS.

Setting the ODS property DataObjectTypeName is meant to allow you to pass a Business or Domain object as a single parameter, rather than a list of parameters, one per member, to your ODS Inserts and Updates. While this seems to work fine for Updates, the ODS Insert will fail since its internal Dictionary of Object members is not initialised. This appears to be a bug in the ODS.

There are a couple of workarounds for this: you can create the Dictionary yourself from the ODS DataSource, or you can create a your custom Business object from the InsertParameter list. But if you don’t feel in the mood for workarounds you are stuck with omitting the DataObjectTypeName property and specifying the list of parameters to your Insert in the markup for the ODS

InsertParameters
asp:Parameter Name="CountryCode" /
asp:Parameter Name="CountryName" /
asp:Parameter Name="ISDCode" /
/InsertParameters

ODS Delete Parameter Will Be Inferred From DataKeys Member Of The GridView

Given a GridView of the following definition:
asp:GridView ID="gvTrial" runat="server" DataKeyNames="CountryCode"

NO DeleteParameter is required in the ODS markup and the DeleteMethod with the single parameter string CountryCode will be invoked from the TypeName (DataAccess) class specified in the ODS markup.

LinkButtons Defined in the GridView Control Template Will Trigger The ODS TypeName (DataAccess) Methods
You just need to give them the standard CommandNames that GridViews give to their Command Buttons. So given the following Button Definitions in the GridView Template:

asp:TemplateField
EditItemTemplate
asp:LinkButton ID="btnUpdate" runat="server" Text="Update" CommandName="Update" /
asp:LinkButton ID="btnCancel" runat="server" Text="Cancel" CommandName="Cancel" /
/EditItemTemplate
ItemTemplate
asp:LinkButton ID="btnEdit" runat="server" Text="Edit" CommandName="Edit" /
/ItemTemplate
FooterTemplate
asp:LinkButton ID="btnInsert" runat="server" Text="Insert" OnClick="lbInsert_OnClick" /
/FooterTemplate
/asp:TemplateField
asp:CommandField ShowDeleteButton="true" /

The Update and Delete methods on the TypeName (DataAccess) class will be fired automatically when the Update and Delete buttons are clicked.

Firing Insert From GridView Footer

This link shows the basic interface I used, with the Insert operation being fired from a LinkButton in the GridView Footer Row. As noted above, the Insert Parameters must be specified in the ODS Markup.

Insert From GridView Footer Where DataSource Is Empty
If your datasource is empty, the GridView will not render, so you wont get a Footer and you wont be able to Insert. So, add an empty record to the DataSource On the ODS SelectMethod, then hide the empty row during OnRowDataBound

The Final ODS

asp:ObjectDataSource runat="server" ID="odsCountry" TypeName="CountrySource" OnInserting="odsCountry_Inserting"
SelectMethod="GetCountries" UpdateMethod="UpdateCountry" InsertMethod="InsertCountry" DeleteMethod="DeleteCountry">
InsertParameters>
asp:Parameter Name="CountryCode" /
asp:Parameter Name="CountryName" /
asp:Parameter Name="ISDCode" /
/InsertParameters
/asp:ObjectDataSource

The GridView


asp:GridView ID="gvTrial" runat="server" DataKeyNames="CountryCode" EmptyDataText="No country data" DataSourceID="odsCountry"
AllowPaging="true" AllowSorting="true" AutoGenerateColumns="false" ShowFooter="true" OnRowDataBound="gvTrial_RowDataBound"

/EditItemTemplate
ItemTemplate
asp:LinkButton ID="btnEdit" runat="server" Text="Edit" CommandName="Edit" /
/ItemTemplate
FooterTemplate
asp:LinkButton ID="btnInsert" runat="server" Text="Insert" OnClick="lbInsert_OnClick" /
/FooterTemplate
/asp:TemplateField
asp:TemplateField
EditItemTemplate
asp:TextBox ID="tbCountryCode" runat="server" Text=''/asp:TextBox
/EditItemTemplate
ItemTemplate
asp:Label ID="lbCountryCode" runat="server" Text=''/asp:Label
/ItemTemplate
FooterTemplate
asp:TextBox ID="tbNewCountryCode" runat="server"></asp:TextBox
/FooterTemplate
/asp:TemplateField
asp:TemplateField
EditItemTemplate
asp:TextBox ID="tbCountryName" runat="server" Text=''/asp:TextBox
/EditItemTemplate
ItemTemplate
asp:Label ID="lbCountryName" runat="server" Text=''/asp:Label
/ItemTemplate
FooterTemplate
asp:TextBox ID="tbNewCountryName" runat="server"/asp:TextBox
/FooterTemplate
/asp:TemplateField
asp:TemplateField
EditItemTemplate
asp:TextBox ID="tbISDCode" runat="server" Text=''/asp:TextBox
/EditItemTemplate
ItemTemplate
asp:Label ID="lbISDCode" runat="server" Text=''>/asp:Label
/ItemTemplate
FooterTemplate
asp:TextBox ID="tbNewISDCode" runat="server"></asp:TextBox
/FooterTemplate
/asp:TemplateField
asp:CommandField ShowDeleteButton="true" /

/Columns
/asp:GridView

The TypeName (DataAccess) Class

Since DataObjectTypeName is omitted you MUST specify all parameters to the Insert and Update Methods. You cannot use your Business Class, which in my case would be ‘Country’. Use the MSDN example as your basic template:

public void UpdateCountry(string CountryCode, string CountryName, string ISDCode)
{
…whatever…
}

public void InsertCountry(string CountryCode, string CountryName, string ISDCode)
{
…whatever…
}

public void DeleteCountry(string CountryCode)
{
…whatever…
}

WPF Setting Default Sort Order For Datagrid Without Using A Static Resource

November 4, 2009

For this you need a CollectionViewSource and bind the DataGrid ItemsSource to the View property of the CollectionViewSource.

The Basic Wire-Up
Andrea Boschin on Silverlight PlayGround demonstrates the basic wiring for Binding the DataGrid ItemsSource to the CollectionViewSource. Andrea is using MVVM and hence the CollectionViewSource is a property in his ViewModel class. I’ve excerpted it here:

Public Class SomeViewModel

// The View of this cvs will get bound to a datagrid
Public CollectionViewSource People { get; set; }

// The actual data that the cvs will construct its view out of
protected ObservableCollection(Of People) PeopleInternal { get; set; }
}
} //end class

In XAML, bind the DataGrid to the View of the People CollectionViewSource:

wpftoolkit:DataGrid ItemsSource="{Binding People.View}"
wpftoolkit:DataGrid.Columns
wpftoolkit:DataGridTextColumn Header="Name"
MinWidth="120" Binding="{Binding Path=Name, Mode=TwoWay}" /
wpftoolkit:DataGridTextColumn Header="Age"
Width="auto" Binding="{Binding Path=Age, Mode=TwoWay}" /
/dg:DataGrid.Columns
/wpftoolkit:DataGrid

Taking over from where Andrea left off, when the ObservableCollection is set, we initialise the CollectionViewSource:


protected ObservableCollection(Of People) PeopleInternal
{ get {return _peopleInternal };
set;
{
_peopleInternal = value
People.Source = _peopleInternal
}
}

Setting Default Sort Order
In order to establish the default Sort Order for the Datagrid, add a SortDescription to the CollectionViewSource that the DataGrid is bound to. This creates the required view.

Let’s say we wanted our Datagrid to have a sort order of ‘Name’, assuming ‘Name’ is a field on the People class.


protected ObservableCollection(Of People) PeopleInternal
{ get {return _peopleInternal };
set;
{
_peopleInternal = value
People.Source = _peopleInternal
// Setting Sort Order for People
// Sort by Name field
People.SortDescriptions.Clear()
SortDescription sd = new SortDescription("Name", ListSortOrder.Ascending)
People.SortDescriptions.Add(sd)
}
}

So, every time PeopleInternal is updated, the CollectionViewSource creates a view on PeopleInternal which is sorted by Name. This is held in People.View. Because the DataGrid is bound to this View the data in it is always sorted by name when the ItemsSource (the ObservableCollection(Of People) is initialised

This works around the behaviour in the WPF Toolkit DataGrid where all SortDescriptions for the DataGrid are lost when the ItemsSource is initialised

Of course, users will still be able to click on Column Headers and sort at any time on any other column they want to.

DataBinding WinForms CheckedListBox

October 12, 2009

Noting some confusion in the Greater Google on the subject of the CheckedListBox, I want to make it clear that this article is about the WINFORMS CheckedListBox class (System.Windows.Forms.CheckedListBox), NOT the WebForms CheckBoxList class (System.Web.UI.WebControls.CheckBoxList). So back to your Postbacks all you WebForms freaks.

Generating CheckedListBox Text
The CheckedListBox is a ListBox which includes a CheckBox to the left of the display text. The display text is computed using the ToString method of the object in the Items collection of the CheckedListBox.

It is also possible, however, to generate the text in each CheckedListBox entry by associating the CheckedListBox with a DataSource and setting the DisplayMember field of the CheckedListBox.

Microsoft do not encourage the use of CheckedListBox DataSource. None of the DataSource, DisplayMember or ValueMember members of CheckedListBox appear in the Intellisense for the control or in the Designer Properties Window because Microsoft has added the Browsable=false attribute to these properties. In the MSDN example, programmers are referred directly to the Items collection as the means of wiring up the control:

To add objects to the list at run time, assign an array of object references with the AddRange method. The list then displays the default string value for each object. You can add individual items to the list with the Add method.

Microsoft MSDN advises users not to use the DataSource member of CheckedListBox because this control is not intended to be DataBound and unpredictable results can ensue if you use the control in a DataBound way. However the DataSource, ValueMember and DisplayMember members can be used as I discovered from Moz2 in this MSDN social forum thread

Why Would You Bother ?
It’s a fair question. For me, I needed to produce a Windows Form with identical look and feel to an existing form which utilised a CheckedListBox, but for processing efficiency also required a SelectedValue member. That led me into a fruitful Googling session which I am now sharing with you.

Setting CheckedListBox DataSource
You can set the DataSource to any Collection. But you must set DataSource after DisplayMember is set. Here’s some VB.NET for your reading pleasure:


'Event Handler for WebSiteType dropdown selected index changed
'Update Listed Domains in CheckBox List clbWebDomain based on selected WebSiteType
Private Sub cboWebSiteType_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboWebSiteType.SelectedIndexChanged
Me.clbWebDomain.DataSource = Nothing
Me.clbWebDomain.Items.Clear()
...Update Me.WebDomainMappingList...
Me.clbWebDomain.ValueMember = "DomainCode"
Me.clbWebDomain.DisplayMember = "URL"
Me.clbWebDomain.DataSource = Me.WebDomainMappingList
...Code to initialise clbWebDomain.Items.....
End Sub

WebDomainMappingList is a GenericList of my Custom Class WebDomainMapping.

Since that WebDomainMapping Class is only used for temporary processing, I actually implemented it as a Structure. I didn’t put in a ToString implementation as I wanted it clear via the DisplayMember that I wanted to display the URL property…and that leads me to my next point, the DisplayMember and ValueMember members must refer to Properties of the Objects/Structures in the Items collection of your CheckedListBox. They cannot be mere fields otherwise your CheckedListBox will fill up with garbage.

Therefore, the following will NOT work:

Friend Structure WebDomainMapping
Dim URL As String
Dim DomainCode As Integer
End Structure

Putting an Object/Structure like this into the Items collection of a CheckedListBox and then using the URL property as a DisplayMember will cause the following output to your CheckedListBox for all the entries. This is actually the default ToString() representation of the object in your CheckedListBox Items Collection

Namespace.frmReport+WebDomainMapping
Namespace.frmReport+WebDomainMapping
etc.

An Object/Structure definition like this will work. It wlll allow URL to be used as the DisplayMember of the CheckedListBox.

Friend Structure WebDomainMapping
Private _URL As String
Public Property URL() As String
Get
Return _URL
End Get
Set (ByVal value As String)
_URL = value
End Set
End Property

Private _DomainCode As Integer
...Property Declaration for DomainCode....
End Structure

Directly Initialise The Items Collection Too
It is not enough merely to initialise the DataSource member of CheckedListBox. You must also directly initialise the Items Collection if you don’t do this you will not get anything at all displayed in the CheckedListBox.

Naturally, the Items collection must contain EXACTLY the same Objects as the DataSource.

Remember, true DataBinding as represented by DataSource, DisplayMember and ValueMember is not officially supported for this control, so utilising these members may not provide the functionality you would expect or may not work at all. For example, here the Items Collection is not automatically populated by assigning the DataSource. What you can do, as I have done, is patch together an implementation or approximation of DataBinding for CheckedListBox. The functionality, or at least most of it, is there because it is inherited from Base Controls.

Final Caveats
The partial implementation of DataBinding in CheckedListBox and hence its flaky functionality is recognised by Microsoft as a bug (see preceeding links), but there is no guarantee this will be fixed to support full DataBinding. Microsoft may decide to chop DataSource from CheckedListBox entirely. I used the above workaround in version 2.0 of the Framework. Who knows, it might be gone or not work at all in later versions.

Why DataBinding Can Only Be To Properties and not Fields
Nikhail Koltari who is a Software Architect at Microsoft, explains some of the reasons why here.

ObjectDataSource could not find a non-generic method that has parameters: . for a SelectMethod

May 7, 2009

So I’ve got an ObjectDataSource with its SelectMethod set to ‘GetStuffById’ and the contract is GetStuffById(int stuffId) and the error in the title comes up.

I’m thinking ‘what the…of course it’s got parameters read the flamin’ contract!. I even had a beautiful FormParameter defined in the markup.

But I left something out. Yes, I left out the parameter NAME.
That’s why there’s a single blank space in the error message between ‘parameters:’ and ‘.’ where the list of parameter names goes.

Classic.

My markup said:

asp:ObjectDataSource ID=”stuffDataSource” runat=”server” DataObjectTypeName=”StuffDto”
SelectMethod=”GetStuffByStuffId” TypeName=”StuffService” >
SelectParameters
asp:FormParameter DefaultValue=”1″ /
/SelectParameters
/asp:ObjectDataSource

Notice the FormParameter does not have a Name attribute? Yeah. Well I didn’t until about five minutes ago.

It should have read:
asp:FormParameter Name=”stuffId” DefaultValue=”1″

And yes, I’m eating my liver.

ObjectDataSource UpdateParameters null

August 18, 2008

Playing around with ObjectDataSource I found that ‘Update’ didn’t work. Specifically, I had an ObjectDataSource being used as a Data Source for a GridView. The ODS.Select was returning a DataSet drawn from an Access database. When I clicked on the GridView ‘Update’ link (after clicking the ‘Edit’ Command link) the data in the DataGrid would return to its pre-edited state. The Update was not being processed, but why ?

Through A Glass Darkly
Peering into the ODS.UpdateParameters I noticed that the parameters corresponding to the original DB values were null. This meant that the Update statement could not select any record to update. To check that the Update would work if correct values were passed in, I edited the DefaultValue of the UpdateParameters to a hardcoded value in Markup. With this in place the DB Update worked. So I had to discover why the Original Values were not being initialized.

This Blog post on Asp.Net Help, Using Parameters with the Object Data Source Control pointed me in the right direction by giving me a pointer to the MSDN article, “How a Data Source Control Creates Parameters for Data-bound Fields” which explains exactly how the ODS Parameters are generated.

Gold In Them Thar Incomprehensible Articles
The precise nugget of MSDN wisdom which assisted me was this:

For an update operation, the data source control populates its UpdateParameters collection with values from the name/value pairs in the Keys, NewValues, and OldValues collections. For a delete operation, the data source control populates its DeleteParameters collection with values from the name/value pairs in the Keys and OldValues collections.

The OldValues collection is not populated by default. It is populated only when the data-source control’s ConflictDetection property is set to CompareAllValues.

Too Easy (or ‘How to be a Genius With Retrospective Understanding’)
I had configured my Dataset to use Optimistic Concurrency, that’s why the ODS Update method was trying to generate parameters for the Original DB Values (original_ID and so on) BUT the KeysCollection from which these parameter values are read, ‘OldValues’, is not generated unless Markup has ConflictDetection=CompareAllValues.


asp:ObjectDataSource ID="teamDataSource" runat="server" DeleteMethod="Delete"
InsertMethod="Insert" OldValuesParameterFormatString="original_{0}"
SelectMethod="GetData" ConflictDetection="CompareAllValues"
TypeName="Screamer.Common.DataSets.dsTeamTableAdapters.TeamTableAdapter"
UpdateMethod="Update"

Now my Update goes sweeter than a hob-nailed boot through a Cane Toad’s spinal cord. Try one today.

ObjectDataSource Resources

Scott Mitchell of 4GuysFromRolla has a great set of tutorials on how to use ObjectDataSource in a multi-tier application. Tutorials 1, 2 and 4 through 6 give detailed instructions on how to create the basic tiers (Data Access, Business Logic, Presentation) using the ODS.