CTICoder

A random spillage of programming (and other) thoughts

Archive for August, 2008

Using Enums with Entity Framework

Posted by Michael Bray on August 16, 2008

I was a bit disappointed (but not surprised) to see that EF didn’t seem to support Enum types on the entities it generates.  Even if you try to manually change the type to an enum type defined in your project, it will complain saying that it can’t find the type.  (Maybe I’m just not doing it right??)

Anyway, there is a seemingly elegant solution… utilize the fact that the entity classes are partial.

For example, if you have an enum:

public enum AccountType
{
    Internal,
    Public,
    External
}

that you want to store in the database (say as part of an ‘Account’ object) then you can just extend the partial ‘Account’ class and change a few properties in the .edmx file… Personally, I would rather have the C# class maintain the naming conventions than the database fields, so in this example I name the field in the database with a ‘t’, to indicate that it is a ‘type’. You could use anything – ‘e’ for ‘enum’, or even ‘enum’ all the way. So for example:

image

Then in the properties of the ‘tAccountType’ field, set the Getter and Setter to ‘private’:

image

And finally, add a class to your code that extends the partial class:

public partial class Account
{
    public AccountType? AccountType
    {
        get { return (AccountType)this.tAccountType; }
        set { this.tAccountType = (int?)value; }
    }
}

…and voila… the class now provides the enum the way it should, and still manages the underlying entity.

Advertisements

Posted in Uncategorized | 9 Comments »

Object Inheritance in Entity Framework

Posted by Michael Bray on August 16, 2008

I started playing with the Entity Framework that was officially released a few days ago along with VS2008 SP1 and .NET 3.5 SP1.  After playing with Linq to SQL, there was one particular feature that I was interested in – the multi-table object inheritance capabilities.  Linq to SQL had the ability to provide inherited types, but the underlying data source stored all of the different types in the same table, so-called Table-Per-Hierarchy (TPH) storage.  In this mechanism, a field in the database is used to distinguish the type that the row represents and it is generated accordingly.  This would lead to artifacts such as a lot of null fields in the database – not such a bad thing as NULL types don’t take up much (if any) space but the table structure is very flat, and not so accommodating if your object hierarchy is more than a few levels deep.

Linq to Entities provides a new type of storage, Table-Per-Type (TPT) that provides a much higher fidelity mapping to the underlying tables as compared to the business classes that are being worked with.  In this mode, there is a separate table for each level of the class hierarchy.  For deep class hierarchies this seems to make more sense.  The down side is that at the very root of the class hierarchy you end up with a table that is gi-normous.

One of the things that I plan to do to mitigate the size of that base table is to link the objects with Guids (uniqueidentifier) instead of ints.  By doing this, I have a root object that has a Guid, and two other properties that I want to be common on all objects – DateCreated and DateModified.  To maximize efficiency, the Guid column is the primary key, and is also the RowGUID.  The derived tables have the same characteristic Guid RowGuid column, and this is the value that I use to link the tables together in a 1-to-1 relationship.  This way I really have a true hierarchy without any additional data storage overhead for the key field. 

Another nice benefit that this provides is that any object in any table can make reference to any other object, and I know that I can find that object.  I also plan to control some of the portions of the Guid, and I’ll use that to identify the type of object (eg Person, Account, Order, etc) so I can know exactly what type of object it is and can load it accordingly.

Here’s an example hierarchy:

image

Note that DbObject is the root of the hierarchy, and assuming that I maintain this structure for all objects in the database, it will grow VERY large.  Is this a problem?   Possibly, but I expect SQL can deal with it.  The main concern is that any time I load one of these objects using Linq to Entities, the SQL query will include a JOIN to the DbObject table, which could be a killer, but possibly not as bad as you might think.  Initial testing indicates that I can load the entire Cisco parts database (which is more than 330,000 items and includes about 15 properties per item) in about 23 seconds.  Not great if the user is waiting, but how often am I going to load the entire database?  Not too often, I hope.  But it isn’t too bad either…  I suspect the use of the ObjectId as the joining field (and the fact that it is the primary key and RowGuid) are significantly helping that result time.

The other idea that I have to help mitigate that huge JOIN is that I may generate two nearly identical models – one with the DbObject and one without it.  This of course doesn’t affect the underlying database – it just affects how Linq to Entities interacts with it.  In that model, it will never attempt to JOIN the DbObject table.  If I’m only looking at the data (not adding a new object or updating it) then I have no need for the data contained in DbObject.  Of course, it also means that I’ll need to have two different models for every object (eg Person and PersonNRO (NRO = NoRootObject)) but it might be worth the gain. This will of course double the work to maintain the structure when changes are made, but I think that I could use a tool or write one myself that would generate the .edmx file for both objects based on a common definition. That’s a project for a rainy day, though.

