ObjectCopier to Copy Common Properties between objects

by Kevin Bosch 9. August 2011 20:11

A useful method to copy properties from one object to another object which has common properties. This is useful is you have a design which uses DTO type objects. Rather than going and writing lines of copy type code you can make a single call to

ObjectCopier.CopyCommonProperties(object1, object2);

public static void CopyCommonProperties(object source, object destination) { var myObjectFields = destination.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var pi in myObjectFields) { if (pi.CanWrite) { var sourcepi = source.GetType().GetProperty(pi.Name); if (sourcepi != null && sourcepi.CanRead) if (pi.PropertyType.FullName == sourcepi.PropertyType.FullName) pi.SetValue(destination, sourcepi.GetValue(source, null), null); } } }
Example Unit Test to show usage More...

Tags: , ,

DataTable

A windows command line application to resize images in a directory

by Kevin Bosch 24. August 2010 20:16

I came across a need to resize a whole bunch of images. So I decided to write a little console application in .NET to do the work for me. With 100’s of images to resize it was worth the effort in creating a little application to do the job for me. CA.Console.ResizeImage.exe is what I came up with the source code is listed in this post

CA.Console.ResizeImage.exe is a command line windows utility that resizes the following supported images "jpeg,gif,bmp,png" in a given directory to a maximum size keeping the aspect ratio. It is possible to configure the source, destination and maximum size of the images via config file or command line switches it also has some default options for click and go solution. You can download the ziped binary and config file CA.Console.ResizeImage.exe.zip (4.69 kb).

In the simplest case, you can just copy CA.Console.ResizeImage.exe into the directory of your images and double click on it. It will then generate new images with the same name into a folder called ResizedImages without any configuration it will resize all images to 500 x 500 adjust height or width accordingly to keep aspect ratio. More...

Tags: ,

.NET Development | DataTable

T4 Text Template Transformation engine

by Kevin Bosch 13. August 2010 20:07

I have just found out about the existence of T4 generator today. Is a very simple yet powerful Template based text generation engine. It uses very familiar asp.net syntax <# #> Its simplicity is its strength. There is a good site on the T4 Text Template Transformation Toolkit by Oleg Sych

T4 Text Transformation Engine

If you have not tried it out you should give it a go.

Looking at generation I am wondering why they have stopped at the generation part. MVC pattern is screaming out to be implemented as part of the generation. I know the MVC model is designed to be implement as part of user interface design. However I think there is a good fit here. The Template generation is clearly the rendering of the view. The model will be the domain specific model such as the data schema when implementing the crud model on top of a relational schema. The controller is responsible for populating the model based on the instance required by the view. As such the part which is screaming out to be specialized is the controller.

Consider the power of the following example which from what i can tell is not possible

        <@# template language="C#" Inherits="GeneratedTextTransformation<MyHelloWorld>#> 
        <# Write(Model.HelloWorld); #>

There is still a bit of learning to do, T4 is looking promising

Tags:

DataTable

How to use the WriteXmlSchema and ReadXmlSchema methods of the .Net DataTable

by Kevin Bosch 7. August 2010 20:13

The WriteXmlSchema and ReadXmlSchema methods provide an easy way to serialize a DataTable schema to transportable format. This is convenient if you want to pass around a schema definition for a data table between applications in your code.

The snippets of code below have been extracted from a unit test the full source of which is here these snippets provide a working example of how the WriteXmlSchema and ReadXmlSchema work.

First we need to define a Data Table. We will define one in C# but this could easily just come from a database query. Our table will look like

TestTable

ColNameTypeConstraint
testIntIntPrimaryKey not null
testStringstring
testStringWithConstraintStringMaxLength = 100
testNonNullStringStringNot Null
testDateTimeDateTime

The C# Code to setup this data structure is as follows:

DataTable dt1 = new DataTable("TestTable");
DataColumn testIntPk = dt1.Columns.Add("testInt", typeof(int));
dt1.PrimaryKey = (new DataColumn[] { testIntPk });
dt1.Columns.Add("testString", typeof (string));
DataColumn testStringWithConstraint = dt1.Columns.Add("testStringWithConstraint", typeof(string));
testStringWithConstraint.MaxLength = 100;
DataColumn testNonNullString = dt1.Columns.Add("testNonNullString", typeof(string));
testNonNullString.AllowDBNull = false;
dt1.Columns.Add("testDateTime", typeof (DateTime));
dt1.AcceptChanges();

Once we have the Table structure setup we can invoke WriteXmlSchema method. The WriteXmlSchema has several overloads one which includes WriteXmlSchema(String) however I have elected to demonstrate using WriteXmlSchema(TextWriter).

StringBuilder sb = new StringBuilder();
StringWriter writer = new StringWriter(sb);
dt1.WriteXmlSchema(writer);
string schemaAsString = sb.ToString();
Trace.WriteLine(schemaAsString);
writer.Close();

After this code has run the result of calling then WriteXmlSchema the string schemaAsString has the following value:

