Rhetos / Rhetos

Rhetos is a DSL platform, specialized for Enterprise Application Development.
http://www.rhetos.org/
Other
48 stars 33 forks source link

Return computed json as simple as possible #196

Open gbanusi opened 4 years ago

gbanusi commented 4 years ago

Hi,

I have a computed with FilterBy method. I need to return a JSON of an object referencing other objects:

Computed Countries 'repository => { return ...; }'
{
        ShortString entity;
        Object Entity 'Module.Entity';

        FilterBy CountriesLastModifiedTime '(repository, parameter) =>
        {
          return repository.Module.Entity.Query().
                Select(x =>  new 
                    {
                        ID = x.ID,
                        ...
                        Entity= x.Entity,
                    }
                ).ToArray().Select(x => new Countries()
                    {
                        ID = x.ID,
                        ...
                        Entity= x.Entity!= null ? x.Entity.ToSimple() : null,
                }).ToArray();
        }';
}

Is there any possibility to avoid selecting twice? What is the best approach?

This is an example how the json should look like:

image

bantolov commented 4 years ago

Hi, What result do you get if you simply remove the first Select and ToSimple()? Are there any errors? For example:

FilterBy CountriesLastModifiedTime '(repository, parameter) =>
{
    return repository.Module.Entity.Query()
        .Select(x => new Countries()
            {
                ID = x.ID,
                ...
                Entity= x.Entity
        }).ToArray();
}';

I have tried to reproduce a similar setup with entities Comment and Book, from Bookstore - Book.rhe. The query seems to work, at least in the LinqPad script:

class PrincipalHasRoleDTO
{
    public Guid ID { get; set; }
    public string Text { get; set; }
    public Bookstore.Book Book { get; set; }
}
...
var data = repository.Bookstore.Comment.Query()
    .Select(comment => new PrincipalHasRoleDTO
    {
        ID = comment.ID,
        Text = comment.Text,
        Book = comment.Book
    })
    .ToArray();

// Trying to simulate the web response:
JsonConvert.SerializeObject(data, Newtonsoft.Json.Formatting.Indented).Dump();

Result:

[
  {
    "ID": "259c6719-d20c-4b76-a2b5-0b24a432391e",
    "Text": "b",
    "Book": {
      "Code": "123",
      "Active": null,
      "AuthorID": "0c61ca43-03ad-4208-a328-ef6cd766745c",
      "NumberOfPages": 123,
      "Price": null,
      "Title": "123",
      "ID": "9b1dd9c7-6e78-4ea0-a24c-9e812a8c15d5"
    }
  },
  {
    "ID": "ce736146-556a-41c8-83e3-23a9e5b3d349",
    "Text": "a",
    "Book": {
      "Code": "123",
      "Active": null,
      "AuthorID": "0c61ca43-03ad-4208-a328-ef6cd766745c",
      "NumberOfPages": 123,
      "Price": null,
      "Title": "123",
      "ID": "9b1dd9c7-6e78-4ea0-a24c-9e812a8c15d5"
    }
  }
]
gbanusi commented 4 years ago

When I try it like you did I get the following:

2019-09-19 17:30:27.3582|ERROR|GlobalErrorHandler|System.Runtime.Serialization.SerializationException: Type 'System.Data.Entity.DynamicProxies.MasterData_ForeignCu_396E705A5D066358B9CC0FF8B3038BA3DD6A41ED30BEA1473A2CD05805515881' with data contract name 'MasterData_ForeignCu_396E705A5D066358B9CC0FF8B3038BA3DD6A41ED30BEA1473A2CD05805515881:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.

bantolov commented 4 years ago

The SerializationException is caused by WCF's JSON serializer having issues with Entity Framework's proxy classes. This is why my test worked well with Newtonsoft's serializer, but web response fails.

Your code example (two Select methods) is a good workaround that helps to simplify the data types for the serializer.

I'll try to find a cleaner solution for this example and post the results here.

bantolov commented 4 years ago

I have done some research, this problem is a combination of several factors:

This whole issue might be solved automatically after we move away from WCF to ASP.NET Core and use a different JSON serializer. In the meantime, you could modify the generated DTO class to provide a customized serialization for the complex properties, that will avoid the issues above.

You can find the complete solution in this commit on Bookstore repository.

  1. It contains a custom concept: ComplexProperty, that fixes this issue with an alternative property for serialization.
    • Modify your implementation of Object concept to do the same, or use ComplexProperty instead.
  2. DSL script DTO.rhe provides an example of using this concept, with web response similar to the one described in the question.