Defining LightSpeed entities at run-time, part 1

I say, here’s fun. –Prince George, Blackadder III

Ever since JD unleashed LightSpeed.MetaData on an unsuspecting world, we’ve been inundated by people asking about the possibility of declaring entities at run-time, for example to reflect a database schema that can be modified or extended by users, or to allow common batch actions to be applied to different databases without having to build explicit models for every database. The answer is that this isn’t directly possible, because LightSpeed needs a .NET class to represent each entity type or database table. This class represents the database schema through its fields and attributes.

But it is possible to create .NET classes at run-time, using the various code generation technologies built into the .NET Framework, such as CodeDOM and Reflection.Emit. Since LightSpeed entities are just code, and pretty predictable code at that, that makes it possible to create LightSpeed entity classes at run-time. And LightSpeed is perfectly happy with this: it doesn’t care whether the entity classes come from a graphical model, run-time code generation or cosmic rays messing with your hard disk. As long as the entity classes are there, LightSpeed can work with them!

Which just leaves the small problem of actually generating the classes.

Introducing Mindscape.LightSpeed.Dynamic

The best technology to use for this depends on your requirements. If you want to be able to inject custom business logic into your run-time entities (under configuration control) then CodeDOM is a good choice because it allows you to think at the C# statement level. If you just want boilerplate entities then Reflection.Emit is a good choice because it is slightly more efficient, though CodeDOM is easier to use if you’re not confident with MSIL. Here’s a fully working sample generator. This one uses Reflection.Emit because, you know, beard of knowledge, but it’s provided as source code so you can extend, reimplement or tweak it as required.

LightSpeed dynamic entity definition sample

I’m not going to go into detail about the sample. It’s mostly a bunch of intimidating looking but actually rather routine IL generators, gaily bedecked with hacks to get around poor generics support in .NET Reflection and topped off with the obligatory expression tree walker to support a nice syntax for applying attributes. Leave a comment or post in the forum if you want to understand more about the workings of the entity generator.

Creating entities at run-time

Here’s an example of defining an entity using the LightSpeed.Dynamic sample library:

DynamicModel model = new DynamicModel("MyModel");
model.AddEntityType("Penguin", typeof(int))
     .AddScalarField("Name", typeof(string))
     .AddScalarField("Age", typeof(int)));

AddEntityType takes the name of the entity type, and the identity type. You can then call AddScalarField to add a field (and wrapper property) with a given name and type. So the model above is equivalent to the following entity declaration:

public class Penguin : Entity<int>
{
  private string _name;
  private int _age;
 
  public string Name { get { ... } set { ... } }
  public int Age { get { ... } set { ... } }
}

where the property implementations are the LightSpeed boilerplate.

Obviously in reality you wouldn’t hardwire names and types into your code as shown above: instead, you’d build the entity dynamically from your desired source:

DynamicModel model = new DynamicModel("MyModel");
DynamicEntityType type = model.AddEntityType("UserContent", typeof(int));
foreach (var userColumn in userColumns)
  type.AddScalarField(userColumn.ColumnName, userColumn.DataType);

You can keep building up the model incrementally until you need to use it, at which point you need to materialise what you’ve been building as an honest to goodness CLR class. For a single type you can do this by calling CreateType(), but most realistic models will have multiple interdependent entities due to associations. For these models you can get types out by name:

Type penguinType = model.GetType("Penguin");

Once you get a type out of the model, all the types in the model are frozen: you can’t make any further changes to them. This is because LightSpeed needs to have proper materialised types in associations, rather than just the ‘builders’ that allow you to keep changing the model.

Using the dynamic entity type

Obviously, you don’t have compile-time access to the classes and properties of dynamic entity types, so you can’t write normal strongly-typed C# code against them. The easiest way to work with dynamic entity types is to use the C# 4.0 dynamic keyword, which allows you to write property and method names in your C# code and have them resolved at run-time. Writing weak-typed queries with dynamic leads to non-idiomatic code but the sample includes some handy extension methods to hide this away. So you can use the FindDynamic extension method to perform queries:

IList<dynamic> all = unitOfWork.FindDynamic(penguinType);
IList<dynamic> elderly = unitOfWork.FindDynamic(penguinType, Entity.Attribute("Age") > 10);

Because the results come through as dynamic, you can access their properties without the compiler sending you horses’ heads through the post:

foreach (dynamic penguin in elderly)
  Console.WriteLine(penguin.Name + " is past it");

And of course you can use the entities to perform inserts, updates or deletes:

private void PerformAnnualPenguinMaintenance()
{
  dynamic p = Activator.CreateInstance(penguinType);
  p.Name = "Baby";
  p.Age = 0;
  unitOfWork.Add(p);
 
  foreach (dynamic penguin in all)
  {
    if (penguin.Age > 10)
      unitOfWork.Remove(penguin);
    else
      penguin.Age = penguin.Age + 1;
  }
 
  unitOfWork.SaveChanges();
}

As you can see, dynamic allows you to work with the dynamically generated entity type almost exactly as you would with a statically defined one.

But what if I have a table or column whose name can’t be spoken in C#? And what about associations? Or validation?

Chill, bro (as I understand the kids are saying). You can still enjoy all of this LightSpeedy goodness with your dynamic entities. I’ll show you how in part 2.

Tagged as LightSpeed

8 Responses to “Defining LightSpeed entities at run-time, part 1”

  • Crazy delicious: defining LightSpeed entities at runtime…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  • Fantastic! This is just what I was waiting for :)

  • [...] the first two parts of this series, I demonstrated a little library that allows you to define LightSpeed [...]

  • [...] part one of this series we saw how to create a simple standalone entity class at run-time so that we could use LightSpeed [...]

  • Hi,

    Why should I need to say AddScalarField etc. Why not extend DynamicObject and intercept/and build the model dynamically? Otherwise it feels more like some sort of a mapping generation interface.

    //Daniel

  • Because LightSpeed examines the fields in the object compose the SQL query, and it uses those fields to store the results. It’s not an ‘encapsulated DataRow’ pattern: it is strongly typed even at the storage level.

    Also, we map types to tables, so either we would have had to rewrite that as well, or users in many scenarios would have had to create derived types of the hypothetical DynamicEntity, which would have defeated the point.

    So it’s a fair point, and indeed implementing IDynamicMetaObjectProvider (can’t use DynamicObject because LightSpeed entities have to derive from Entity) was my first thought; but I don’t think it’s possible without a ground-up rewrite of LightSpeed.

  • Ok, so you got a constraint to Entity. Create a DynamicEntity that extends DynamicObject and contains an. entity and your dynamic model which is built when people dynamically assigns values to it.

    //Daniel

  • That might work for inserts and updates, but not for selects. Consider:

    var dynamicEntity = FindPenguinById(1);
    Console.WriteLine(penguin.Age);

    How does LightSpeed know that the entity also has a Name property? Where does it store the Name value? The only way I can think of is to do a SELECT * instead of selecting specific columns, stash all the values in the hypothetical DynamicEntity, and return them on demand. But this means we’re either bypassing LightSpeed, or rewriting it from the ground up.

    I agree that if I were designing an ORM to be ‘natively’ dynamic (like Rails or that dynamic mapper I saw from Microsoft whose name I can’t remember), then no, you wouldn’t want to have to set up all the mappings this way; but that’s not what I’m trying to show here. LightSpeed *needs* to know the mappings before it starts loading data: I’m trying to show how to achieve that at run-time instead of design-time.

  • Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top