<?xml version="1.0" encoding="utf-16"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="TestTable" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="TestTable">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="testInt" type="xs:int" />
              <xs:element name="testString" type="xs:string" minOccurs="0" />
              <xs:element name="testStringWithConstraint" minOccurs="0">
                <xs:simpleType>
                  <xs:restriction base="xs:string">
                    <xs:maxLength value="100" />
                  </xs:restriction>
                </xs:simpleType>
              </xs:element>
              <xs:element name="testNonNullString" type="xs:string" />
              <xs:element name="testDateTime" type="xs:dateTime" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
    <xs:unique name="Constraint1" msdata:PrimaryKey="true">
      <xs:selector xpath=".//TestTable" />
      <xs:field xpath="testInt" />
    </xs:unique>
  </xs:element>
</xs:schema>

You can see all the elements specified for the datatable have been included in the XSD (ie xml) for the dataTable.

To reverse the process we can read the XML schema from the string into a new instance of a data Table as follows:

DataTable dt2 = new DataTable();
StringReader reader = new StringReader(schemaAsString);
dt2.ReadXmlSchema(reader);

At this point the DataTable which is read in from the string will be exactly the same as the version which was serialized. To check on this we have the following Unit Test Assertions which ensure the deserialized version is identical in all aspects.

// now Assert dt = dt2
// check the Name of table
Assert.AreEqual(dt1.TableName, dt2.TableName);
// check number of colunms is the same 
Assert.AreEqual(dt1.Columns.Count, dt2.Columns.Count);
// names are all the colunm names are the same
foreach (DataColumn dc1 in dt1.Columns)
{
    Assert.IsNotNull(dt2.Columns[dc1.ColumnName]);
}
// check the primary key
Assert.AreEqual(dt1.PrimaryKey[0].ColumnName, dt2.PrimaryKey[0].ColumnName);
// check the max length constraint
Assert.AreEqual(dt1.Columns["testStringWithConstraint"].MaxLength,
                dt2.Columns["testStringWithConstraint"].MaxLength);
// check the null constraint
Assert.AreEqual(dt1.Columns["testNonNullString"].AllowDBNull,
               dt2.Columns["testNonNullString"].AllowDBNull);

Tags: ,

DataTable

Select Distinct Values from a .NET DataTable

by Kevin Bosch 5. August 2010 21:19

There are numerous times when it would be handy to have a DISTINCT function to select distinct values out of a datatable used earlier in code. This saves additional round trips to the SQL server. I have used this code in the passed :

private static bool ColumnEqual(object a, object b)
{
    if (a == DBNull.Value && b == DBNull.Value) //  both are DBNull.Value
        return true;
    if (a == DBNull.Value || b == DBNull.Value) //  only one is DBNull.Value
        return false;
    return (a.Equals(b));  // value type standard comparison
}

public static DataTable SelectDistinct(DataTable sourceTable, string columnName)
{
    // Create a new DataTable
    DataTable resultdt = new DataTable();
    // Get the Colunm to use
    DataColumn dc = sourceTable.Columns[columnName];
    if (dc != null)
    {
        // Add a col to the result table
        resultdt.Columns.Add(columnName, dc.DataType);

        // set an object to hold the previous value, and set it value to null fo the first element
        object lastValueAdded = null;
        // get the a sorted list of the results
        DataRow[] sourceDataRows = sourceTable.Select(string.Empty, columnName);
        foreach (DataRow dr in sourceDataRows)
        {
            // if it is the first element or a diffrent value add to the result table
            if (lastValueAdded == null || !(ColumnEqual(lastValueAdded, dr[dc])))
            {
                // set the value for the LastValueAdded
                lastValueAdded = dr[dc];
                resultdt.Rows.Add(new [] { lastValueAdded });
            }
        }
    }
    else
    {
        throw new DataException(string.Format("Column Name {0} Does not exist in SourceTable",
                                               columnName));
    }

    return resultdt;
}

This code is a well know derivative from one at Microsoft. http://support.microsoft.com/default.aspx?scid=kb;en-us;326176#1. The usage is simple as is outlined below:

DataTable Resultdt = DataHelper.SelectDistinct(Sourcedt, "TestCol");
More Examples can be found at Examples using DataHelper.SelectDistinct

The problem with this code was that it did not support multiple distinct fields in a single table. This is a slight drawback for many distinct usages such as drop down lists which typically require value and id.

Rather than rewriting the function above to support multiple fields, I took a closer look at the framework and the distinct functionality is actually wrapped up in the DataView.ToTable Method which was released with .NET 2.0.

Summary usage:

    DataView distinctTableView = new DataView(sourceDataTable);
    DataTable distinctResult;
    // Select Distinct on Col1 and Col2
    distinctResult = distinctTableView.ToTable(true, new[] { "Col1","Col2" });

More comprehensive Examples using a Unit Test

