Starcounter / Starcounter.Uniform

Helper library for server-side part of uniform components.
MIT License
0 stars 0 forks source link

Add support for nested view-model properties #15

Closed PatrykSzwer closed 5 years ago

PatrykSzwer commented 6 years ago

I have situation like that: View-model for transfer row:

{
  "Name": "",
  "From": {
    "Name": "",
    "Url": "" 
  },
  "To": {
    "Name": "",
    "Url": "" 
  },
}

Data-model:

    [Database]
    public class Transfer
    {
        public Transaction Transaction { get; set; }
        public Item Item { get; set; }               
        public Host From { get; set; }             
        public Host To { get; set; }               
    }

Uni-data-table implementation:

            this.TransfersDataTable = new DataTableBuilder<TransferRow>()
                .WithDataSource(_transactionsRepository.GetAllTransactionTransfers(this.Data), data => data.WithFilter(new TransfersFilter()))
                .WithColumns(columns =>
                    columns
                        .AddColumn(b => b.From.Name, column => column.DisplayName("From").Sortable().Filterable()))
                .Build();

When I try to run this I'm reciving exception:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   at Starcounter.Startup.Routing.PageContextSupport.HandleContext(Object page, Object context)
   at Starcounter.Startup.Routing.Activation.DefaultPageCreator.Create(RoutingInfo routingInfo)
   at Starcounter.Startup.Routing.Router.<>c__DisplayClass8_0.<RunResponse>b__0()
   at Starcounter.Startup.Routing.Router.TerminalMiddleware.Run(RoutingInfo routingInfo, Func`1 next)
   at Starcounter.Startup.Routing.Router.RunWithMiddleware(RoutingInfo routingInfo, IEnumerable`1 middlewares)
   at Starcounter.Startup.Routing.Router.<>c__DisplayClass9_0.<RunWithMiddleware>b__0()
   at Starcounter.Startup.Routing.Middleware.ContextMiddleware.Run(RoutingInfo routingInfo, Func`1 next)
   at Starcounter.Startup.Routing.Router.RunWithMiddleware(RoutingInfo routingInfo, IEnumerable`1 middlewares)
   at Starcounter.Startup.Routing.Router.<>c__DisplayClass9_0.<RunWithMiddleware>b__0()
   at Starcounter.Db.<>c__DisplayClass50_0`1.<Scope>b__0() in C:\tc_work\sc-19418\Level1\src\Starcounter\Db.cs:line 636
   at Starcounter.Internal.TransactionManager.Scope(TransactionHandle handle, Action action) in C:\tc_work\sc-19418\Level1\src\Starcounter\TransactionManager.cs:line 557
   at Starcounter.Transaction.Scope(Action action) in C:\tc_work\sc-19418\Level1\src\Starcounter\Transaction.cs:line 233
   at Starcounter.Db.Scope(Action action, Boolean isReadOnly) in C:\tc_work\sc-19418\Level1\src\Starcounter\Db.cs:line 613
   at Starcounter.Db.Scope[TResult](Func`1 func, Boolean isReadOnly) in C:\tc_work\sc-19418\Level1\src\Starcounter\Db.cs:line 637
   at Starcounter.Startup.Routing.Middleware.DbScopeMiddleware.Run(RoutingInfo routingInfo, Func`1 next)
   at Starcounter.Startup.Routing.Router.RunWithMiddleware(RoutingInfo routingInfo, IEnumerable`1 middlewares)
   at Starcounter.Startup.Routing.Router.RunResponse(Type pageType, Request request, String[] arguments)
   at Starcounter.Startup.Routing.Router.<>c__DisplayClass7_0.<HandleGet>b__1(String arg, Request request)
   at lambda_method(Closure , Request , IntPtr , IntPtr )
   at Starcounter.Rest.UserHandlerInfo.RunUserDelegate(Request req, IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack) in C:\tc_work\sc-19418\Level1\src\Starcounter.Rest\UriHandlersManager.cs:line 203
   at Starcounter.Internal.Web.AppRestServer.RunDelegateAndProcessResponse(IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack, Request req) in C:\tc_work\sc-19418\Level1\src\Starcounter.Apps.JsonPatch\AppRestServer.cs:line 95
   at Starcounter.Rest.UriManagedHandlersCodegen.RunUriMatcherAndCallHandler(String methodSpaceUriSpace, String methodSpaceUriSpaceLower, Request req, UInt16 portNumber, Response& resp) in C:\tc_work\sc-19418\Level1\src\Starcounter.Rest\UriManagedHandlersCodegen.cs:line 1274
   at Starcounter.Self.DoSelfCall(UInt16 portNumber, String method, String relativeUri, Dictionary`2 headersDictionary, String body, Byte[] bodyBytes, HandlerOptions handlerOptions, Request req) in C:\tc_work\sc-19418\Level1\src\Starcounter.Internal\Rest\Self.cs:line 481
   at Starcounter.Self.GET(String uri) in C:\tc_work\sc-19418\Level1\src\Starcounter.Internal\Rest\Self.cs:line 102
   at Inventory.Api.MainHandlers.<>c.<Register>b__0_1(String id) in C:\repositories\Inventory New\Inventory\src\Inventory\Api\MainHandlers.cs:line 11
   at lambda_method(Closure , Request , IntPtr , IntPtr )
   at Starcounter.Rest.UserHandlerInfo.RunUserDelegate(Request req, IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack) in C:\tc_work\sc-19418\Level1\src\Starcounter.Rest\UriHandlersManager.cs:line 203
   at Starcounter.Internal.Web.AppRestServer.RunDelegateAndProcessResponse(IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack, Request req) in C:\tc_work\sc-19418\Level1\src\Starcounter.Apps.JsonPatch\AppRestServer.cs:line 95
   at Starcounter.Internal.AppsBootstrapper.ProcessExternalRequest(Request req) in C:\tc_work\sc-19418\Level1\src\Starcounter.Apps.JsonPatch\AppsBootstrapper.cs:line 356
HResult=-2146232828
---> System.ArgumentException: Expression 'b => b.From.Name' refers to a property that is not from type Inventory.ViewModels.TransferRow.
   at Starcounter.Uniform.Builder.DataColumnBuilder`1.AddColumn[TColumn](Expression`1 propertySelector, Action`1 configure)
   at Inventory.ViewModels.TransactionPage.<>c.<OnData>b__3_1(DataColumnBuilder`1 columns) in C:\repositories\Inventory New\Inventory\src\Inventory\ViewModels\TransactionPage.json.cs:line 30
   at Starcounter.Uniform.Builder.DataTableBuilder`1.WithColumns(Action`1 configure)
   at Inventory.ViewModels.TransactionPage.OnData() in C:\repositories\Inventory New\Inventory\src\Inventory\ViewModels\TransactionPage.json.cs:line 27
   at Starcounter.Transaction.Scope(Action action) in C:\tc_work\sc-19418\Level1\src\Starcounter\Transaction.cs:line 225
   at Starcounter.XSON.Internal.XSONInternals.RunWithinApplication(String appName, Action action) in c:\github\Starcounter.XSON\src\Starcounter.XSON\Internal\XSONInternals.cs:line 41
HResult=-2147024809

Most important: System.ArgumentException: Expression 'b => b.From.Name' refers to a property that is not from type Inventory.ViewModels.TransferRow.

which is not true, since TransferRow has b.From.Name. I think we should implement support for nested view-models since it is/will be really popular use-case, and right now user can't have automatically generated filtering and sorting for those columns. It is possible to workaround this but I'm not sure if it should be necessary.

CC: @joozek78

joozek78 commented 6 years ago

Most important: System.ArgumentException: Expression 'b => b.From.Name' refers to a property that is not from type Inventory.ViewModels.TransferRow.

which is not true, since TransferRow has b.From.Name

The warning is not clear enough, but it's true. Name is not a property of TransferRow. It's a property of TransferRow_From.

I think we should implement support for nested view-models since it is/will be really popular use-case

I think it's a good idea, but I don't know if we have enuogh time for this. It's not my decision

warpech commented 6 years ago

I just bumped on that problem.

My JSON:

{
  "Id": "",
  "Created": "",
  "MainViewUri": "", /*this works*/
  "MainView": {  /*this is problematic*/
    "Uri": ""
  },
  "DeleteTrigger$": 0
}

My C#:

public CompositionsPage()
{
    this.CompositionsTable = new DataTableBuilder<CompositionsPageCompositionTableItem>()
        .WithDataSource(DbLinq.Objects<HtmlViewComposition>().Where(x => x.NextVersion == null), data => data
            .WithConverter(CompositionRow)
            .WithFilter(new CompositionFilter()))
        .WithColumns(columns =>
            columns
                .AddColumn(b => b.Created, column => column.DisplayName("Created at").Sortable().Filterable())
                .AddColumn(b => b.MainViewUri, column => column.DisplayName("Main view").Sortable().Filterable())
                .AddColumn(b => b.MainView.Uri, column => column.DisplayName("Main view").Sortable().Filterable())
        )
        .Build();
}

Expected

MainViewUri and MainView.Uri should bind equally well.

(MainViewUri is just a getter for MainView.Uri)

Actual

b => b.MainViewUri works as desired but b => b.MainView.Uri throws:

System.ArgumentException
  HResult=0x80070057
  Message=Expression 'b => b.MainView.Uri' refers to a property that is not from type BlendingEditor.Website.ViewModels.CompositionsPageCompositionTableItem.
  Source=Starcounter.Uniform
  StackTrace:
   at Starcounter.Uniform.Builder.DataColumnBuilder`1.AddColumn[TColumn](Expression`1 propertySelector, Action`1 configure)
   at BlendingEditor.Website.ViewModels.CompositionsPage.<>c.<.ctor>b__1_2(DataColumnBuilder`1 columns) in W:\repo\HeadsOmni\src\Blending.Mapper\Blending\src\BlendingEditor\Website\ViewModels\CompositionsPage.json.cs:line 24
   at Starcounter.Uniform.Builder.DataTableBuilder`1.WithColumns(Action`1 configure)
   at BlendingEditor.Website.ViewModels.CompositionsPage..ctor() in W:\repo\HeadsOmni\src\Blending.Mapper\Blending\src\BlendingEditor\Website\ViewModels\CompositionsPage.json.cs:line 19
   at BlendingEditor.Website.Api.MainHandlers.<>c__DisplayClass0_1.<Register>b__26() in W:\repo\HeadsOmni\src\Blending.Mapper\Blending\src\BlendingEditor\Website\Api\MainHandlers.cs:line 156
   at Starcounter.Db.<>c__DisplayClass50_0`1.<Scope>b__0()
   at Starcounter.Internal.TransactionManager.Scope(TransactionHandle handle, Action action)
   at Starcounter.Transaction.Scope(Action action)
   at Starcounter.Db.Scope(Action action, Boolean isReadOnly)
warpech commented 5 years ago

@PatrykSzwer could you please investigate. Perhaps @miyconst can help out.

PatrykSzwer commented 5 years ago

I added support for single-level nested properties in the 2.5.0 version. There is also PR for the documentation about the new feature here https://github.com/Starcounter/Starcounter.Uniform/pull/29 but I think this issue can be already closed.

miyconst commented 5 years ago

https://github.com/Starcounter/Starcounter.Uniform/commit/949d513071e06c8504edc02ea01104e862be0d8a#diff-247c387328a4566f44c7c89cfab4f205R48