serenity-is / Serenity

Business Apps Made Simple with Asp.Net Core MVC / TypeScript
https://serenity.is
MIT License
2.6k stars 798 forks source link

Exporting USERs to excel thorws exceptions #1972

Closed XDDDev closed 7 years ago

XDDDev commented 7 years ago

When we try to export UserGrid in EXCEL, it throws exception, here is the stack "\Modules\Administration\User\UserGrid.ts"

Cannot create an instance of an interface. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.MissingMethodException: Cannot create an instance of an interface.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[MissingMethodException: Cannot create an instance of an interface.] System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +113 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +206 System.Activator.CreateInstance(Type type, Boolean nonPublic) +83 System.Activator.CreateInstance(Type type) +11 System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +197

[MissingMethodException: Cannot create an instance of an interface. Object type 'System.Data.IDbConnection'.] System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +233 System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +531 System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +330 System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +331 System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +105 System.Web.Mvc.Async.<>cDisplayClass21.b19(AsyncCallback asyncCallback, Object asyncState) +743 System.Web.Mvc.Async.WrappedAsyncResult1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +14 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +343 System.Web.Mvc.Controller.b1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +25 System.Web.Mvc.Async.WrappedAsyncVoid1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +465 System.Web.Mvc.Controller.b14(AsyncCallback asyncCallback, Object callbackState, Controller controller) +18 System.Web.Mvc.Async.WrappedAsyncVoid1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +20 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +374 System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +16 System.Web.Mvc.MvcHandler.b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +52 System.Web.Mvc.Async.WrappedAsyncVoid1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +30 System.Web.Mvc.Async.WrappedAsyncResultBase1.Begin(AsyncCallback callback, Object state, Int32 timeout) +128 System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +384 System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +48 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +103 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

volkanceylan commented 7 years ago

Make sure your controller derives from ServiceEndpoint

XDDDev commented 7 years ago

Thank you. It still does not export, does not even hit in debug point in UserEndpoint.cs file. Only prompt I get to download an excel file name "LIstExcel" and when I try to save it, it does not get downloaded.

This is what I have now in UserEndpoint.cs file

public class UserController : ServiceEndpoint
{

    public FileContentResult ListExcel(IDbConnection connection, ListRequest request)
    {
        var data = List(connection, request).Entities;

        var report = new DynamicDataReport(data, request.IncludeColumns, typeof(XDD_PM.Administration.Forms.UserColumns));
        var bytes = new ReportRepository().Render(report);
        var reportName = "UserList_";
        return ExcelContentResult.Create(bytes, reportName + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".xlsx");
    }

    [AcceptVerbs("POST"), JsonFilter]
    public Result<SaveResponse> Create(SaveRequest<MyRow> request)
    {
        return this.InTransaction("Default", (uow) => new MyRepository().Create(uow, request));
    }

.......................... }

And this is what I have in my Grid.cs file:

protected getButtons() { var buttons = super.getButtons();

        // PDF Button
        buttons.push(Common.PdfExportHelper.createToolButton({
            grid: this,
            onViewSubmit: () => this.onViewSubmit()

        }));

        // Excel Button
        buttons.push(Common.ExcelExportHelper.createToolButton({
            grid: this,
            onViewSubmit: () => this.onViewSubmit(),
            service: UserService.baseUrl + '/ListExcel'
        }));
        return buttons;

    }

Would you please suggest what I am doing wrong?

sayuga commented 7 years ago

Double check your steps. I just did it without issue. Here is a quick walk through.


In UserEndpoint cs:

Add: using Serenity.Reporting; using Serenity.Web;

//.. public FileContentResult ListExcel(IDbConnection connection, ListRequest request) { var reportName = "My Report"; var timeStamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); var data = List(connection, request).Entities; var report = new DynamicDataReport(data, request.IncludeColumns, typeof(Columns.UserColumns)); var bytes = new ReportRepository().Render(report); return ExcelContentResult.Create(bytes, reportName + timeStamp + ".xlsx"); }


Then in UserGrid.ts

getButtons() { var buttons = super.getButtons(); var MyRow = UserRow.Fields;

        buttons.push(Common.ExcelExportHelper.createToolButton({
            grid: this,
            onViewSubmit: () => this.onViewSubmit(),
            service: UserService.baseUrl + '/ListExcel',
            separator: true,
            hint: "",
            title: "My Users List"
        }));            

        return buttons;
    }