Anyway, the one odd thing I’ve found as I’ve started working with this stuff is that in the normal model (with the DbObject), the Entity Container object ONLY provides access to root objects (in this case, a member called DbObjectSet).  So you don’t get a direct reference to any of the derived types at all:

image

Note that there is no ‘PersonObjectSet’, ‘EmployeeObjectSet’, ‘ContactObjectSet’ or ‘AccountObjectSet’.  So how do you load those types of entities?  You have to use a function that is provided on DbObjectSet:

Model1Container c = new Model1Container(); 
var people = from p in c.DbObjectSet.OfType<Person>() 
             select p; 

This will return a list of all the objects in the People table (along with the relevant data from DbObject) as one single class ‘Person’.  Amazing!

Posted in .NET | Tagged: , | 2 Comments »

Olympic Size BSOD

Posted by Shaun McDonnell on August 13, 2008

Oops.  What’s that on the ceiling?

olympics.bsod

MBray edits:

One of the images of the computer malfunction that appears on the gizmodo website

Posted in Uncategorized | Leave a Comment »

MsBuild: Custom Task: Drive Mapper

Posted by Shaun McDonnell on August 11, 2008

For those not familiar with MsBuild, it is Microsoft’s Build Engine that comes with the .NET 2.0 Framework.  It is a highly flexible and highly reusable framework that makes it possible to build almost any type of software and deploy any type of product.  Personally, I think it is an excellent tool.

One common task that I have wanted to perform in MsBuild is drive mapping.  If I could have a Custom MsBuild Task that would map a drive letter to a UNC path, I could deploy my application to anywhere within the network (testing, staging, and even production).  So, recently, I set out to create that Custom MsBuild Task and here are the results and the code.

First off, in order to create an Custom MsBuild Task, you have to inherit from Microsoft.Build.Utilities.Task and then implement the bool Execute() member method required by the base class.

In order to use the Win32 functions that map and unmap drives.  We’re going to have to use Platform Invoke (P/Invoke).  The three platform methods that I use are (click on the function will take you to its documentation):

Take a look the implementations of the above methods in C#

