Friday, 6 November 2009

EF: Extension method for fetching entities by their ID

Here's a handy extension method which lets you get an entity from an object context given the entity's ID:

public static T GetEntityById(this ObjectContext context, int id)
{
EntityType type = (from meta in context.MetadataWorkspace.GetItems(DataSpace.CSpace)
where meta.BuiltInTypeKind == BuiltInTypeKind.EntityType
select meta)
.OfType()
.Where(e => e.Name == typeof(T).Name).Single();

EntityKey key = new EntityKey(context.GetType().Name + "." + typeof(T).Name, "Id", id);

return (T)context.GetObjectByKey(key);
}

Tuesday, 5 May 2009

Reflection - Getting WinForm controls that implement a certain interface

I'm writing some code today for a compact framework application that will be used to help me do two-way databinding. On each form I want to dynamically invoke the databinding methods on each usercontrol that is involved with databinding. To do this I have defined an interface called IDataBoundControl which I want to use to identify controls that are involved with databinding. I use reflection to find these controls then add them to a collection of objects. Then I can just iterate around that collection and invoke the databinding methods on each control, thus calling their databinding methods.

public ArrayList GetDataBoundControls()
{
// Get the type of the current Win Form
Type t = this.GetType();
// This is the interface we want to use to find the controls
Type interfaceType = typeof(IDataBoundControl);
ArrayList databoundControls = new ArrayList();

// Get all the private fields
FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);

// Loop through each field
foreach (FieldInfo fi in fields)
{
  // Check that it's a UserControl so we don't waste time with it in case it isn't
  if (fi.FieldType.IsSubclassOf(typeof(UserControl)))
  {
    // Loop through the interfaces that the type implements
    foreach (Type it in fi.FieldType.GetInterfaces())
    {
      // If it implements the IDataBoundControl interface
      if (string.Compare(it.Name, interfaceType.Name,
        StringComparison.OrdinalIgnoreCase) == 0)
      {
        // Then get the object itself
        object control = fi.GetValue(this);

        // And add it to the ArrayList of databound controls for this form
        dataBoundControls.Add(control);
      }
    }
  }
}
}

Thursday, 22 January 2009

Fixed header in GridViews

Firstly wrap your gridview in a div

<DIV style="OVERFLOW: auto; HEIGHT: 120px"><asp:GridView ... /></DIV>

Then in the GridView add a HeaderStyle:

<asp:GridView ID="MyGridView" runat="server">
<HeaderStyle CssClass="gridheader" />
</asp:GridView>

Then add the css class

.gridheader
{
background-color: white;
position:relative;
top:expression(this.offsetParent.scrollTop);
}

All done!

Tuesday, 20 January 2009

Bulk SQL Inserting/Updating using XML

I've been writing a data migration tool in C# which has to populate various tables the largest of which is 400,000 rows. I've been using LINQ to SQL so at first I tried creating entities one at a time and inserting them. This works ok but once you get to over a 1000 you notice things slowing down, and they continue slowing down until the whole thing just takes forever to do!!!

So the solution I discovered was to use XML. Rather than doing the inserts one at a time I add them to a dataset then when the dataset reaches a pre-determined size (I chose 2000 rows) I convert the dataset to XML and pass it to a stored procedure. SQL can do these 2000 inserts in less than a second so what once took 20-30 mins now takes less than a minute, not bad!!

The SQL syntax for processing the XML and inserting is:

CREATE PROCEDURE [dbo].[BulkInsertUsers]
@xmldoc nvarchar(max)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

declare @xml_hndl int

--prepare the XML Document by executing a system stored procedure
exec sp_xml_preparedocument @xml_hndl OUTPUT, @xmldoc

INSERT INTO [User]
(
u_id,
u_firstname,
u_lastname
)
SELECT
UserId, FirstName, LastName
FROM
OPENXML(@xml_hndl, '/NewDataSet/Users', 1)
WITH
(
UserId int 'UserId',
FirstName varchar(100) 'FirstName ',
LastName varchar(100) 'LastName '
)

END

Friday, 16 January 2009

Linq to Sql - Grouping by multiple columns

Here's an example of how to group by multiple fields in Linq to sql. The sql:

SELECT City, JobTitle, Count(1)
FROM Customer
GROUP BY City, JobTitle

Is represented by the following Linq code:

from c in DataContext.Customers
group c.JobTitle by
new
{
c.City,
c.JobTitle,
}
into g
select new
{
g.Key.City,
g.Key.JobTitle,
Total = g.Count()
}

Note that this will return an anonymous type with 3 properties City,JobTitle and Total. You could just write "select g" instead of "select new..." and then access the properties from g rather than creating this anonymous type. The example I've used can be set to a datasource and then the DataField properties can easily be set.

Left outer join in Linq to Sql

Here's how to do a left outer join in sql. So in sql we would have:

SELECT c.Name, o.Price
FROM Customer c LEFT OUTER JOIN Order o ON c.Id = o.CustomerId

This becomes in Linq:

from c in DataContext.Customers
join o in DataContext.Orders on c.Id equals o.CustomerId into customerOrders
from co in customerOrders.DefaultIfEmpty()
select new
{
c.Name,
co.Price
}

The trick is in the DefaultIfEmpty which will join a null Order object onto the Customer as would happen with a left outer join.

Friday, 9 January 2009

Web methods - calling code-behind methods from the client

Web methods are really simple to use. First up you need to make sure in your page's ScriptManager you have the EnablePageMethods property set to true.

Next write your method in the code-behind for your page, making sure to put the WebMethod attribute on it:

[WebMethod]
public string GetMyData(string args)
{
// Do something
return "blah blah";
}

Now in your .aspx page add a function which will call this method:

<script language="javascript">

function getDataFromServer()
{
var args = 'blah'; // the arguments for the server-side method
// you could use JSON here to pass several args

PageMethods.GetMyData(args, OnSucceeded, OnFailed);
}

function OnSucceeded(result)
{
// Here I'm just writing out the result to a textbox
$('#txtOutput').val(result);
}

function OnFailed(error, userContext, methodName)
{
alert("Failed");
}
</script>


And then all you need to do is stick a call to your javascript method somewhere, eg. on a button click:

<input type="submit" text="Do something" onclick="getDataFromServer();">