Defining LightSpeed entities at run-time, part 3

In the first two parts of this series, I demonstrated a little library that allows you to define LightSpeed entity classes at run-time, enabling scenarios such as user-configured databases or schema-agnostic batch processing. Throughout those parts I used the C# dynamic keyword to allow me to access properties that weren’t defined at design time.

However, to access a property through dynamic, I still have to know the name of the property at compile time, even if I don’t know the entity type on which the property will be declared.

A lot of the time, this isn’t a problem. For example, if you want to allow the user to filter or sort on a particular property, you only need the string name anyway:

Query query = new Query {
  EntityType = dynamicEntityType,
  QueryExpression = Entity.Attribute(userFilterField) == userFilterValue,
  Order = Order.By(userSortField)
};

But what if your application needs to display data from specific fields to the user? You can’t write myEntity.userDisplayField, because the C# runtime binder will look for a property whose name is “userDisplayField”, not a property whose name is the value of userDisplayField.

Enter LightSpeed 4, and the shiny new LightSpeed.MetaData library.

Full metal dynamic

With the metadata library, you can get information about the fields and associations declared on an entity — well, whoop de do, we already know that, we just declared them! — and you can get the values of those fields and associations on a particular entity. (In a future build you’ll also be able to set the values, but that’s not in the current beta. Maybe tomorrow.)

Let’s see how it works.

model.AddEntityType("Hog Farm", typeof(int))
     .AddScalarField("Farmer Name", typeof(string));
 
using (IUnitOfWork unitOfWork = context.CreateUnitOfWork())
{
  Type entityType = model.GetType("Hog Farm");
  dynamic farm = unitOfWork.FindById(entityType, 1);
 
  var entityInfo = EntityInfo.FromType(entityType);
  var farmerNameField = entityInfo.Fields.Single(f => f.PropertyName == "Farmer Name");
 
  Console.WriteLine(farmerNameField.GetValue(farm));
}

This is a bit cluttered, so let’s spin up a handy extension method:

public static class EntityInfoExtensions
{
  public static FieldInfo Field(this EntityInfo entityInfo, string name)
  {
    return entityInfo.Fields.Single(f => f.PropertyName == name);
  }
}

Now we can write:

Console.WriteLine(entityInfo.Field("Farmer Name").GetValue(farm));

You can imagine similar extension methods and similar syntax for associations. And if you’re writing a lot of code like this then you may decide you don’t need C# dynamic, in which case you can use variables of type Entity and stick on another extension method:

public static object GetFieldValue(this Entity entity, string name)
{
  return entity.EntityInfo().Field(name).GetValue(entity);
}
 
// Allows us to write:
var farm = unitOfWork.FindById(entityType, 2);
Console.WriteLine(farm.GetFieldValue("Farmer Name"));

I see what you did there

As a side note, you may have noticed that Farmer Name isn’t a valid C# property name because it has a space in it. In part two we got around this by calling the property FarmerName and mapping it to the spacey column name with a ColumnAttribute. When we use the metadata library, we don’t need to worry about this because we’re addressing the field by name, and the CLR doesn’t care that the field name has a space in it. This is why in the future all coding will be done through Reflection.Emit and the LightSpeed metadata API.

That’s it

In this series we’ve seen how to define entity classes at run-time, customise their mappings and load behaviour, and work with them through C# dynamic binding and through the LightSpeed metadata API.

All the source code for the sample run-time entity library can be downloaded here. You’re welcome to modify it as you see fit to add new capabilities or to handle some of the stuff I didn’t implement. Please leave a comment or drop a note in the forums if you do — we’d be fascinated to hear what you’re up to!

Tagged as LightSpeed

Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top