What’s new in LLBLGen Pro v5.0 CTP 2
We’ve released the second CTP for LLBLGen Pro v5.0! Since the first CTP which was released back in March, we’ve been hard at work to implement features we wanted for v5.0. It’s taken a bit longer than expected as the main feature, Derived Models (more on that below), turned out to be a bigger feature than we initially thought and it affected more aspects of the designer than anticipated at first. Nevertheless, I’m very happy with the result, as it turned out even better than I imagined.
This CTP is the last one before beta and RTM. It’s open for all V4 licensees and comes with its own temporary license which expires on December 31st, 2015. Beta and RTM are expected in Q1 2016. To download, log into our website and go to ‘My Account->Downloads->4.2->Betas’.
So what’s new since CTP 1? I’ve described the highlights below. Of course the usual pack of bug fixes and improvements across the board are also included in this CTP.
Derived Models
Ever since v4 I wanted to do more with the designer, to do more with the entity model a user defines using it: we have all this model information contained in a single place and there should be more ways to leverage that than just for a mapping model to use with an ORM. The idea started when I looked into usage patterns again where ORM models are used. Nowadays most people define a different model in their application which is used to transport data from the ORM to the UI, e.g. across service boundaries or inside an MVC model class. This model is often hand-written, and tied to the entity classes using hand-written queries or Automapper mapping files. In other words: a hand-written chain of dependencies stored in multiple places which can break at any moment without noticing.
Another use case of models defined on top of an entity model is with document databases. Web applications nowadays aren’t just monolithic stacks with one database and one runtime, they’re built as a group of technologies working together in harmony: a UI on the client in JS, a backend targeting multiple databases using multiple paradigms, etc. It’s more and more common to store non-volatile data in de-normalized form inside a document database for faster querying, and update that data on a regular basis, at runtime. This data is e.g. retrieved from the relational database which is used with the ORM and entity classes.
For these two scenarios I’ve developed the Derived Models feature: Derived Models are models of hierarchical elements defined on top of the entity model. They define an optionally de-normalized form of the underlying abstract entity model. This all might sound complicated, but it’s actually very straightforward.
Let’s see a picture of the editor to get started. (click the picture below for a bigger version)
For the picture, I’ve picked a complicated scenario as it illustrates most of what you can do with the editor. On the far left of the designer you see the Project Explorer which is the overview of the elements in your project. The project loaded has an entity model with entities in various inheritance hierarchies, and a Derived Model called Docs. The Derived Model uses the target framework Document Database. We’ll get to that in a minute.
The editor shown in the screenshot shows the derived element BoardMember, which is derived from the entity BoardMember. On the left of the editor you see the entity hierarchy in tree form: you can navigate from the root entity BoardMember to related entities infinitely. On the right you’ll see the shape of the derived element BoardMember. When you check a checkbox on the left, it’s included in the shape on the right.
That’s of course not all. You can also de-normalize fields. In the example above, you’ll see in the shape of the derived element a de-normalized field, namely BoardMember.ManagesDepartmentName. This field is de-normalized from BoardMember.ManagesDepartment, as you can see on the left. De-normalizing is as simple as selecting the fields and clicking a button. The shape shown above just takes a couple of seconds to create, as the editor is smart enough to e.g. add all fields of a related element if you check the checkbox of a navigator. We of course built in tooling to get these derived elements added quickly to derived models and e.g. you can add fields in bulk and de-normalize fields in bulk.
The advantage of this is that if something changes in your entity model, the derived model changes too. Be it a type of a field that gets changed, a field that gets removed, a relationship that changes… the designer picks this up and keeps your Derived Models in sync.
You can define as many Derived Models as you want, each with different derived elements and target frameworks. They’re for all supported ORMs, so they work equally for Entity Framework, NHibernate, Linq to Sql and our own LLBLGen Pro Runtime Framework.
“Models…. I need code!”
Now that we have a Derived Model with elements that derive from our abstract entity model, we can do things with it, like generate code. There are two different code bases generated per Derived Model: a set of DTO / Document classes (which represent the derived elements in the Derived Model) and a set of persistence methods to fetch the DTO / Document classes using the ORM used in the Entity Model.
With v5.0 we’ll ship with two target frameworks to be used with a Derived Model: DTO class model and Document Database. The DTO class model offers (through presets) a choice between read-only DTOs and read/write DTOs. Read-only DTOs offers Linq projections for the DTO classes generated, the Read/write DTOs also offer projections back to the root entity of the derived element so it can be persisted into the relational database using the ORM of choice.
The Document Database framework offers a choice (through presets) between RavenDB, MongoDB and ‘Generic Document Database’. The last one can be used with e.g. RethinkDB and Microsoft’s DocumentDB. This means that you can define your models in the designer, then decide how you want to use your derived models (e.g. as DTOs or for a document database), generate code and use them with very little code.
Let’s look at an example of such a DTO / Document class. Here we have a Customer derived element which is derived from the Customer entity which has a value-typed field named ‘Address’. This is the typical Northwind Customer. As you can see the fields contain attributes. These attributes are added to the fields based on rules. E.g. you can define a rule (as I did here) where all string-typed fields will get a StringLength attribute with the length of the field. You define this once per model (so the rule is used for all string fields), and can override it at the field level if you want to. This requires very little work. The ID field here is CustomerID and it’s marked with the KeyAttribute automatically. We’ll add one line of code later on to tell RavenDB how to determine the key, and that’s it.
1: //------------------------------------------------------------------------------
2: // <auto-generated>This code was generated by LLBLGen Pro v5.0.</auto-generated>
3: //------------------------------------------------------------------------------
4: using System;
5: using System.ComponentModel;
6: using System.Runtime.Serialization;
7: using System.Xml.Serialization;
8: using System.Collections.Specialized;
9: using System.Collections.Generic;
10:
11: namespace Docs.DocumentClasses
12: {
13: /// <summary> Document class which is derived from the entity 'Customer'.</summary>
14: public class Customer
15: {
16: /// <summary>Gets or sets the CompanyName field. Derived from Entity Model Field 'Customer.CompanyName'</summary>
17: [StringLength(40)]
18: public System.String CompanyName { get; set; }
19: /// <summary>Gets or sets the ContactName field. Derived from Entity Model Field 'Customer.ContactName'</summary>
20: [StringLength(30)]
21: public System.String ContactName { get; set; }
22: /// <summary>Gets or sets the ContactTitle field. Derived from Entity Model Field 'Customer.ContactTitle'</summary>
23: [StringLength(30)]
24: public System.String ContactTitle { get; set; }
25: /// <summary>Gets or sets the CustomerId field. Derived from Entity Model Field 'Customer.CustomerId'</summary>
26: [StringLength(5)]
27: [System.ComponentModel.DataAnnotations.Key]
28: public System.String CustomerId { get; set; }
29: /// <summary>Gets or sets the VisitingAddress field. </summary>
30: public CustomerTypes.VisitingAddress VisitingAddress { get; set; }
31: }
32:
33: namespace CustomerTypes
34: {
35: /// <summary> Document class which is derived from the value-type 'AddressVt (VisitingAddress)'.</summary>
36: public class VisitingAddress
37: {
38: /// <summary>Gets or sets the Address field. Derived from Entity Model Field 'AddressVt.Address'</summary>
39: [StringLength(60)]
40: public System.String Address { get; set; }
41: /// <summary>Gets or sets the City field. Derived from Entity Model Field 'AddressVt.City'</summary>
42: [StringLength(15)]
43: public System.String City { get; set; }
44: /// <summary>Gets or sets the Country field. Derived from Entity Model Field 'AddressVt.Country'</summary>
45: [StringLength(15)]
46: public System.String Country { get; set; }
47: /// <summary>Gets or sets the RegionVtField field. </summary>
48: public VisitingAddressTypes.RegionVtField RegionVtField { get; set; }
49: }
50:
51: namespace VisitingAddressTypes
52: {
53: /// <summary> Document class which is derived from the value-type 'RegionVt (VisitingAddress.RegionVtField)'.</summary>
54: public class RegionVtField
55: {
56: /// <summary>Gets or sets the Region field. Derived from Entity Model Field 'RegionVt.Region'</summary>
57: [StringLength(15)]
58: public System.String Region { get; set; }
59: }
60: }
61: }
62:
63: }
Additionally to this class and its nested types, the designer also generates a projection extension method and, in the case of Read/Write DTOs, it will generate methods to project the DTO back to an entity. For this example I’ve chosen to store the data as documents in a RavenDB database so only the projection extension method is generated:
1: //------------------------------------------------------------------------------
2: // <auto-generated>This code was generated by LLBLGen Pro v5.0.</auto-generated>
3: //------------------------------------------------------------------------------
4: using System;
5: using System.Collections.Generic;
6: using System.Linq;
7: using System.Text;
8:
9: namespace Docs.Persistence
10: {
11: /// <summary>Static class for (extension) methods for fetching and projecting instances of Docs.DocumentClasses.Customer from the entity model.</summary>
12: public static partial class CustomerPersistence
13: {
14: /// <summary>Extension method which produces a projection to Docs.DocumentClasses.Customer which instances are projected from the
15: /// results of the specified baseQuery, which returns Northwind.Adapter.EntityClasses.CustomerEntity instances, the root entity of the derived element returned by this query.</summary>
16: /// <param name="baseQuery">The base query to project the derived element instances from.</param>
17: /// <returns>IQueryable to retrieve Docs.DocumentClasses.Customer instances</returns>
18: public static IQueryable<Docs.DocumentClasses.Customer> ProjectToCustomer(this IQueryable<Northwind.Adapter.EntityClasses.CustomerEntity> baseQuery)
19: {
20: return baseQuery.Select(p__0 => new Docs.DocumentClasses.Customer()
21: {
22: CompanyName = p__0.CompanyName,
23: ContactName = p__0.ContactName,
24: ContactTitle = p__0.ContactTitle,
25: CustomerId = p__0.CustomerId,
26: VisitingAddress = new Docs.DocumentClasses.CustomerTypes.VisitingAddress()
27: {
28: Address = p__0.VisitingAddressAddress,
29: City = p__0.VisitingAddressCity,
30: Country = p__0.VisitingAddressCountry,
31: RegionVtField = new Docs.DocumentClasses.CustomerTypes.VisitingAddressTypes.RegionVtField()
32: {
33: Region = p__0.VisitingAddressRegion,
34: },
35: },
36: });
37: }
38: }
39: }
With these two classes I can now fetch Customer instances using any Linq query which produces Customer entities. The classes are set-up to be usable with RavenDB too, so I can just go ahead and fetch some entities and store them as Customer instances in RavenDB: This is the only code I have to write, the code above is generated, you don’t have to write that.
1: // Setup the DB connection to RavenDB.
2: _store = new DocumentStore() { Url = "http://localhost:8080/", DefaultDatabase = "Northwind" };
3: _store.Initialize();
4: _store.Conventions.FindIdentityProperty = p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute);
5:
6: //....
7:
8: // then fetch some customers and store them.
9: using(var adapter = new DataAccessAdapter())
10: {
11: var metaData = new LinqMetaData(adapter);
12: var toInsert = metaData.Customer.Where(c=>c.Country=="USA").ProjectToCustomer().ToList();
13: using(var session = _store.OpenSession())
14: {
15: foreach(var d in toInsert)
16: {
17: session.Store(d);
18: }
19: session.SaveChanges();
20: }
21: }
This works with Entity Framework, NHibernate and Linq to Sql as well: the only thing that changes is the way the query is fetched: the projection method, it’s the same. For the DTO class model the projection methods are the same as well. And as the Projection method returns an IQueryable, you can append other elements too, like ordering or additional where clauses, Distinct etc.
The shown derived model class and query above is of course rather simple, yet it illustrates the important aspects: the ties between your derived models and your entity model aren’t obscurely defined in several files without any connection but in the central place where you also define your entity model and more importantly: when the entity model changes the derived model will change with it, together with the projection methods.
Of course, the normal things like copy/paste are also supported for Derived Models. For instance if you copy a Derived Model from a project in one designer instance to an empty project in another designer instance, you’ll get the entities the model is derived from, and optionally the mappings and target table definitions brought in as well. All that with just two keystrokes.
So what else is new?
Preset editor/viewer
The LLBLGen Pro code generator is actually a hierarchical task executor where the tasks happen to be individual code generation tasks. Which tasks to execute and with which parameters is defined using Presets. The user picks a preset and executes the set of tasks defined by the preset with a click of a button. Sometimes you want to alter the parameters, or add an additional task to the set of tasks. In previous iterations we had a UI interface with complex controls to edit the tree, which was time consuming to use. It turned out if you write the preset in the XML editor (a preset is defined in an XML file) using some intellisense it was much faster. So we created a new preset viewer which allows the user to view and copy it, start edit operations etc. on presets and pair that with the already available text editor in the designer:
We used this system to write the Derived Element presets and templates and it felt much better than the previous system. ‘Dog-fooding’ it ourselves was a great way to see whether we were on the right track so our users won’t run into snags.
Code generation configuration overhaul
In previous versions when you started a code generation action you’d generate only code for the entity model. Now that we had multiple models inside the designer, we needed a new way to define what to generate code for. So we did:
The new system allows the user to select for which models to generate code and configure them individually.
Wrapping up…
There are more smaller things added and changed, like a new interface for the command line code generator for batch processes, and we’ll add more smaller features before beta/RTM. I hope you like what we have added and it meets what you’re looking for!