[DllImport("mpr.dll")] public static extern int WNetAddConnection2A ( [MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource, [MarshalAs(UnmanagedType.LPStr)] string lpPassword, [MarshalAs(UnmanagedType.LPStr)] string UserName, int dwFlags ); [StructLayout(LayoutKind.Sequential)] public struct NETRESOURCEA { public int dwScope; public int dwType; public int dwDisplayType; public int dwUsage; [MarshalAs(UnmanagedType.LPStr)] public string lpLocalName; [MarshalAs(UnmanagedType.LPStr)] public string lpRemoteName; [MarshalAs(UnmanagedType.LPStr)] public string lpComment; [MarshalAs(UnmanagedType.LPStr)] public string lpProvider; public override string ToString() { string str = "LocalName: " + lpLocalName + " RemoteName: " + lpRemoteName + " Comment: " + lpComment + " lpProvider: " + lpProvider; return (str); } } [DllImport("mpr.dll")] public static extern int WNetCancelConnection2A ( [MarshalAs(UnmanagedType.LPStr)] string lpName, int dwFlags, bool fForce ); [DllImport("Kernel32.dll")] public static extern int FormatMessage ( int dwFlags, IntPtr lpSource, int dwMessageID, int dwLanguageID, StringBuilder lpBuffer, int nSize, IntPtr arguments );

 

Together, all of these methods will do the following:

  • Map a Drive
  • Unmap a Drive
  • Display an Error Message returned by the operating system if the map fails.

Now that we have all of these methods in place, we need to create properties to be used within the MsBuild Xml file for our task.  Here is the code for those properties:

[Required] public bool RemoveIfExists { get { return _RemoveIfExists; } set { _RemoveIfExists = value; } } [Required] public string Path { get { return _Path; } set { _Path = value; } } [Required] public string DriveLetter { get { return _DriveLetter; } set { _DriveLetter = value; } }

Then, we need to have some methods that will make calling the external methods above a little bit smoother:

protected int MapDrive() { NETRESOURCEA[] resources = new NETRESOURCEA[1]; resources[0] = new NETRESOURCEA(); resources[0].dwType = 1; int dwFlags = 1; resources[0].lpLocalName = _DriveLetter; resources[0].lpRemoteName = _Path; resources[0].lpProvider = null; int ret = WNetAddConnection2A(resources, null, null, dwFlags); return ret; } protected int UnmapDrive() { int ret = WNetCancelConnection2A(_DriveLetter, 0, true); return ret; }

Lastly, we need to implement the execute method because it is the method that will be executed by MsBuild:

public override bool Execute() { int ret = MapDrive(); int ret3 = 0; bool success = false; StringBuilder message = new StringBuilder(500); int messageId = FormatMessage(4096, IntPtr.Zero, ret, 0, message, 500, IntPtr.Zero); Log.LogMessage("Format Message Result: {0}", new object[] { ret }); Log.LogMessage("Mapping Result: {0}", new object[] { message.ToString() }); if (ret == 85 && RemoveIfExists) { int ret2 = UnmapDrive(); StringBuilder unmapMessage = new StringBuilder(500); FormatMessage(4096, IntPtr.Zero, ret2, 0, unmapMessage, 500, IntPtr.Zero); Log.LogMessage("Format Message Result: {0}", new object[] { ret2 }); Log.LogMessage("Unmapping Result: {0}", new object[] { unmapMessage.ToString() }); ret3 = MapDrive(); StringBuilder mapMessage = new StringBuilder(500); int mapMessageId = FormatMessage(4096, IntPtr.Zero, ret3, 0, mapMessage, 500, IntPtr.Zero); Log.LogMessage("Format Message Result: {0}", new object[] { ret3 }); Log.LogMessage("Second Mapping Result: {0}", new object[] { mapMessage.ToString() }); success = !(ret3 > 85); } else { success = !(ret > 0); } return success; }

Here’s a look at all of the code combined:

using System; using System.Collections.Generic; using System.Text; using Microsoft.Build.Utilities; using Microsoft.Build.Framework; using System.Runtime.InteropServices; namespace Cti.MsBuild.Tasks { [StructLayout(LayoutKind.Sequential)] public struct NETRESOURCEA { public int dwScope; public int dwType; public int dwDisplayType; public int dwUsage; [MarshalAs(UnmanagedType.LPStr)] public string lpLocalName; [MarshalAs(UnmanagedType.LPStr)] public string lpRemoteName; [MarshalAs(UnmanagedType.LPStr)] public string lpComment; [MarshalAs(UnmanagedType.LPStr)] public string lpProvider; public override string ToString() { string str = "LocalName: " + lpLocalName + " RemoteName: " + lpRemoteName + " Comment: " + lpComment + " lpProvider: " + lpProvider; return (str); } } public class MapDriveTask : Microsoft.Build.Utilities.Task { protected string _Path = String.Empty; protected string _DriveLetter = String.Empty; protected bool _RemoveIfExists = false; [DllImport("mpr.dll")] public static extern int WNetAddConnection2A ( [MarshalAs(UnmanagedType.LPArray)] NETRESOURCEA[] lpNetResource, [MarshalAs(UnmanagedType.LPStr)] string lpPassword, [MarshalAs(UnmanagedType.LPStr)] string UserName, int dwFlags ); [DllImport("mpr.dll")] public static extern int WNetCancelConnection2A ( [MarshalAs(UnmanagedType.LPStr)] string lpName, int dwFlags, bool fForce ); [DllImport("Kernel32.dll")] public static extern int FormatMessage ( int dwFlags, IntPtr lpSource, int dwMessageID, int dwLanguageID, StringBuilder lpBuffer, int nSize, IntPtr arguments ); [Required] public bool RemoveIfExists { get { return _RemoveIfExists; } set { _RemoveIfExists = value; } } [Required] public string Path { get { return _Path; } set { _Path = value; } } [Required] public string DriveLetter { get { return _DriveLetter; } set { _DriveLetter = value; } } protected int MapDrive() { NETRESOURCEA[] resources = new NETRESOURCEA[1]; resources[0] = new NETRESOURCEA(); resources[0].dwType = 1; int dwFlags = 1; resources[0].lpLocalName = _DriveLetter; resources[0].lpRemoteName = _Path; resources[0].lpProvider = null; int ret = WNetAddConnection2A(resources, null, null, dwFlags); return ret; } protected int UnmapDrive() { int ret = WNetCancelConnection2A(_DriveLetter, 0, true); return ret; } public override bool Execute() { int ret = MapDrive(); int ret3 = 0; bool success = false; StringBuilder message = new StringBuilder(500); int messageId = FormatMessage(4096, IntPtr.Zero, ret, 0, message, 500, IntPtr.Zero); Log.LogMessage("Format Message Result: {0}", new object[] { ret }); Log.LogMessage("Mapping Result: {0}", new object[] { message.ToString() }); if (ret == 85 && RemoveIfExists) { int ret2 = UnmapDrive(); StringBuilder unmapMessage = new StringBuilder(500); FormatMessage(4096, IntPtr.Zero, ret2, 0, unmapMessage, 500, IntPtr.Zero); Log.LogMessage("Format Message Result: {0}", new object[] { ret2 }); Log.LogMessage("Unmapping Result: {0}", new object[] { unmapMessage.ToString() }); ret3 = MapDrive(); StringBuilder mapMessage = new StringBuilder(500); int mapMessageId = FormatMessage(4096, IntPtr.Zero, ret3, 0, mapMessage, 500, IntPtr.Zero); Log.LogMessage("Format Message Result: {0}", new object[] { ret3 }); Log.LogMessage("Second Mapping Result: {0}", new object[] { mapMessage.ToString() }); success = !(ret3 > 85); } else { success = !(ret > 0); } return success; } } }

 

If you compile this code, you can then use this task in your MsBuild Xml file like so:

<?xml version="1.0" encoding="utf-8" ?> <Project DefaultTargets="MyTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <UsingTask AssemblyFile="Cti.MsBuild.Tasks.dll" TaskName="Cti.MsBuild.Tasks.MapDriveTask" /> <Target Name="MyTarget"> <MapDriveTask Path="\\coke\Projects" DriveLetter="Z:" RemoveIfExists="true" /> </Target> </Project>

There you have it.  MsBuild is going to make life a whole alot easier when it comes to building, compiling, and deploying.

Posted in MsBuild | 1 Comment »

Annoying <Script> Tag Bug When Using Client-Side Web Services

Posted by Shaun McDonnell on August 11, 2008

This one took me hours to figure out and there is no rhyme or reason to it.  Yet, I have been able to reproduce it consistently so I thought I would share the details here.

Microsoft .NET 3.5 introduced a new Attribute called [ScriptService] that we can put at the top of our WCF Services or our ASMX Web Services.  The addition of this attribute to a service allows for that service to be called from the client-side of a web application using javascript.

 [ScriptService] public class Service : System.Web.Services.WebService 

Behind the scenes, .NET creates some javascript and it is automatically embedded in your code.  You can actually see the javascript it creates by just adding a ‘/js’ to the end of the ASMX url like this:

http://services.customers.ctiusa.com/cisco/ipphoneservice.asmx/js

If you want to use the client-side accessible web service from a pure HTML page (or something else that isn’t .NET) you can actually make a direct reference to that javascript path like this:

 <script language="javascript" src="http://services.customers.ctiusa.com/cisco/ipphoneservice.asmx/js" type="text/javascript">

However, this won’t work and the browser won’t be able to find the javascript reference.  Why?  Because it doesn’t like ‘/>’ for ending the script tag.  It wants the full ‘ ‘ like this:

 <script language="javascript" src="http://services.customers.ctiusa.com/cisco/ipphoneservice.asmx/js" type="text/javascript">  

Once you do that, you will be able to access your web service through pure asynchronous javascript calls.

-Shaun

 

Posted in .NET, Bugs, Tips | 1 Comment »

Simpler pattern for SqlConnection

Posted by Michael Bray on August 8, 2008

In our May Contest, there was a post about SqlConnection pattern:

using (SqlConnection conn = new SqlConnection(connectionString)) 

  try 
  { 
    conn.Open(); 
    //add commands and execution here 
  } 
  catch(Exception ex) 
  { 
    string error = ex.Message; //do something with the error 
  } 
  finally 
  { 
    if(conn.State == ConnectionState.Open) conn.Close(); 
  } 
}

With the try/catch there to make sure the connection gets closed. Well it turns out that the try/catch in this statement is unnecessary, since the Dispose(…) on SqlConnection performs a Close() on the connection anyway… here’s the disassembled code from Dispose(…):

protected override void Dispose(bool disposing)
{
  if (disposing)
  {
    this._userConnectionOptions = null;
    this._poolGroup = null;
    this.Close();
  }
  this.DisposeMe(disposing);
  base.Dispose(disposing);
}

So the original code can simplify down into:

using (SqlConnection conn = new SqlConnection(connectionString)) 

  conn.Open();
  … 
}

Posted in Uncategorized | Leave a Comment »