Introduction to the Lightswitch Weakly Typed API
The typical coding experience in a Lightswitch app revolves around the objects you've created -- your tables, your screens, queries, and so on. When you click "Write Code" in our design experience, there is a rich and strongly typed programming experience for you to use. Your intellisense picker understands that a "Customer" exists and has properties like "LastName". And you can write code that looks like
private void Customer_Created()
{
this.LastName = "Smith";
}
To make this happen, as you are working in the Lightswitch design experience, Lightswitch is generating code on your behalf that defines the Customer class and all of its properties. These objects aren't simple; there are separate concrete implementations of them for client and server, and the magic to make a Customer remote back and forth between Client and Server is all handled for you via code generation.
Lightswitch gives you a programming experience that is suited towards your specific project. Your first app will have "Customer", "Person", Address", "Order", whereas your second app may have objects like "Appointment", "Office", and "Holiday".
But what happens if you want to write some code that works on any entity? What if you won't know its name until runtime?
In addition to the strongly typed, code generated programming experience we expect customers to use, Lightswitch also offers a second programming experience, one that is late bound and weakly typed.
Suppose that like above, you have a table named Customer, with a string property named "LastName". The following code is the weakly typed equivalent to the above code:
private void Customer_Created() {
this.Details.Properties["LastName"].Value = "Smith";
}
Why might you want to use this?
Consider the case above: suppose I want to write an extension method that works on any entity. Say that I am writing a logging or auditing framework to include in an LS app. I want to make any entity in the system "describe" itself.
/// <summary>
/// Given any possible Lightswitch Entity, return a string of key:value pairs that describe its primary key columns and values
/// </summary>
/// <param name="entity">The entity instance</param>
/// <returns>an Id string</returns>
public static string GetKeyString(Microsoft.LightSwitch.IEntityObject entity)
{
string s = "";
object propVal = null;
foreach (Microsoft.LightSwitch.Model.IKeyPropertyDefinition kp in entity.Details.GetModel().KeyProperties)
{
if (!string.IsNullOrEmpty(s))
s += ", ";
s += kp.Name; s += ":";
propVal = entity.Details.Properties[kp.Name].Value;
if (null != propVal)
s += propVal.ToString();
else
s += "(null)";
IEntityPropertyDefinition nameProp = entity.Details.GetModel().Properties.SingleOrDefault(x => x.Name == "Name");
if (null != nameProp)
{
s += ", Name:"; s += entity.Details.Properties["Name"].Value;
}
}
return s;
}
There are a few things going on in this code.
1) we're using "IEntityObject". An IEntityObject can hold any LightSwitch entity. If a function asks for an IEntityObject, you can pass it a Customer, an Order -- whatever you like.
2) We're using the Model API off of the Details property. A lot of the tricky "goodies" in the Lightswitch programming experience are available off of the "Details" member of any object. The Model retreives the metadata of the object instance. This is how the object describes itself to the Lightswitch runtime.
3) We're using the weakly-typed property access, just like we did above in the "Customer" example. The difference is that we don't know the name of the property we want to access; we're asking the Lightswitch Model what the names of the properties are.
4) Note that the Model API knows the difference between a property and a Key property. This is how we can ask any entity what its primary key columns are. Once we know the appropriate property names, we can use the weakly typed property access to get their values as normal.
Try pasting this code somewhere in your own project. Send it different types of entities and convince yourself that it will work.
Challenge: Modify this code so that it returns the name of the type of entity, in addition to its key info. For instance, if I pass this routine a "Customer" instance, and Customer's Id column is named "Id", and the customer I pass in is customer id #7, I would expect the output to be:
"Customer, Id:7"
If you figure it out, wait a day or two before responding with an answer.
Are there other uses you can think of for this? Suppose that you have lots phone number properties on a few different entities in your LS app. Suppose that you want to do phone-number defaulting, for any phone number on any entity in the system. Imagine a routine called "SetPhoneNumbers", and that you called this routine from the _Created event for each of the entities in your system.
Here's what the logic might look like:
private static void SetPhoneNumber(IEntityObject e) {
foreach (var v in e.Details.GetModel().Properties.Where(x => x.PropertyType.Name == "PhoneNumber"))
{
e.Details.Properties[v.Name].Value = "(800)555-1212";
}
}
Some quick notes:
1) we're doing a LINQ query against the Properties collection in the Model API. We're asking LS to give us any property object who's type is "PhoneNumber".
2) we're using the C# "var" syntax to avoid caring about what the Lightswitch Property object actually is. You will get full intellisense if you type this code in manually. You won't have to worry about Ligthtswitch's Model.IEntityPropertyDefinition
I think it's pretty cool that in 3 lines of code, given any entity of any type from any LS project, we can set any discovered phone number fields on that entity to some default value. Or run any other logic we like.
I hope you've enjoyed this introduction to the weakly typed programming experience. So far, we've only discussed entities and entity properties. We also used the Model API to get the metadata about a runtime entity instance, like it's properties, key properties, and the data types of its properties. These simple techniques let developers write re-usable Lightswitch functionality.