Monday, April 11, 2011

Processing Anonymous Types from Projections

Processing Anonymous Types from Projections
Andrew Conrad, developer lead for Microsoft ’ s .NET Data Services (Project Astoria), has sample code
for a custom CopyToDataTable () extension method that accepts generic types other than DataRow s
( http://blogs.msdn.com/aconrad/archive/2007/09/07/science - project.aspx ). His
implementation doesn ’ t accept Nullable < T > types, but a reader provided a fix to support them.
However, it ’ s usually simpler to write your own code to process anonymous types than to use a patched
copy of a custom extension method.
The following code, which also is part of the click event handler for the Apply Filter button, runs when
you select the DataTable (Projection) option, clones dtProj from the OrdersDataTable type, populates
it from a projection that omits five properties of the Orders object, and adds it to the DataSet ’ s Tables
collection.
C# 3.0
private void btnApplyFilter_Click(object sender, System.EventArgs e)
{
// ...
NorthwindTyped.OrdersDataTable dtProj =
(NorthwindTyped.OrdersDataTable)(dsNwindT.Orders.Clone());
// ...
// DataTable populated from projection
var OrdersProjection = from o in dsNwindT.Orders
where o.GetOrder_DetailsRows().Sum(d = > d.Quantity *
d.UnitPrice * (Decimal)(1 - d.Discount)) >
Convert.ToDecimal(txtValue.Text)
select new
{
o.OrderID,
o.CustomerID,
o.EmployeeID,
o.OrderDate,
o.RequiredDate,
o.ShippedDate,
o.ShipVia,
o.Freight,
o.ShipCountry
};
foreach (var o in OrdersProjection)
{
DataRow projRow = dtProj.NewRow();
projRow[0] = o.OrderID;
projRow[1] = o.CustomerID;
projRow[2] = o.EmployeeID;
projRow[3] = o.OrderDate;
projRow[4] = o.RequiredDate;
projRow[5] = o.ShippedDate;
projRow[6] = o.ShipVia;
projRow[7] = o.Freight;
To preserve the rows presence in query
projRow[13] = o.ShipCountry
dtProj.Rows.Add(projRow);
}
dtProj.TableName = OrdersProj;
dsNwindT.Tables.Add(dtProj);
bsOrders.DataSource = dsNwindT;
bsOrders.DataMember = OrdersProj;
bsOrders.ResetBindings(false);
}
Saving changes is trickier because dtProj doesn ’ t have the original data values to support optimistic
concurrency management. The newUpdateSql UPDATE command doesn ’ t include original values in its
WHERE clause, so it succeeds without raising a DBConcurrencyException error. The origUpdateSql
string restores the UPDATE command to its previous state with original values.
C# 3.0
private void btnSaveChanges_Click(object sender, System.EventArgs e)
{
if (dsNwindT.HasChanges())
{
if (dtProj != null & & dtProj.GetChanges() != null)
{
try
{
// Update only the row with the manual changes
taOrders.Adapter.UpdateCommand.CommandText = newUpdateSql;
NorthwindTyped.OrdersDataTable projChanges =
(NorthwindTyped.OrdersDataTable)dtProj
.GetChanges(DataRowState.Modified);
if (projChanges.Count() > 0)
{
taOrders.Update(projChanges);
projChanges.Clear();
}
}
catch (DBConcurrencyException ex)
{
MessageBox.Show(Concurrency exception occurred on update.);
ex.Row.ClearErrors();
}
finally
{
taOrders.Adapter.UpdateCommand.CommandText = origUpdateSql;
}
}
taOrders.Update(dsNwindT.Orders);
dsNwindT.AcceptChanges();
}
}
The primary issue with this approach is substituting “ last writer wins ” for optimistic, value - based
concurrency with changes to the filtered dataset.

No comments:

Post a Comment