Available Presets per Framework

Data Transfer Object (DTO) class model

This framework will create:

  • DTO Class model, one file per Root Derived Element.
  • IQueryable based projection code which fetches data using the Entity Model's target framework into DTO classes.
  • Optionally, classes which support projection back from DTO to entity classes to persist changes back to the database using the entity model target framework.

This framework is supported on .NET v4.0 and higher and Netstandard 1.6 and higher.

Netstandard 1.6 support and missing attribute definitions

.NETstandard 1.6 is supported as a target platform, and generating code for that platform will generate a csproj file which is compatible with the .NETStandard tooling in Visual Studio 2017. There's one problem that needs a workaround on .NETStandard 1.6: missing attribute definitions.

Some default attributes in the DTO framework are not supported on .NET standard 1.6. One of them is the SerializationAttribute. To avoid compile errors, there are two workarounds:

  1. Go to Project -> Settings and then navigate to the DTO model in the settings tree-> Attributes. Click the attributes which are a problem and press 'DEL' to delete them.
  2. Use a polyfil in an additional class added to the DTO codebase:
#if NETSTANDARD1_6
namespace System.Runtime.Serialization
{
    public class SerializableAttribute : Attribute
    {
    }
}
#endif

Presets Defined

The following presets are defined.

SD.DTOClasses.ReadOnlyDTOs

This preset generates two VS.NET projects: (RootNamespace is the name you specify as root namespace in the code generation configuration dialog)

  1. A project called RootNamespace, generated in the folder RootNamespace, which contains a namespace RootNamespace.DtoClasses, which contains DTO classes, using one file per root derived element and all its embedded derived elements. The VS.NET project is generated in this folder as well.
  2. A project called RootNamespace.Persistence, generated in the folder RootNamespace.Persistence, which contains per root document a static class called RootDerivedElementNamePersistence with static Projection methods to create a projection from an entity query to the Root Derived Element instance, including nested elements. The VS.NET project is generated in this folder as well.

See Persistence Methods for details below.

SD.DTOClasses.ReadWriteDTOs

This preset is equal to SD.DTOClasses.ReadOnlyDTOs, but now it also generates static methods in the RootDerivedElementNamePersistence class to create a PK filter based on the DTO instance to fetch the original root entity from the database, create PK filters and to update the originating entity the Root Derived Element was instantiated from. These write methods are only generated if the RootDerivedElement has an ID field. See for an example how to use the methods, below.

Info

Only the root entity of the Root Derived Element is updated.

Example

Example how to use the extra write methods. This example uses LLBLGen Pro, but is can be used as well for the other ORM frameworks the LLBLGen Pro Designer supports

// dto's are created, using LLBLGenPro
return linqMetaData.Customer.Where(c => c.VisitingAddressCountry == "USA")
               .ProjectToCustomerOrder().ToList();

// dto's are altered and a new one is added to the list and then send back.
// coming back to API, now we have to fetch the entities, update them and save them back in 
// one go. The method below fetches an entity per dto, which is less efficient.
using(var adapter = new DataAccessAdapter())
{
    var metaData = new LinqMetaData(adapter);
    var uow = new UnitOfWork2();
    foreach(var dto in dtos)
    {
        // fetch the entity back from the database
        var e = metaData.Customer
                            .FirstOrDefault(CustomerOrderPersistence.CreatePkPredicate(dto));
        if(e == null)
        {
            // new entity
            e = new CustomerEntity();
        }
        // update e with values of dto
        e.UpdateFromCustomerOrder(dto);

        // log for persistence
        uow.AddForSave(e);
    }
    uow.Commit(adapter);
}

Alternatively, to fetch the original entities in one go, use the following approach:

// dto's are created and send to the caller.
return linqMetaData.Customer.Where(c => c.VisitingAddressCountry == "USA")
                .ProjectToCustomerOrder().ToList();
// coming back to API, now we have to fetch the entities, update them and save them back 
// in one go.
using(var adapter = new DataAccessAdapter())
{
    var metaData = new LinqMetaData(adapter);
    // fetch the entities back in one go.
    var entities = metaData.Customer.Where(CustomerOrderPersistence
                       .CreatePkPredicate(dtos)).ToList();
    var uow = new UnitOfWork2();
    foreach(var dto in dtos)
    {
        var e = entities
                    .FirstOrDefault(CustomerOrderPersistence.CreateInMemoryPkPredicate(dto));
        if(e == null)
        {
            // new entity
            e = new CustomerEntity();
        }
        // update e with values of dto
        e.UpdateFromCustomerOrder(dto);

        // log for persistence
        uow.AddForSave(e);
    }
    uow.Commit(adapter);
}

Document Database

The Document Database framework is meant to be used with a document database, where data obtained from the RDBMS, through the supported ORM framework is stored in denormalized form in a document database. This way the document database model can be used as a read-only form of the data in the RDBMS and can be kept in-sync.

Additionally, you can use this framework without the entity model by discarding the code generated for the entity model and solely use the entity model as an abstract entity model and theoretical basis for the derived model and its classes. In this case you only use the generated DTO classes and the optional document database setup/usage code to work with the document database client.

This framework will create:

  • DTO Class model, one file per Root Derived Element.
  • IQueryable based projection code which fetches data using the Entity Model's target framework into DTO classes.
  • For the Document database chosen (through the preset), optionally, a set of classes for setting up/using the Document database's client.

This framework is supported on .NET 4.0 or higher, but it depends on the client of the target database whether the target document database presets are available for .NET 4.0 or that .NET 4.5 or higher has to be chosen.

Presets defined