XDDDev commented 7 years ago

Thank you, but it still does not work: This is what I have in my UserEndpoint.cs file.

namespace Administration.Endpoints { using Serenity.Data; using Entities; using Repositories; using Serenity; using Serenity.ComponentModel;
using Serenity.Services; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Web.Mvc; using MyRepository = Repositories.UserRepository; using MyRow = Entities.UserRow;
using Serenity.Reporting; using Serenity.Web;

[ServiceAuthorize(typeof(MyRow))]
[RoutePrefix("Services/Administration/User"), Route("{action}")]   
public class UserController : ServiceEndpoint
{

    public FileContentResult ListExcel(IDbConnection connection, ListRequest request)
    {
        var data = List(connection, request).Entities;

        var report = new DynamicDataReport(data, request.IncludeColumns, typeof(Columns.UserColumns));
        var bytes = new ReportRepository().Render(report);
        var reportName = "UserList_";
        return ExcelContentResult.Create(bytes, reportName + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".xlsx");
    }

    [AcceptVerbs("POST"), JsonFilter]
    public Result<SaveResponse> Create(SaveRequest<MyRow> request)
    {
        return this.InTransaction("Default", (uow) => new MyRepository().Create(uow, request));
    }

    [AcceptVerbs("POST"), JsonFilter]
    public Result<SaveResponse> Update(SaveRequest<MyRow> request)
    {
        return this.InTransaction("Default", (uow) => new MyRepository().Update(uow, request));
    }

    [AcceptVerbs("POST"), JsonFilter]
    public Result<DeleteResponse> Delete(DeleteRequest request)
    {
        return this.InTransaction("Default", (uow) => new MyRepository().Delete(uow, request));
    }

    [AcceptVerbs("POST"), JsonFilter]
    public Result<UndeleteResponse> Undelete(UndeleteRequest request)
    {
        return this.InTransaction("Default", (uow) => new MyRepository().Undelete(uow, request));
    }

    [AcceptVerbs("GET", "POST"), JsonFilter]
    public Result<RetrieveResponse<MyRow>> Retrieve(RetrieveRequest request)
    {
        return this.UseConnection("Default", (cnn) => new MyRepository().Retrieve(cnn, request));
    }

    [AcceptVerbs("GET", "POST"), JsonFilter]
    public Result<ListResponse<MyRow>> List(ListRequest request)
    {
        return this.UseConnection("Default", (cnn) => new MyRepository().List(cnn, request));
    }
    public ListResponse<MyRow> List(IDbConnection connection, ListRequest request)
    {
        return new MyRepository().List(connection, request);
    }

    private static string[] permissionsUsedFromScript;

    /// <summary>
    /// This declares a dynamic script with key 'UserData' that will be available from client side.
    /// We don't cache it at dynamic script manager, because dynamic scripts are cached globally,
    /// similar to static variables, not per user.
    /// </summary>
    [NonAction, DataScript("UserData", CacheDuration = -1)]
    public ScriptUserDefinition GetUserData()
    {
        var result = new ScriptUserDefinition();
        var user = Authorization.UserDefinition as UserDefinition;

        if (user == null)
        {
            result.Permissions = new Dictionary<string, bool>();
            return result;
        }

        result.Username = user.Username;
        result.DisplayName = user.DisplayName;

        result.Permissions = TwoLevelCache.GetLocalStoreOnly("ScriptUserPermissions:" + user.Id, TimeSpan.Zero,
             UserPermissionRow.Fields.GenerationKey, () =>
             {
                 var permissions = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

                 if (permissionsUsedFromScript == null)
                 {
                     permissionsUsedFromScript = new UserPermissionRepository().ListPermissionKeys().Entities
                              .Where(permissionKey =>
                              {
                                     // this sends permission information for all permission keys to client side.
                                     // if you don't need all of them to be available from script, filter them here.
                                     // this is recommended for security / performance reasons...
                                     return true;
                             }).ToArray();
                 }

                 foreach (var permissionKey in permissionsUsedFromScript)
                 {
                     if (Authorization.HasPermission(permissionKey))
                         permissions[permissionKey] = true;
                 }

                 return permissions;
             });

        return result;
    }
}

}

volkanceylan commented 7 years ago

There is working examples in Serene