[.Net] File Upload (custom action): Object reference not set to an instance of an object.

[.Net] File Upload (custom action): Object reference not set to an instance of an object.

Vincent.PVincent.P Posts: 35Questions: 6Answers: 0
edited August 2021 in Editor

Hello everyone,

On a project we are doing, we want to be able to upload files to an Azure Blob Storage. It is working fine as long as we don't want to save any information inside the database.

Here is the relevant part of the code that is failing:

                    .MJoin(
                        new MJoin("uploaded_files")
                        .Link("scraping_consult.id","assigned_files_articles.article_id")
                        .Link("uploaded_files.id","assigned_files_articles.file_id")
                        .Field(new Field("id")
                            // .SetFormatter( Format.NullEmpty() )
                            .Upload(
                                new Upload((file, id) => {
                                    var storage = new Storage("articles");
                                    return storage.UploadFile(file); // saving on the Azure Blob Storage
                                })
                                .Db("uploaded_files", "id", new Dictionary<string, object>
                                {
                                    {"file_name", Upload.DbType.FileName},
                                    {"file_size", Upload.DbType.FileSize},
                                    {"extension", Upload.DbType.Extn}                                    
                                })
                            )
                            .SetFormatter(Format.NullEmpty())
                 )

If we comment the .Db(…) function call, the file is uploaded on Azure, but when we keep the .Db(…) call, the file isn't even uploaded (the .Db(…) seems to be called before the new Upload(…) and returns the following error: Object reference not set to an instance of an object.

Any idea how we could save the information inside the database with the custom function? The return storage.UploadFile(file) returns the current web_path of the file.

Best regards,
Vincent.

This question has an accepted answers - jump to answer

Answers

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Could you try adding .TryCatch(false) just before the .Process(...) call please? That might narrow down for us where in the library the error is being thrown.

    Thanks,
    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0
    edited August 2021

    Hi Allan, here is the stacktrace:

    System.NullReferenceException: Object reference not set to an instance of an object.
       at DataTables.Upload._path(String val, String name, String id)
       at DataTables.Upload._dbExec(Editor editor, IFormFile upload)
       at DataTables.Upload.Exec(Editor editor)
       at DataTables.Editor._Upload(DtRequest data)
       at DataTables.Editor._Process(DtRequest data)
       at DataTables.Editor.Process(DtRequest data)
       at DataTables.Editor.Process(IEnumerable`1 data, String culture)
       at DataTables.Editor.Process(HttpRequest request, String culture)
       at pr01.Pages.ConsultModel.GetArticles() in C:\Users\Vincent\project\Pages\Overview\Consult.cshtml.cs:line 1390
       at pr01.Pages.ConsultModel.GetOverview(String dpmt) in C:\Users\Vincent\project\Pages\Overview\Consult.cshtml.cs:line 921
       at pr01.Pages.ConsultModel.OnPost() in C:\Users\Vincent\project\Pages\Overview\Consult.cshtml.cs:line 77
       at lambda_method281(Closure , Object , Object[] )
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.ActionResultHandlerMethod.Execute(Object receiver, Object[] arguments)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeNextResourceFilter()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
       at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
       at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
       at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
       at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
    

    Best regards,
    Vincent

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Hi Vincent,

    Many thanks! This is a curious one - two questions:

    1. What version of the .NET libraries for Editor are you using (the DataTables.dll)? If not the latest (2.0.5 as of today) it would be worth updating as I'm seeing how the code would get into that point with the way it is currently written.
    2. What does storage.UploadFile(file) return - is it a string, number or something else? I don't think it matters to be honest, but just in case it is useful information...

    Thanks,
    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0

    Hi Allan,

    Currently we are on version 2.0.2, I will look at updating it to the latest version but we need to be careful with this as the project will soon™ reach Production :smile:

    About storage.UploadFile(file), it currently returns only a string. I was wondering if because it is not the id (which is a int in the db) that it could lead to the issue.

    But the lambda function (file, id) => { … } is not executed at all with the .Db (it fails before it uploads the file).

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Thanks.

    In _dbExec() the _path method is only called if the pathFields parameter has any values in it.

    In turn, that will only happen if the properties are custom, DbType.SystemPath or DbType.WebPath, none of which appear to be the case here.

    I can certainly add a check to make sure that the _path has a string (in fact, I see no harm in doing that, so I've added that in). I think that will fix what you are seeing, but I'm slightly concerned that I don't understand why it is happening in the first place!

    Do you want me to build a new dll for you, or are you happy to do so from that repo?

    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0

    Hi Allan,

    I think it'll be best if you build the DLL for us and we try it right away :smile:

    I'll let you know the results.

    Best regards,
    Vincent

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin
    Answer ✓

    Hi Vincent,

    Here we go: https://www.dropbox.com/s/q6jgt6dtqo4pz4a/Editor-dll-2.0.6-dev.zip?dl=0 . The dll itself will show as 2.0.5 - I just haven't updated the version from the recent release.

    Let me know how you get on with it.

    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0

    Hi Allan,

    The issue we had seems to be solved by the new dll.

    However, is it possible to return multiple values from the closure function?

    How could we use them inside the .Db function?

          new Upload((file, id) => {
                    var storage = new Storage("articles");
                    return storage.UploadFile(file); // we would like to return the id + webpath here
                })
                .Db("uploaded_files", "id", new Dictionary<string, object>
                {
                    {"file_name", "test"},
                    {"file_size", Upload.DbType.FileSize},
                    {"extension", Upload.DbType.Extn}
                    {"web_path", ""}, // We would like to use the returned value here                
                })
    

    Is it possible?

    Thanks,
    Vincent

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Hi Vincent,

    Good to hear the patch worked - thanks for letting me know.

    That particular method isn't possible I'm afraid. Instead what you need to do is update the newly created database row in your action function. You've got the id for the primary key, so something like:

    editor.db().Update(
      "uploaded_files",
      new Dictionary<string, object>{
        {"web_path", myVar}
      },
      new Dictionary<string, object>{
        {"id", id}
      }
    );
    

    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0
    edited August 2021

    Hi Allan,

    Thanks, that does the trick.

    However, I have few other concerns:

    • As it is part of an MJoin, it doesn't update the Link table beween the uploaded file table and the item that is associated with the file. It is just empty.

    • Is it possible to prevent adding a new item in the Db if the file already exists?

    as an example, let's say I have "file.txt" and I upload it the first time. Let's assume I get the id "5". I then upload multiple other files and at some point I upload "file.txt" again (same file). How could I say to the DataTables Editor to reuse the ID 5 and not add a new item in the database?

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    as an example, let's say I have "file.txt" and I upload it the first time

    This gets a bit more nuanced - consider the case where someone else has a file which is also called file.txt which has completely different contents. Or you come back to the system after a few months (or even a few hours) and try to upload a file with the same name, but different contents. This proposed scheme would either overwrite the old file or throw away the new one. Using filename for uniqueness, in my opinion, is not a good move. You could perform a hash on the file and check that for uniqueness, but you'd need a validator to reject the upload and instruct them to select the file that was previously uploaded, which isn't ideal. Unless you are dealing with large files, I'd just let them upload it again.

    As it is part of an MJoin, it doesn't update the Link table beween the uploaded file table and the item that is associated with the file. It is just empty.

    Until you submit the form, yes, that should be the case. The uploaded_files table will get written to with the file information, but the link table will only be written to when you submit the form.

    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0

    Hi Allan,

    The filename example was not a good one. Indeed, we would keep a hash of the file in the table.

    This is how everything should happen:

    1. Someone uploads a file, let's say we get the hash "abcdef123" from it.
      • The file is attached to an item in the db (let's say, file id is 3, item id is 5). Everything is fine
    2. Someone else uploads the same file (returning the same hash "abcdef123")
      • The file is not uploaded but the item they are updating is attached to file id 3 as well.

    The goal is to allow multiple files to be attached to multiple items, but only one existing shared file should exist on the server.

    I tried to use the editor.PreUpload function for that but it seems it can't be used that way.

    Best regards,
    Vincent.

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    HI Vincent,

    I'm afraid what you describe is not something our upload functionality can currently handle. Some kind of reusable image / file picker is something we need to look into adding to Editor, but at the moment, the upload is assumed to always create a new file. Apologies.

    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0

    Hi Allan,

    I understand. It's not really an easy task to tackle as the file would be read twice in case it has to be written.

    We are also wondering if it would be really a must have to write those files once or not.

    Thanks a lot for your help of the issue.

    Quick question: when should we expect the new version of the editor (with the recent change) to be the official release?

    Best regards,
    Vincent.

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    Hi Vincent,

    Do you mean with the change in the .NET code for the dll? Probably towards the end of this month. There are only this and one other change to Editor since the 2.0.5 release thus far.

    Regards,
    Allan

  • Vincent.PVincent.P Posts: 35Questions: 6Answers: 0

    Hi Allan,

    Yes that was the meaning of my question

    Thanks a lot for your help.

    Best regards,
    Vincent.

Sign In or Register to comment.