All presets for Document Database generate the following two VS.NET projects: (RootNamespace is the name you specify as root namespace in the code generation configuration dialog)

  1. A project called RootNamespace, generated in the folder RootNamespace, which contains a namespace RootNamespace.DocumentClasses, which contains read-wite document classes, using one file per root document and all its embedded derived elements. The VS.NET project is generated in this folder as well.
  2. A project called RootNamespace.Persistence, generated in the folder RootNamespace.Persistence, which contains per root derived element a static class called RootDerivedElementNamePersistence with static Projection methods to create a projection from an entity query to the Root Derived Element instance, including nested elements.The VS.NET project is generated in this folder as well.

See Persistence Methods for details below. Per supported document database more code can be generated or be required. This is detailed below per database.

Tip

If you don't want to use the generated entity model code, but solely the document database code, simply disable the entity model code generation task when generating code in the designer. The RootNamespace.Persistence VS.NET project will then be empty and if applicable, only contain the code to work with the document database.

RavenDB

RavenDB is supported using the preset SD.RavenDB. It generates no extra classes specifically for RavenDB.

It generates a KeyAttribute on each ID field in the Root derived element class. This attribute is defined in the System.ComponentModel.DataAnnotations namespace of .NET. Additionally, the DocumentStore instance has to be told how to obtain the ID fields. This is done this way:

// .NET 4.0
documentStoreInstance.Conventions.FindIdentityProperty = 
                        p=>p.GetCustomAttributes(true).Any(a=>a is KeyAttribute);

// .NET 4.5 or higher
documentStoreInstance.Conventions.FindIdentityProperty = 
                        p=>p.GetCustomAttribute(typeof(KeyAttribute))!=null;

Example:

_store = new DocumentStore() {Url = "http://localhost:8080/", DefaultDatabase = "Northwind"};
_store.Initialize();
_store.Conventions.FindIdentityProperty = 
                        p => p.GetCustomAttributes(true).Any(a => a is KeyAttribute);

MongoDB

MongoDB is supported using the preset SD.MongoDB. This preset relies on the official MongoDB C# driver (we use v2.x syntax, not v1.x). MongoDB's driver uses Bson class mappings. LLBLGen Pro generates these mappings for all derived element classes, including inheritance definitions in the class BsonClassMappings which is in the generated RootNameSpace.Persistence project. If the entity model isn't generated the RootNameSpace.Persistence project still is, which will then contain just the BsonClassMappings file.

To tell MongoDB the class mappings, you have to execute one line of code at the start of the program:

BsonClassMappings.RegisterClassMappings();

This will register the class mappings with the .NET mongo driver. The BsonClassMappings class requires a reference to the MongoDB.Driver package from Nuget, which means this reference has to be added to the generated RootNamespace.Persistence VS.NET project.

Generic Document Databases, RethinkDB / Microsoft DocumentDB

LLBLGen Pro offers generic Document Database support through the SD.GenericDocDB preset. This preset is e.g. usable for RethinkDB, Microsoft DocumentDB and others, and generates a JsonProperty("id") attribute on the ID field of a root derived element class, and other than that generates no additional code.

This will result in:

[Newtonsoft.Json.JsonProperty("id")]
public string MyID {get; set;}

If the ID field is nullable, "NullValueHandling=NullValueHandling.Ignore" is specified in the JsonProperty attribute:

[Newtonsoft.Json.JsonProperty("id", NullValueHandling=NullValueHandling.Ignore)]
public int? MyID {get; set;}

The RootNamespace VS.NET project requires a reference to Newtonsoft.Json, which has to be added manually through Nuget.

For RethinkDB, users have to use the Newtonsoft approach to serializing documents.

Inheritance support

There's currently no inheritance support in the RethinkDB driver for .NET, and this means that persisting documents using inheritance will succeed but deserialization won't work: the data isn't deserialized in subtypes, as there's no type information available in the Json.

For Microsoft DocumentDB, inheritance support is provided by an external library: https://github.com/markrexwinkel/azure-docdb-linq-extension/blob/master/src/DemoApp/Program.cs

Persistence methods

Per derived model definition a separate VS.NET project is generated, named RootNamespace.Persistence. Per Root Derived Element, a class file is generated in this VS.NET project, called RootDerivedElementNamePersistence.

This class contains the following methods:

  • An extension method, named ProjectToRootDerivedElementName(this IQueryable baseQuery), where RootDerivedElementName is the name of the derived element and the suffix specified in the project setting DerivedElementClassNameSuffix (located in the LLBLGen Pro Designer Project settings: Derived Model -> Code Generation -> General). This method is used to project an IQueryable<entity> to an IQueryable<dto>.
  • An extension method named ProjectToRootDerivedElementName(this EntityType entity), where RootDerivedElementName is the name of the derived element and the suffix specified in the project setting DerivedElementClassNameSuffix. This method is used to project an in-memory entity instance to a dto/doc class instance.
  • A method CreatePkPredicate(dto). This method creates an Expression<Func<>> which is usable in a Where clause to fetch the original entity the dto was projected from. Method is present only in the SD.DTOClasses.ReadWriteDTOs preset and only if the Root Derived Element has an Id field.
  • A method CreatePkPredicate(IEnumerable). Similar to CreatePkPredicate(dto) but this time it produces a predicate to fetch all entities from which the specified dto's are projected. Method is present only in the SD.DTOClasses.ReadWriteDTOs preset and only if the Root Derived Element has an Id field.
  • A method UpdateFromRootDerivedElementName. This method is an extension method on the root entity of the RootDerivedElementName derived element and updates the fields of the entity instance with the values in the dto specified. It sets the Pk field in the entity if it's not marked as readonly. Method is present only in the SD.DTOClasses.ReadWriteDTOs preset and only if the Root Derived Element has an Id field. This method can be used both for updating an existing entity with the values of a DTO, but also for creating a new entity: a new entity class instance can be set with the values from the DTO using the same method.