[Test]
public void SelectDistinctValuesExample()
{
    DataTable dt = new DataTable();
    dt.Columns.Add("Col1", typeof (int));
    dt.Columns.Add("Col2", typeof(int));
    dt.Columns.Add("Col3", typeof(int));
    dt.AcceptChanges();

    dt.LoadDataRow(new object[] {1,1,1}, true);
    dt.LoadDataRow(new object[] {1,1,2}, true);
    dt.LoadDataRow(new object[] {1,1,3}, true);
    dt.LoadDataRow(new object[] {1,2,1}, true);
    dt.LoadDataRow(new object[] {1,2,2}, true);
    dt.LoadDataRow(new object[] {1,2,3}, true);
    dt.LoadDataRow(new object[] {1,3,1}, true);
    dt.LoadDataRow(new object[] {1,3,2}, true);
    dt.LoadDataRow(new object[] {1,3,3}, true);

    dt.LoadDataRow(new object[] {2,1,1}, true);
    dt.LoadDataRow(new object[] {2,1,2}, true);
    dt.LoadDataRow(new object[] {2,1,3}, true);
    dt.LoadDataRow(new object[] {2,2,1}, true);
    dt.LoadDataRow(new object[] {2,2,2}, true);
    dt.LoadDataRow(new object[] {2,2,3}, true);
    dt.LoadDataRow(new object[] {2,3,1}, true);
    dt.LoadDataRow(new object[] {2,3,2}, true);
    dt.LoadDataRow(new object[] {2,3,3}, true);

    DataView distinctTableView = new DataView(dt);
    DataTable distinctResult;

    // Example 1 Select Distinct on Col1
    distinctResult = distinctTableView.ToTable(true, new[] { "Col1" });
    // assert result is  
    // 1
    // 2 
    Assert.AreEqual(2, distinctResult.Rows.Count);
    Assert.AreEqual(1, distinctResult.Rows[0][0]);
    Assert.AreEqual(2, distinctResult.Rows[1][0]);


    // Example 2 Select Distinct on Col2
    distinctResult = distinctTableView.ToTable(true, new[] { "Col2" });
    // assert result is  
    // 1
    // 2 
    // 3
    Assert.AreEqual(3, distinctResult.Rows.Count);
    Assert.AreEqual(1, distinctResult.Rows[0][0]);
    Assert.AreEqual(2, distinctResult.Rows[1][0]);
    Assert.AreEqual(3, distinctResult.Rows[2][0]);

    distinctResult = distinctTableView.ToTable(true, new[] { "Col1" , "Col2" });
    Assert.AreEqual(6, distinctResult.Rows.Count);
    // assert result is
    // 1,1
    // 1,2
    // 1,3
    // 2,1
    // 2,2
    // 2.3
    Assert.AreEqual(1, distinctResult.Rows[0][0]);
    Assert.AreEqual(1, distinctResult.Rows[1][0]);
    Assert.AreEqual(1, distinctResult.Rows[2][0]);
    Assert.AreEqual(1, distinctResult.Rows[0][1]);
    Assert.AreEqual(2, distinctResult.Rows[1][1]);
    Assert.AreEqual(3, distinctResult.Rows[2][1]); // only do 6 cells assume rest are correct
}

Tags: ,

C# | DataTable

Cement Expressions as values in a DataTable

by Kevin Bosch 4. August 2010 21:27

I came across a problem the other day which was interesting. Using the SqlDataAdapter it will not allow you to use a field with an expression.; This makes sense as the bulk adapter was built to update a data source which was used to retrieve the data in the first place. As such an expression is simply a calculation of other derived fields and does not need to be saved. In using the SqlDataAdapter for a bulk insert operation one of the fields which was in the source was actually an expression. When trying to run the bulk update from the SqlDataAdapter the code generated the flowing error:

System.InvalidOperationException: The column mapping from SourceColumn 'colname' failed because the DataColumn 'colname' is a computed column.

So to get around this problem I decided to write a procedure to cement the expressions as values pre update. This works much like a copy and paste by values in excel would do. You pass in a datatable which had expression in and it. The code would take the expressions copy the values out into a temp column delete the original column then rename the temp column to the original. The net effect of doing this is that the expressions would be converted to values which could be used in the SqlDataAdapter for a bulk update

The code to Cement Expressions is below:

// gets the first col which has an expression on. // This will need to be refactored if you have expressions based on expressions as you will // need to be aware of dependency order // if no expressions are found it will return null. private DataColumn GetColunmWithExpression(DataTable dt) { DataColumn result = null; foreach (DataColumn dcloop in dt.Columns) { if (!string.IsNullOrEmpty(dcloop.Expression)) { result = dcloop; break; } } return result; } public void CementExpressionsAsValues(DataTable dt) { DataColumn colWithExpression = GetColunmWithExpression(dt); int excapeCounter = 0; while (colWithExpression != null && excapeCounter < dt.Columns.Count) { string tempColName = colWithExpression.ColumnName + Guid.NewGuid().ToString(); dt.Columns.Add(tempColName, colWithExpression.DataType); foreach(DataRow dr in dt.Rows) { dr[tempColName] = dr[colWithExpression.ColumnName]; } dt.Columns.Remove(colWithExpression); dt.Columns[tempColName].ColumnName = colWithExpression.ColumnName; colWithExpression = GetColunmWithExpression(dt); excapeCounter++; } }

Tags: ,

C# | DataTable

Calendar

<<  February 2012  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
2728291234
567891011

View posts in large calendar
Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012 Code Associate