Recommended way to use LLBLGen in ASP.NET Core Web Application

Posts   
 
    
Posts: 37
Joined: 09-Nov-2016
# Posted on: 26-Feb-2018 15:50:15   

Hi,

I am using LLBLGen 5.3 and LLBLGen Runtime Pro Framework in an ASP.NET Core Web Application (WebAPI).

When I retrieve an Entity, I get a CircularReference error. This is not totally unexpected as I know LLBLGen does not serialize to JSON and I have previously worked around the issues like this (https://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=24155).

I am serializing my entities to JSON in another project (A .NET Core Console Application) and have solved it by creating a DTO Class Model from a Derived Model.

My question is, would it make sense to build the Api around DTO Class Models instead of trying to solve the problems with serializing the "regular" LLBLGen entities?

Based on the documentation (https://www.llblgen.com/Documentation/5.3/Derived%20Models/dto_llblgen.htm) it seems like a viable approach (both for getting and updating data). Is it also the recommended approach?

Best regards, Andreas

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 26-Feb-2018 21:58:41   

My question is, would it make sense to build the Api around DTO Class Models instead of trying to solve the problems with serializing the "regular" LLBLGen entities?

Yes, I believe DTOs come handy here, as you build a customized trimmed data objects to be sent over the wire, which only include the data you need per call.

Posts: 37
Joined: 09-Nov-2016
# Posted on: 27-Feb-2018 09:21:09   

Hi Walaa,

Thank you for your response.

We are using some inheritance, meaning that most of our models inherit from some base class, for example Person1Actor and Person2Actor both inherit from Actor.

Therefore I am a bit worried about this sentence in the documentation:

Inheritance and derived elements is limited. Due to the fact the Linq query projection is run on the raw data coming from the database, it's not possible to determine at runtime which derived element subtype to materialize: no supported ORM framework supports this. The downside is that for m:1/1:1 related embedded derived elements, subtypes aren't materialized.

I am not sure I understand this, or if it is related to us. Is there an example somewhere?

Other than the above, are there any downsides to using DTO's? The documentation is great at explaining how I work with the DTO's but not very clear on the use-cases (or I might have missed that part). It seems like the DTO's are a "light-weight" version of the regular entities which I assume comes with some limitations (if not, why even have the regular entities)?

Best regards Andreas

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 27-Feb-2018 12:32:33   

Jordbaerhjelmen wrote:

Hi Walaa,

Thank you for your response.

We are using some inheritance, meaning that most of our models inherit from some base class, for example Person1Actor and Person2Actor both inherit from Actor.

Therefore I am a bit worried about this sentence in the documentation:

Inheritance and derived elements is limited. Due to the fact the Linq query projection is run on the raw data coming from the database, it's not possible to determine at runtime which derived element subtype to materialize: no supported ORM framework supports this. The downside is that for m:1/1:1 related embedded derived elements, subtypes aren't materialized.

I am not sure I understand this, or if it is related to us. Is there an example somewhere?

These problems are related to nested elements inside the DTOs. If you want to return a DTO derived from Person1Actor and it has no nested elements which are derived from subtypes, you're fine.

example: BoardMember has m:1 relationship with CompanyCar, which has two subtypes: FamilyCar and SportsCar. Creating a derived element on BoardMember including the CompanyCar and its two subtypes is perfectly fine when using a DocDB and no linq queries. Problems arise when a BoardMember instance has to be fetched and its related CompanyCar instance (which can be a FamilyCar or a SportsCar). It's not possible to make this distinction at runtime. This means the projection will only contain the data of CompanyCar and never FamilyCar/SportsCar instances. See the generated code below: (here, no suffix was specified)


public static IQueryable<DTOs.DtoClasses.BoardMember> ProjectToBoardMember(this IQueryable<BoardMemberEntity> baseQuery)
{
    return baseQuery.Select(p__0 => new DTOs.DtoClasses.BoardMember()
    {
        CompanyCar = new DTOs.DtoClasses.BoardMemberTypes.CompanyCar()
        {
            Brand = p__0.CompanyCar.Brand,
            CarId = p__0.CompanyCar.CarId,
        },
        EmployeeId = p__0.EmployeeId, 
        // ...

The projection here can only create new CompanyCar derived element class instances, but not FamilyCar or SportsCar instances.

Another example of inheritance problems: Department has a 1:n relationship with Employee. Employee has subtypes Manager and Clerk and Manager has as subtype Boardmember. To fetch the related embedded documents derived from the Employee hierarchy in the Department derived root document is problematic: each type has to be projected to its own embedded document type, e.g. with its own further projections and queries. Below is an example of how this is done, which works only on LLBLGen Pro: all other linq providers fail.


Employees = p__0.WorksForDepartment.Employees.OfType<InheritanceOneReverseEngineering.EntityClasses.Employee>()
    .Where(p__1 => !(p__1 is InheritanceOneReverseEngineering.EntityClasses.Manager) &&
                    !(p__1 is InheritanceOneReverseEngineering.EntityClasses.Clerk) &&
                    !(p__1 is InheritanceOneReverseEngineering.EntityClasses.BoardMember))
    .Select(p__1 => new DTOs.DtoClasses.BoardMemberDTOTypes.WorksForDepartmentDTOTypes.EmployeeDTO()
    {
        Name = p__1.Name,
    })
    .Union(p__0.WorksForDepartment.Employees.OfType<InheritanceOneReverseEngineering.EntityClasses.Manager>()
            .Where(p__1 => !(p__1 is InheritanceOneReverseEngineering.EntityClasses.BoardMember))
            .Select(p__1 => new DTOs.DtoClasses.BoardMemberDTOTypes.WorksForDepartmentDTOTypes.EmployeeManagerDTO()
            {
                ManagesDepartment = new DTOs.DtoClasses.BoardMemberDTOTypes.WorksForDepartmentDTOTypes.EmployeeManagerDTOTypes.ManagesDepartmentDTO()
                {
                    DepartmentId = p__1.ManagesDepartment.DepartmentId,
                    DepartmentType = p__1.ManagesDepartment.DepartmentType,
                    MeetingRoomCode = p__1.ManagesDepartment.MeetingRoomCode,
                    Name = p__1.ManagesDepartment.Name,
                },
                ManagesDepartmentId = p__1.ManagesDepartment.DepartmentId,
            }))
    .Union(p__0.WorksForDepartment.Employees.OfType<InheritanceOneReverseEngineering.EntityClasses.Clerk>()
            .Select(p__1 => new DTOs.DtoClasses.BoardMemberDTOTypes.WorksForDepartmentDTOTypes.EmployeeClerkDTO()
            {
                JobDescription = p__1.JobDescription,
            }))
    .Union(p__0.WorksForDepartment.Employees.OfType<InheritanceOneReverseEngineering.EntityClasses.BoardMember>()
            .Select(p__1 => new DTOs.DtoClasses.BoardMemberDTOTypes.WorksForDepartmentDTOTypes.EmployeeBoardMemberDTO()
            {
                CompanyCarId = p__1.CompanyCar.CarId,
            }))
    .ToList(),

While this works, the query is SLOW as you can imagine. The main issue is the same: it has to determine which DTO type to project the data to. (Automapper has the same issue btw, it's related to what is needed to determine the type to project to based on the data fetched from the DB, which is not doable with simple code, sadly)

You could also opt for typedlists (e.g. POCO typed lists with linq or queryspec), which are flat lists and which work OK with inheritance.

Other than the above, are there any downsides to using DTO's? The documentation is great at explaining how I work with the DTO's but not very clear on the use-cases (or I might have missed that part). It seems like the DTO's are a "light-weight" version of the regular entities which I assume comes with some limitations (if not, why even have the regular entities)?

DTOs are projected models of the entity model, which can and usually do, contain denormalized fields. Additionally, and this is a true difference, is that related elements are 'nested' and part of the parent type.

E.g. consider 'Product' which is related to OrderDetails and also to Supplier. I can create two DTOs, OrderDetails and Supplier, which derive from the entities OrderDetails and Supplier resp. and both have a nested element 'Product' but these are not the same: there aren't 'relationships': things are denormalized. So you can see them as document hierarchies you see with documentDBs.

They're useful if you want to use data in a different form in the client as you'd do on the server: e.g. an order form might need the order data and the customer name, but doesn't need the customer entity in full. This has led in the past decade to patterns like MVC and MVVM, where models were created for UI purposes (the M in MVC), and the data in those models is tailored to the usecase of the data: the UI, not how it's logically related in an entity model in the domain.

The entity model is required as the dto/document model is derived from it. Removing the entity model would give you effectively a dto model without a source.

I still think the cycles in serialization to json should be solvable though with a setting but if you have already tried those, it might indeed be useless to pursue that further. Serializing entities and entity hierarchies to the client might work but they're in the form which makes logically sense for the domain, but not necessarily for ideal usage in the UI (MVC/MVVM), see the order form example above. Here you can e.g. use DTOs or typedlists to gather the data needed for your UI/logic (the VC part) instead of entity hierarchies.

DTO models in our designer help you define such a model without a lot of coding. but it's perfectly fine to define your own if you want, and define the linq/queryspec queries yourself and project them to the objects you want to send to the client. Doing these things by hand can be time consuming and keeping things in sync adds to that, hence we added the feature to the designer to make it easier for user to use them.

As you use a lot of inheritance, you might run into the limitations I described and in general it's then better to return the entities (which led to problems in serialization) or as flat lists.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 37
Joined: 09-Nov-2016
# Posted on: 01-Mar-2018 18:58:33   

Hi Otis,

Thank you very much for your thorough response and explanation.

I still think the cycles in serialization to json should be solvable though with a setting but if you have already tried those, it might indeed be useless to pursue that further.

You are right, it is most likely solvable, the reason I did not pursue it further, is that the produced JSON contains internal LLBLGen data not relevant for the end-user (if I recall).

Building an Api around the DTO’s solves most of the above problem, but still leaves one, which is returning an entity and its related and inherited entities.

We do have subtypes so that rules out using DTO’s. Our (simplified) structure looks something like this (-> denotes a relationship):

Item -> Incident -> ActorIncident -> Actor (which is an abstract class inherited by Person and Institution).

We need to return an Item with all the information from its related entities and if using DTO’s we can’t return data from Person or Institution (as you explained). If we don’t return this data the frontend needs to query the Person or Institution endpoint to fill in the missing data before showing it to the user, which we can’t do.

I tried using the TypedList you suggested, however I am getting an Exception that I am not sure I understand:

SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryExecutionException: 'An exception was caught during the execution of a retrieval query: The multi-part identifier "LPLA_6.Id" could not be bound.
The multi-part identifier "LPLA_6.OldItemId" could not be bound.
The multi-part identifier "LPLA_6.Id" could not be bound...

I have created a repository that shows the error, which can be found here: https://github.com/andreasnauta/test-llblgen-webapi

Steps to reproduce the error: 1: Execute the database script on an SQL Server 2017 Express Edition: 2: Change the connection string in the StartUp.cs class in the WebAPI project (if it is anything else than localhost). 3: Run the WebAPI project. 4: In the browser go to {localhost}/api/SpecificItem/GetInflated/1 (It’s the same error whether or not that database contains any data)

When I look at the relationship structure in the TypedList in the Designer, it looks wrong, but I am unable to change it, maybe that is the cause.

Is TypedList the best way to achieve what I am trying to do?

Best regards, Andreas

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 01-Mar-2018 20:49:29   

regarding the typedlist error: could you try the latest hotfix build (5.3.4)? We fixed a linq related issue in that one, which might be the cause of your issue.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 37
Joined: 09-Nov-2016
# Posted on: 01-Mar-2018 23:08:35   

That was it simple_smile It works now.

I have have one Item related to multiple Incidents but only the first Incident is listed in the TypedList. How does a TypedList handle 1:n relationships? For example, the output from the DTO looks like this:

{  
       "id":1,
       "incidents":
        [  
             {  
                  "grouping":123,
                  "id":1
              },
              {  
                   "grouping":1212,
                   "id":2
              }
        ]
}

Seems to come out like this in the TypedList (with slightly different names):

{
      "itemId": 1,
      "incidentId": 1,
      "incidentGrouping": 123
}
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 02-Mar-2018 09:37:09   

Thanks for testing simple_smile

A typed list is a flat list, so you add multiple entities to the typedlist, the relationships in the typed list (tab 2 in the typedlist editor) define how it's joined at runtime, and when the typedlist is fetched, it effectively creates a join query and projects the results to the typedlist row type. So this won't give you a hierarchical set, but a flat list.

For DTOs, the root of the hierarchy can be an inheritance entity, that's OK. The problem is only with the nested DTOs, like in your example the 'Incidents', those are nested, if these are subtypes, then it's problematic.

Frans Bouma | Lead developer LLBLGen Pro