LLBLGen Pro v2.5 has been released!
Back in January 2007 we started designing and developing LLBLGen Pro v2.5, and it's finally here!
When you develop a framework, at a given moment in time you'll wonder: "Ok, now that I have all the basics covered, which direction shall I go into now, what are the more meaningfull additions to the framework?", and we came to the conclusion that a more framework focussed approach was best, instead of adding more into UI oriented fluff like generating user interfaces (we still can do that though with a set of templates we developed, but it's not the core of the design). So, we added more entity management features, made the framework richer with a Dependency Injection mechanism and of course the usual set of new databases.
To enlist the full set of changes would be too much, so I'll give a brief overview of the main new things we added.
- Auditing support. We made auditing a first class citizen in the framework. The auditing support is setup in such a way that you can also use it as a journalling feature. In a lot of applications, developers need to add a facility which tracks which user changed/deleted/created/added etc. which data and when. If Auditing isn't part of a persistence framework, you have to add it manually to all possible situation when entity data might get changed in the database. This can be a cumbersome exercise because the data you audit perhaps has to be persisted in the same database, and if possible, in the same transaction. What makes it complicated is that you want this to be transparent: there shouldn't be chance that a developer forgets to call audit logic.
LLBLGen Pro uses a plug-in style system for Validator objects, Concurrency predicate factories and now also for Auditor objects and Authorizers (see below). An entity gets its Auditor plugged into itself by the framework, you of course are completely flexible which one (as it's your code which is plugged into the entity), and from then on the framework makes the calls to the auditor so it can record actions and which data was related to these actions. The framework also asks the auditor if it has anything to persist in the running transaction if that transaction is about to be committed. This way, everything is transparent: the developer simply focusses on the code necessary for the application, and the framework takes care of the rest. - Authorization support. Another feature which should be embedded deep inside the framework is authorization. As with auditing, authorization is something which isn't easy to add to a persistence framework from the outside: to intercept every possible action the persistence framework makes is already not a picknick, what's worse is that you want this too to be transparent: it has to work in situation A where you examine an entity in a service, but it also has to work when you bind a collection of entities to a grid for example.
Authorization can also be done in a variety of different ways: do you use windows authentication? Thread principals? ASP.NET role based security? Your own? So we made this flexible: you simply write a small authorizer class, which gets a call when some action takes place (e.g. reading a field, saving an entity, deleting an entity setting a field etc.), and the authorizer simply returns true or false, true for allow, false for cancel. How the authorizer does this, that's up to you. The framework however has deep knowledge of these authorizers. So every action which could be needing authorization, we made authorization aware so it calls into an existing authorizer, inside an entity. These authorizers get plugged into the entity once, and the developer has no headaches if authorization code is called or not.
This is something which leads to very simple code, and which at the same time can give great flexibility. For example, say you have a Customer entity which has a CCNumber field. Alice is allowed to see these numbers, but Bob, poor Bob, he's a temp hire and he isn't allowed to see CCNumber fields of Customer entities on screen. How would you develop this? Everywhere where Customer is used you probably would need to check who's accessing which property? Say we bind a list of Customer entities to a grid. The grid has a nice CC Number column. What we want is that when Alice uses the application, the CC Number column is filled, but when Bob uses the application, the column is simply blank. Also code which is ran by Bob which affects the CC Number shouldn't be having an effect on CC Number.
So we write a simple class:[DependencyInjectionInfo(typeof(CustomerEntity), "AuthorizerToUse", ContextType = DependencyInjectionContextType.Singleton)] public class CustomerAuthorizer:AuthorizerBase { public override bool CanGetFieldValue(IEntityCore entity, int fieldIndex) { // get the current thread's principal identity IIdentity id = Thread.CurrentPrincipal.Identity; // calling my own ACL service to obtain role rights for the current user. This can be // any system you want or you might want to use a completely different way of checking if a user // has a given set of rights. 'MyACLService' is a service you write yourself. RoleRightCollection roleRights = MyACLService.GetRoleRights(id); bool toReturn = true; switch((CustomerFieldIndex)fieldIndex) { case CustomerFieldIndex.CCNumber: // here I can check if the current user is allowed to access CCNumber. // I'll illustrate that with the role rights I received from the // imaginary MyACLService mentioned above // Alice is a manager, Bob isn't. toReturn = roleRights.Contains(Roles.Managers); break; } return toReturn; } }
You'll probably notice the attribute. We'll come to that in a second. The code is pretty straight forward. Alice, who is in the Manager role, is allowed to see CC Numbers, Bob the temp hire isn't. LLBLGen Pro will receive for Alice 'true' and for Bob 'false' and will act accordingly.
The code is straight forward, just fill in the blanks if needed, and set-and-forget: so always there. - Dependency Injection mechanism. We called it a 'mechanism' and not a full framework, because we didn't implement a full Dependency Injection (DI) framework like StructureMap or spring.net, we just added a mechanism to the framework which uses DI to set Validators, Concurrency predicate factories, Auditors and also Authorizers. The example given above is code which resides in your project, or in a separate assembly for example. The DI mechanism of the LLBLGen Pro framework injects instances of annotated classes into the types specified in the attribute. You can configure this in the application's configuration file as well, so you can drop an assembly with classes to inject into an application's bin folder at any time, or switch them later on if you for example need different auditing, or a different authorization scheme.
We more or less added it to make it easier for developers to use DI without having to convince management to embed yet another framework, and also simply because a lot of developers still haven't heard of what DI is nor how to use it. - Fast and compact serialization/deserialization. We enhanced the XML serialization/deserialization a lot, so exporting/importing entity graphs to/from XML is very fast now and the XML is very compact (as you would write it yourself with a single tiny state tracker element appended to the batch). The real killer is the binary serialization though. Binary serialization done by the .NET binary formatter is pretty slow, as the binary formatter class is pretty slow. The data blocks are also very large compared to what you'd expect from the actually data size. Simon Hewitt wrote a tremendous fast (think 300-400% faster and 13MB (binary formatter) vs. 1MB (fast serializer), that's compact!) binary serializer for LLBLGen Pro and we embedded his code in the core framework so all our customers can now enjoy this great enhancement. On top of that, you can easily add a packer class to it which packs the data even more so you get ultra small data blocks.
- Even better prefetch paths We made the prefetch path engine (eager loading) even smarter so it now knows more ways to optimize the queries it has to create. This leads to even faster graph fetching. Of course we also streamlined a lot of code under the hood to make data fetching much faster in general.
- Singularization and pluralization of names The LLBLGen Pro designer now singularizes and pluralizes names for entities, fields etc. based on a plug-in based system. This allows users to add a plug-in which can singularize and pluralize names in their own language. We ship a plug-in for English, which is based on Castle's Inflector class.
- Abbreviation support The LLBLGen Pro designer helps developers a lot by creating entities, mapping fields etc. As these actions are based on the meta-data it has retrieved from the schema(s) of the relational database, it's key that the names for the entities and fields are easily constructable from this meta-data, otherwise the developer has to rename a lot of fields afterwards which hurts productivity. For example, if you have a table with fields like Addr_id, CustNo, OrdrCnt etc., you actually want fields which are named like AddressId, CustomerNumber, OrderCount. The abbreviation support lets you specify a list of abbreviation - full word pairs which are then used to build the names for entities and fields, which leads to a great productivity gain.
- In-memory hierarchical filtering and projections. As Linq to Objects is still some time away and also because not everyone will move to .NET 3.5 right away, it's key that a persistence mechanism has proper in-memory filtering of entities and also has proper projection mechanisms so you can consume the entity data in different ways. We added a couple of new predicate classes which allow you to filter in-memory entity sets based on aggregate functions ran on related entities in memory, your own function etc. etc. so you can for example fetch a graph with customers and their orders and order detail entities and then in-memory create an entity view on the customer set to view only those customers which have orders with a total higher than $500, without a complex set of loops, copying entities into new collections etc.
- Excluding fields for fetching. If an entity contains a blob/image/text field which contains for example megabytes of data, it can be pretty expensive to fetch these entities including that field into memory if you just want to read some other fields of the entity. It also sometimes isn't necessary to fetch all fields, say you just want the CustomerId and CompanyName fetched for a set of customer entities. We already had the facility that you could formulate a subset and then use a projection fetch to project the resultset onto an entity class (or other class), however when you fetched a graph (e.g. Customer, Order, Order Details, Product, Employee) in a prefetch path (eager loading), it was tricky, for example if a node deep in the graph required to have several fields excluded. We added a feature called 'Excluded fields' which allows you to define a set of fields which should be excluded for the fetch (or which should be the only fields fetched and the rest should be excluded) for every fetch performed by the framework, including for nodes in a graph to fetch. If you then later on decide to fetch the excluded field or fields into the existing entity, you can do that with a special fetch method which efficiently will fetch the excluded fields and will merge them into the existing entity objects.
- Sybase ASA and ASE support, SqlServer CE Desktop support. As usual we added a bunch of databases to the set of supported databases. This time we added the Sybase family of databases (iAnywhere (ASA) and Adaptive Server Enterprise (ASE)) and also SqlServer CE Desktop to the list of databases which can be used with LLBLGen Pro.
Interested in the full list of changes? Please download the documentation from http://www.llblgen.com and check the 'What's new in LLBLGen Pro v2.5' page.
LLBLGen Pro v2.5 is a free upgrade for v2.0 customers.