Archive for December, 2009
Composite keys in LightSpeed 3
Tagged as LightSpeedAs part of our programme to improve LightSpeed’s compatibility with legacy databases, we’ve added composite key support as part of version 3. However, our implementation of composite keys is a little different from the way that LINQ to SQL or Entity Framework do it, so I thought it was worth saying a little more about the way it works in LightSpeed.
Representing a composite key
In LightSpeed, every entity has an Id. That’s “an” Id, not “one or more.” So if you want to represent a composite key, you have to represent it as a single value, even though it may have several parts. The way to do this in .NET is to create a value type — think of the Point structure which includes X and Y properties — which you do in C# by declaring a struct and in Visual Basic by declaring a Structure. As the composite key is, well, a key, the value type has to be immutable.
Let’s look at an example. Suppose we’re modelling products that are built for international markets. Our database contains a Product table, but it has a composite key consisting of an integer ProductTypeId and a string CultureId. For example, if widgets are product type 1, then widgets destined for the French Canadian market would have a ProductTypeId of 1 and a CultureId of fr-CA. To model this in LightSpeed, we’ll need a value type representing these two elements:
public struct ProductId { private readonly int _productTypeId; private readonly string _cultureId; public ProductId(int productTypeId, string cultureId) { _productTypeId = productTypeId; _cultureId = cultureId; } public int ProductTypeId { get { return _productTypeId; } } public string CultureId { get { return _cultureId; } } }
Now we just use this value type as the Id type of our entity:
public class Product : Entity<ProductId> { // ... }
Rounding out the ID type
If you build the above, you’ll notice a few compiler warnings. C# wants you to implement the Equals and GetHashCode methods for the ProductId value type. This isn’t 100% necessary, but it’s good style and improves efficiency. The canonical implementations for a value type generally follow a pattern like the following:
public struct ProductId { public override bool Equals(object obj) { if (obj is ProductId) { ProductId other = (ProductId)obj; return other.ProductTypeId == ProductTypeId && StringUtils.EqualsIgnoreCase(other.CultureId, CultureId); } else { return false; } } public override int GetHashCode() { return 29 * ProductTypeId.GetHashCode() + (CultureId == null ? 0 : CultureId.GetHashCode()); } }
It’s also a good idea to override ToString(), so that when you’re debugging and you’re trying to identify the entity in a DataTip or the Watch window, you’ll see a readable ID value like {2/fr-CA} rather than just the type name.
Assigning composite IDs
One of the tricky bits to working with composite keys is that LightSpeed likes to be in charge of assigning IDs. But in a composite key scenario, that’s not possible. Composite keys typically have business meaning: in our case the ProductTypeId refers to a product type which presumably exists elsewhere, and the CultureId is also meaningful. Even if this weren’t the case, LightSpeed doesn’t have an IdentityMethod that could assign IDs of your custom type.
To get around this, you have to override the Entity.GeneratedId() method on your entity type, and have that return an instance of the composite key type. The typical pattern is to store a “pending ID” in a transient field and return that when LightSpeed decides it’s time to assign an ID. Here’s an example:
public class Product : Entity<ProductId> { [Transient] private ProductId _pendingId; protected override object GeneratedId() { return _pendingId; } }
It’s very important that the holding field be transient! Otherwise LightSpeed will try to store it in a PendingId, which will cause errors firstly because the ProductId value can’t be translated to a single database value and secondly because the Product table doesn’t have a PendingId column!
Finally, you’ll need to provide a way for the code that creates Products to specify the ID of a newly created Product. You can do this via a constructor overload, but if you do this, don’t forget that you must also provide a default constructor for LightSpeed to use when materialising Products from the database. Alternatively, you can use a factory method to distinguish the specific case of creating a new Product record from the construction of an in-memory Product instance:
public class Product : Entity<ProductId> { public static Product CreateNew(int productTypeId, string culture) { Product product = new Product(); product._pendingId = new ProductId(productTypeId, cultureId); return product; } }
Note you only need to use the ID providing method when creating a Product for insertion. At all other times, LightSpeed creates Product instances for you from the database records, and sets up their IDs automatically. You might want to deprecate the default constructor so that people get compiler warnings if they try to create Product instances themselves instead of using the overloaded constructor or the factory method.
We’re done
This is sufficient to define a composite keyed entity in LightSpeed. We’ll be putting out a couple more posts over the holiday period showing how to define queries and associations involving the composite key. Because, gosh darn it, if there’s anything more festive than a composite key, then I sure as heck don’t want to know about it.
Moving from LightSpeed 2 to LightSpeed 3
Tagged as LightSpeedLightSpeed 3 has a high degree of compatibility with LightSpeed 2: all existing LightSpeed 2 code should run unchanged against LightSpeed 3. However, there are a few minor changes which LightSpeed 3 users should be aware of.
.NET 3.5 SP1 dependency
LightSpeed now requires .NET 3.5 SP1, though we do still include a build that is compatible with .NET 2.0.
Obsolete APIs
The following APIs have been obsoleted. They are still available and still work in LightSpeed 3, but will raise a compiler warning, and will be removed in a future version.
- LightSpeedContext.Default and LightSpeedContext.BeginUnitOfWork. These were provided to ease migration from LightSpeed 1.x to LightSpeed 2, and are being removed because they don’t work well with LINQ and because many users have found them confusing. (Heck, even we’ve found them confusing.) Use the LightSpeedContext(string configurationName) constructor instead of LightSpeedContext.Default, and the LightSpeedContext.CreateUnitOfWork() instance method instead of LightSpeedContext.BeginUnitOfWork.
- IUnitOfWork.FindOne(object id). This has been renamed to FindById. This is because it was too easy to mistakenly call the FindOne(object) overload when one was intending to call FindOne(Query) or FindOne(QueryExpression).
Logging object
The logging interface, ILogger.LogSql, is declared to receive an object. In LightSpeed 2.x, the object was always a StringBuilder or a string. In LightSpeed 3, the object may also be an instance of the new CommandLog class. The reason for this is to provide structured logging information in case you want to display it in a custom format.
Existing code which calls ToString() or performs a cast to string will not be affected because CommandLog returns the same text as before in these cases. However, code which specifically assumes a StringBuilder will need to be updated.
XML documentation for your LightSpeed models
Tagged as LightSpeedIf you write your LightSpeed entities by hand, it’s easy to write XML documentation, so that users of your classes get helpful tooltips on the properties, methods and parameters. In LightSpeed 3, you can now create XML documentation in the designer.
To show or edit the XML documentation, just right-click anywhere in your model and choose Documentation. LightSpeed displays a Documentation window:
The Documentation window allows you to conveniently edit common documentation elements such as the Summary and Remarks. If you want to add less common documentation tags, such as <exception> or <example> elements, you can do so via the Additional box, but in this case you have to include the XML tags themselves:
As you move around the model, the Documentation window tracks the current selection, just like the Properties window does.
XML documentation isn’t yet available for all model elements, but it’s there for the most common ones: entities, entity properties, associations and stored procedures. If you’ve got a requirement for documentation on something that doesn’t support it yet, drop us a note in the forums and we’ll get it sorted for you.
Want to take it for a spin? Download the free Express edition today!
Getting started with LightSpeed migrations
Tagged as LightSpeedOne of the big new features of LightSpeed 3 is migrations support. We’ll be providing more detailed how-tos on this over the next few weeks, but I thought it would be useful to kick off with a quick overview of the what and how of LightSpeed migrations, starting with the question on everybody’s lips…
Just what are migrations anyway?
Let’s suppose you have an application, and it uses a database. If you’re wise, you’ll have a SQL script for setting up that database, probably consisting of a truckload of CREATE TABLE statements and maybe a few INSERTs for reference data. By running these scripts, you can easily and reliably create a new database for your application to run on.
Now let’s suppose you upgrade the application, and the upgrade requires you to store some new data in the database. Maybe a new field (column) for one kind of entity, and a couple of new kinds of entities (tables). You can update your create script, no problem… but what about existing databases?
Well, okay, instead of updating your create script, you can create an update script, which does only the new bits (an ALTER TABLE and a couple of CREATE TABLEs). For new databases, you’ll run the create script followed by the update script; for existing databases, you’ll just run the update script. No worries.
But then it happens again! (Thanks a bunch, Marketing.) Sure, you can create another update script. But now for new databases, you have to run the create script and two update scripts, but for existing databases, you might have to run both update scripts or just the second, depending on whether the original update script had already been applied. So now you need to detect what level an existing database is at before you run your scripts. Well, maybe you can do that now. But what about after the fifth upgrade? The tenth? And what if someone doesn’t want to go straight to your latest and greatest — they want the same version you installed in their Ruritanian office three years ago.
That’s where migrations come in. A migrations framework automates tracking and applying database version changes — typically schema changes but could also include data changes such as if a new column needs to be defaulted or new reference data needs to be added. You write each migration step — the create and update scripts in the story above — and the migration framework figures out which steps need to be applied in any given situation, and applies them. For example, if you tell the migration framework to upgrade the Podunk office to version 9, which is what they’ve got in the Ruritanian office, the migrations framework can figure out that Zimiamvia is currenly on version 6, and apply only steps 7, 8 and 9.
How do I write migration scripts?
You might think the obvious way to write your migration steps would be as the SQL statements to be executed for each step — the CREATE TABLE and ALTER TABLE statements mentioned above. This can work okay but is fairly labour-intensive, and gets plain vexing when multiple databases enter the picture — for example, if some of your customers use an Oracle back-end, but others use SQL Server or MySQL. Many migration frameworks, including LightSpeed’s, therefore get you to write your migration scripts using a higher-level API; the migration framework will then generate the low-level SQL scripts when you run a migration.
So in LightSpeed, the way to write a migration (by which I mean a single version-to-version step) is to create a class in C# or Visual Basic which derives from the Migration base class:
public class AddProductColumns : Migration { }
This migration doesn’t do anything yet. The actual migration logic goes in your override of the Up() method:
public override void Up() { AddColumn("Products", "Name", ModelDataType.String); AddColumn("Products", "Description", ModelDataType.String); }
Now when you run this migration, it’ll add two columns, Name and Description, to the Products table, of data type NVARCHAR(MAX) or VARCHAR2(MAX) or however your database likes to spell Big Honking String.
This sounds dangerously like work. Can’t you write the code for me?
If you’re using the LightSpeed designer, you can get it to create migrations for you. To do this, you first need to associate a migrations project with your model. This tells the designer where to create the migration class files. A migrations project is just an ordinary C# or Visual Basic class library project (it does have to be in the same solution as the project containing your model though). To associate a migrations project with a model, open the model and choose Set Migrations Project from the Migrations menu.
Once you’ve done this, you can choose Create Migration from the Migrations menu, and LightSpeed will compare your model with the previous version (the last one you created a migration for), and suggest the steps that should go in the migration. Uncheck anything that you don’t want, give the migration a name, and LightSpeed will create the migration class and implementation for you.
You’re not restricted by what the designer gives you, though. Once the migration is created, it’s just C# or Visual Basic source code, and you can edit it to your heart’s content.
How do I run migrations?
There are three ways of running migrations: from the command line, from your own programs and from the LightSpeed designer. We’ll talk about the first two options in a future post: for today, I just want to describe how to run migrations from the designer.
Again, open your model and go to the Migrations menu. Choose Run Migrations to display the migrations window.
There are a number of things you can do in this window, but I want to concentrate on a specific workflow, of applying migrations to a connected database. You can connect to a database, or choose another workflow such as generating SQL scripts for a DBA to run, via the Change button. Once you’re connected, LightSpeed will show you the current database version (if any). Click on the desired target version and choose Apply Now. And that’s it. LightSpeed figures out which migrations to apply, and applies the changes specified in your migration code for each migration that’s needed.
This has been a whistlestop tour of migration concepts and practice and we’ll have a lot more to say about this over time. If it’s been enough to whet your appetite, why not download the free Express edition and give it a go!
Improved support options for customers
Tagged as LightSpeed, MegaPack, Products, SimpleDB Management Tools, VS File Explorer, WPF Diagramming, WPF Elements, WPF Property GridHand in hand with our recent volume discounts and LightSpeed 3.0 release we have started to put in place an improved support capability for customers who need priority support options or may occasionally want to escalate support beyond what we normally offer.
Tooting our own horn a little, we have had very positive feedback from many customers who have said our support is fantastic. We hope this addition helps us maintain end user happiness as we continue to grow and our support load scales up.
What is priority support?
Priority support is the option to ensure that your request is dealt with promptly, and is reviewed before and prioritised over normal forum posts. It is also useful for folks who wish to not use the forum which is quite open and may not be viable for talking about sensitive parts of a software system.
Can I use it for feature requests?
No, we still urge that feature requests be posted in the forums so that others may discuss them (and because we don’t think it’s fair to charge you for suggesting cool features!). Features are still only added to products at our discretion.
How much does priority support cost?
We have priced support at $199 USD per priority support issue OR at $499 for a 5 pack of support issues, a 50% saving. You will be able to see if you have support tickets in your store account page. We have been issuing 1 priority support ticket per customer to LightSpeed 3.0 customers and upgrading Enterprise Edition customers. For other products, we’re in the process of migrating Enterprise customers to have a priority ticket added to their account.
The pricing is designed to be very cost effective and is inexpensive compared to the support options offered for other software (for example, one commercial NHibernate support vendor quotes 600 Euro, approximately $870 USD, per incident for ad hoc support).
You can purchase Priority support tickets in our online store.
Do I need it? Should I put all my requests through priority support?
If you need a guaranteed response, or a quick response is crucial to you, or you don’t want to discuss your issue on the public forum, then you should put a request through priority support. The forum will still get monitored actively by Mindscape staff and questions will be answered, but there are no guarantees around response times (our response times have usually been pretty good, but never guaranteed!).
Priority support is for those times when you need additional help and you don’t want to rely on the forums. You are welcome to simply buy a support ticket as you need them for $199 USD however it may be wiser to simply purchase a 5 pack in order to save 50%.
If I’ve missed any questions you have then please post a comment – I’m happy to answer them :-)
![]()
BrainDump (1)
Community Code (1)
Events (7)
General (34)
Lab Samples (2)
LightSpeed (144)
MegaPack (3)
News (52)
NHibernate Designer (4)
Products (68)
Projects (4)
Screencast (6)
SharePoint (2)
Silverlight (7)
Silverlight Elements (14)
SimpleDB Management Tools (12)
Visual Studio (4)
VS File Explorer (6)
WPF (34)
WPF Diagramming (17)
WPF Elements (30)
WPF Property Grid (26)
![]()
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
December 2007
November 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007





Posted by Ivan Towlson on 22 December 2009






