How to instantiate derived LINQ to SQL entities

A lot has been written about how to set up (table per class) inheritance with LINQ to SQL. I use it a lot and I always use an enum as inheritance discriminator.
But now I need to instantiate new instances of derived classes in code. The user is provided a ComboBox with the available enum values and an ‘Add’ button. When the ‘Add’ button is pressed, I need an instance of the type that ‘belongs’ to the selected enum.

The obvious (and fastest) approach would be: the switch statement.

	public enum MyEnum
	{
		Rectangle = 1,
		Triangle = 2,
	}

	public MyShape CreateInstance(MyEnum requiredShape)
	{
		switch (requiredShape)
		{
			case MyEnum.Rectangle:
				return new MyRectangle();

			case MyEnum.Triangle:
				return new MyTriangle();

			default:
				throw new System.NotSupportedException(requiredShape.ToString());
		}
	}

The drawbacks of this approach are:

  • For each set of classes with subclasses you will need to write a switch statement.
  • Maintainability; when the inheritance specifications change, you also need to update this code.

It would be easier (less code, better maintainable) to have a method that will figure out the class to instantiate from the inheritance mappings of the LINQ entity. Using reflection on the InheritanceMappingAttributes is how to determine the type that belongs to a certain enum value. Indeed, this will be slower, but I will take that for granted.
Some LINQ entities might have been set up like this:

	[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.MyShape")]
	[global::System.Data.Linq.Mapping.InheritanceMappingAttribute(Code="Rectangle", Type = typeof(MyRectangle), IsDefault = true)]
	[global::System.Data.Linq.Mapping.InheritanceMappingAttribute(Code="Triangle", Type = typeof(MyTriangle))]
	public abstract partial class MyShape : INotifyPropertyChanging, INotifyPropertyChanged
	{
		[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Type", DbType="Int NOT NULL", CanBeNull=false, IsDiscriminator=true)]
		public MyEnum Type
		{
			get { ... }
			set { ... }
		}
		...
	}

	public partial class MyRectangle : MyShape
	{
		...
	}

	public partial class MyTriangle : MyShape
	{
		...
	}

Let’s create a factory(ish) class (it’s not a fully equipped object factory…) that does the reflection work and the instantiation of the appropriate class. It can be a simple static class with a static method that will do the job:

	public static class LinqEntityFactory
	{
		public static TBaseClass CreateEntity(Enum inheritanceModifier)
			where TBaseClass : class
		{
		...
		}
	}

Time to the implement the CreateEntity method. Always start with checking the input values:

			// Check the input.
			if (inheritanceModifier == null)
			{
				throw new System.ArgumentNullException("inheritanceModifier");
			}

The type of the class to instantiate is specified in one of the attributes. If the attribute is not found, fall back to the attribute that is marked as default.

			Type enumType = inheritanceModifier.GetType();
			// Get the inheritance attributes.
			MemberInfo info = typeof(TBaseClass);
			var attribs = info.GetCustomAttributes(typeof(InheritanceMappingAttribute), false).Cast();

			// Find the inheritance attribute that specifies the requested object type.
			// Instead of a string comparison, try to parse the Code value. The Code
			// of the attribute is always provided as string, representing either
			// the name of the Enum value or the (int) value of it.
			var attr = attribs.Where(a =>
				inheritanceModifier.Equals(Enum.Parse(enumType, a.Code.ToString())))
				.SingleOrDefault();
			// If the attribute was not found, find the inheritance default.
			if (attr == null)
			{
				attr = attribs.Where(a => a.IsDefault).SingleOrDefault();
			}

If the provided class is really marked with InheritanceModifierAttributes, then we now have the attribute. What is left is to create the instance.
But beware, the type may represent an abstract class. Instantiating abstract classes at runtime cause errors which can be hard to debug. Therefore, throw an exception in this case providing the value of the enum.
Also an additional check is added for the case the method is called on an object that has no InheritanceModifierAttributes at all.

			// The attribute contains the type of the object to instantiate.
			// Verify whether the required type is not abstract. Abstract types
			// can occur when the default inheritance attribute has been
			// specified with an abstract class. In that case, raise an
			// error because it can be hard to debug a runtime abstract class
			// instantiation exception.
			// By the way, there is always a default inheritance attribute,
			// however since LINQ to SQL entities inherited from object (by default)
			// this method can also be called on objects without any inheritance
			// specifier. So always check for null.
			if (attr == null || attr.Type.IsAbstract)
			{
				throw new System.NotSupportedException(inheritanceModifier.ToString());
			}
			else
			{
				// Create and return the required instance.
				return (TBaseClass)Activator.CreateInstance(attr.Type);
			}

And this is how to use the factory:

MyEnum requiredShape = MyEnum.Triangle;
MyShape shape = LinqEntityFactory.CreateEntity<MyShape>(requiredShape);
// shape is now an instance of MyTriangle.

That’s it. Thanks for reading an I hope you will find this post useful.

About these ads

About He Willem

Educated in electronics and information technology. Experienced software engineer, currently working as lead C# and WPF developer. View all posts by He Willem

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: