ASP.NET Core 1.0 MVC File Upload with MS SQL SERVER FileTable

This article shows how to upload and download files in ASP.NET Core 1.0 MVC and save the files to a MS SQL Server using FileTable. The data access for the application is implemented in a separate project and EF7 migrations is used to setup the select logic for the database.

Code: https://github.com/damienbod/AspNet5FileUploadFileTable

Update: 02.07.2016: Updated to ASP.NET Core RTM
Update: 18.05.2016: Updated to ASP.NET Core RC2
Update: 08.12.2015: config files cleaned up and returning proper 415 response for unsupported media type

Step 1: Settings up the database FileTable

A new database is created in MS SQL Server which has Filestreams enabled. This feature only works with windows authentication. Firstly if not already configured, the Filestream access level is set to 2.

EXEC sp_configure filestream_access_level, 2
RECONFIGURE
GO

Once this has been set, create a new directory to save the files. In this example, C:\damienbod\WebApiFileTable is used. Now execute the following command:

CREATE DATABASE WebApiFileTable
ON PRIMARY
(Name = WebApiFileTable,
FILENAME = 'C:\damienbod\WebApiFileTable\FTDB.mdf'),
FILEGROUP FTFG CONTAINS FILESTREAM
(NAME = WebApiFileTableFS,
FILENAME='C:\damienbod\WebApiFileTable\FS')
LOG ON
(Name = WebApiFileTableLog,
FILENAME = 'C:\damienbod\WebApiFileTable\FTDBLog.ldf')
WITH FILESTREAM (NON_TRANSACTED_ACCESS = FULL,
DIRECTORY_NAME = N'WebApiFileTable');
GO

Now you can check if your database settings are ok.

SELECT DB_NAME(database_id),
non_transacted_access,
non_transacted_access_desc
FROM sys.database_filestream_options;
GO

The database should be configured as follows:
FileTableWebApi01

Now create a table for the file uploads:

USE WebApiFileTable
GO
CREATE TABLE WebApiUploads AS FileTable
WITH
(FileTable_Directory = 'WebApiUploads_Dir');
GO

The files can be saved, deleted or updated using the following path:

\\{yourPCname}\{mssqlserver}\WebApiFileTable\WebApiUploads_Dir

The files can also be accessed using plain SQL.

INSERT INTO [dbo].[WebApiUploads]
([name],[file_stream])
SELECT
'NewFile.txt', * FROM OPENROWSET(BULK N'd:\NUnit-2.6.1.msi', SINGLE_BLOB) AS FileData
GO>

Step 2: Adding the Entity Framework 7 data access layer

A file description table is created for searching and returning multiple records. This is used to setup a download link and provide a small description of the file. To create the table, Entity Framework code first is used in this example.

Add Entity framework 7 to the project.json file in your project. The EF7 dependencies need to be added and also the ef commands.

{
    "dependencies": {
        "Microsoft.NETCore.App": {
            "version": "1.0.0",
            "type": "platform"
        },
        "Microsoft.EntityFrameworkCore": "1.0.0",
        "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
        "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0",
        "Microsoft.AspNetCore.Diagnostics": "1.0.0",
        "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0",
        "Microsoft.EntityFrameworkCore.Tools": {
            "version": "1.0.0-preview2-final",
            "imports": [
                "portable-net45+win8+dnxcore50",
                "portable-net45+win8"
            ]
        },
        "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0",
        "Microsoft.AspNetCore.Mvc": "1.0.0",
        "Microsoft.AspNetCore.Razor.Tools": {
            "version": "1.0.0-preview2-final",
            "type": "build"
        },
        "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
        "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
        "Microsoft.AspNetCore.StaticFiles": "1.0.0",
        "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
        "Microsoft.Extensions.Configuration.Json": "1.0.0",
        "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0",
        "Microsoft.Extensions.Logging": "1.0.0",
        "Microsoft.Extensions.PlatformAbstractions": "1.0.0",
        "Microsoft.Extensions.Localization": "1.0.0",
        "Microsoft.Extensions.Logging.Console": "1.0.0",
        "Microsoft.Extensions.Logging.Debug": "1.0.0",
        "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0",
        "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
            "version": "1.0.0-preview2-final",
            "type": "build"
        },
        "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
            "version": "1.0.0-preview2-final",
            "type": "build"
        }
    },

  "tools": {
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.EntityFrameworkCore.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": [
        "portable-net45+win8+dnxcore50",
        "portable-net45+win8"
      ]
    },
    "Microsoft.Extensions.SecretManager.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": "portable-net45+win8+dnxcore50"
    },
    "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
      "version": "1.0.0-preview2-final",
      "imports": [
        "portable-net45+win8+dnxcore50",
        "portable-net45+win8"
      ]
    }
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "dnxcore50",
        "portable-net45+win8"
      ]
    }
  }
}


        

An entity context class has to be created to use the database. This is used for the migrations and also the data access.

using DataAccess.Model;
using Microsoft.EntityFrameworkCore;

namespace DataAccess
{
    public class FileContext : DbContext
    {
        public FileContext(DbContextOptions<FileContext> options) :base(options)
        { }
        
        public DbSet<FileDescription> FileDescriptions { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<FileDescription>().HasKey(m => m.Id);
            base.OnModelCreating(builder);
        }
    }
}

The class used as the entity also needs to be created. The primary key for this class is also defined in the context class.

using System;
using System.ComponentModel.DataAnnotations;

namespace DataAccess.Model
{
    public class FileDescription
    {
        public int Id { get; set; }
        public string FileName { get; set; }
        public string Description { get; set; }
        public DateTime CreatedTimestamp { get; set; }
        public DateTime UpdatedTimestamp { get; set; }
        public string ContentType { get; set; }
    }
}

The connection string needs to be added to the config file which is used in the context. This is required for migrations and also running the application.

{
    "ApplicationConfiguration": {
        "SQLConnectionString": "Data Source=N275\\MSSQLSERVER2014;Initial Catalog=WebApiFileTable;Integrated Security=True;"
    }
}

The migrations can be created and the database can be updated. Open the application using the command line in the src folder where the project is defined. The migrations are executed from the project containing the Startup file where the DbContext is used. The migrations needs to be set to this project then in the AddDbContext method.

>
> dotnet restore
>
> dotnet ef migrations add testMigration
>
> dotnet ef database update
>
>

If you don’t want to use EF7 migrations, you could just create the SQL table using plain TSQL.

USE [WebApiFileTable]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[FileDescriptions](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[FileName] [nvarchar](max) NULL,
	[Description] [nvarchar](max) NULL,
	[CreatedTimestamp] [datetime] NOT NULL,
	[UpdatedTimestamp] [datetime] NOT NULL,
	[ContentType] [nvarchar](max) NULL,
 CONSTRAINT [PK_dbo.FileDescription] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

Step 3: MVC 6 Single or Multiple file upload and download

The MVC 6 application is a simple project with razor views and a FileUpload MVC 6 controller to upload and download the files. The data access project is added as a reference in the project.json file in the dependencies. It does not matter if the dependencies uses sources from NuGet or from local projects.

{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Diagnostics": "1.0.0",
    "Microsoft.AspNetCore.Mvc": "1.0.0",
    "Microsoft.AspNetCore.Razor.Tools": {
      "version": "1.0.0-preview2-final",
      "type": "build"
    },
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.AspNetCore.StaticFiles": "1.0.0",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
    "Microsoft.Extensions.Configuration.Json": "1.0.0",
    "Microsoft.Extensions.Logging": "1.0.0",
    "Microsoft.Extensions.Logging.Console": "1.0.0",
    "Microsoft.Extensions.Logging.Debug": "1.0.0",
    "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0",
    "DataAccess": "*",
    "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0"
  },

    "tools": {
        "BundlerMinifier.Core": "2.0.238",
        "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final",
        "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final",
        "Microsoft.EntityFrameworkCore.Tools": {
            "version": "1.0.0-preview2-final",
            "imports": [
                "portable-net45+win8+dnxcore50",
                "portable-net45+win8"
            ]
        }
    },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dotnet5.6",
        "portable-net45+win8"
      ]
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "Areas/**/Views",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "prepublish": [ "bower install", "dotnet bundle" ],
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

ASP.NET 5 provides the IFormFile class for file upload. This class is used inside the FileDescriptionShort, which is used for single or multiple file uploads.

public class FileDescriptionShort
{
	public int Id { get; set; }

	public string Description { get; set; }

	public string Name { get; set; }

	public ICollection<IFormFile> File { get; set; }
}

The FileUploadController has two action methods. The controller uses the default DI with constructor injection to add the dependencies. The UploadFiles action method uses the FileDescriptionShort class as a parameter. The method takes all the files and saves each file directly to the MS SQL Server FileTable. Then the file descriptions are saved to the database. The descriptions are used to list and download to files.

The file upload logic was built using the following two blogs:

http://www.mikesdotnetting.com/article/288/asp-net-5-uploading-files-with-asp-net-mvc-6

http://dotnetthoughts.net/file-upload-in-asp-net-5-and-mvc-6/

Thanks for these articles.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using DataAccess;
using DataAccess.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
    
using FileResult = DataAccess.Model.FileResult;

namespace AspNet5FileUploadFileTable.Controllers
{
    [Route("api/test")]
    public class FileUploadController : Controller
    {
        private readonly IFileRepository _fileRepository;
        private readonly IOptions<ApplicationConfiguration> _optionsApplicationConfiguration;

        public FileUploadController(IFileRepository fileRepository, IOptions<ApplicationConfiguration> o)
        {
            _fileRepository = fileRepository;
            _optionsApplicationConfiguration = o;
        }

        [Route("files")]
        [HttpPost]
        [ServiceFilter(typeof(ValidateMimeMultipartContentFilter))]
        public async Task<IActionResult> UploadFiles(FileDescriptionShort fileDescriptionShort)
        {
            var names = new List<string>();
            var contentTypes = new List<string>();
            if (ModelState.IsValid)
            {
                // http://www.mikesdotnetting.com/article/288/asp-net-5-uploading-files-with-asp-net-mvc-6
                // http://dotnetthoughts.net/file-upload-in-asp-net-5-and-mvc-6/
                foreach (var file in fileDescriptionShort.File)
                {
                    if (file.Length > 0)
                    {
                        var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
                        contentTypes.Add(file.ContentType);
                        names.Add(fileName);

                        // Extension method update RC2 has removed this 
                        await file.SaveAsAsync(Path.Combine(_optionsApplicationConfiguration.Value.ServerUploadFolder, fileName));
                    }
                }
            }

            var files = new FileResult
                            {
                                FileNames = names,
                                ContentTypes = contentTypes,
                                Description = fileDescriptionShort.Description,
                                CreatedTimestamp = DateTime.UtcNow,
                                UpdatedTimestamp = DateTime.UtcNow,
                            };

            _fileRepository.AddFileDescriptions(files);

            return RedirectToAction("ViewAllFiles", "FileClient");
        }

        [Route("download/{id}")]
        [HttpGet]
        public FileStreamResult Download(int id)
        {
            var fileDescription = _fileRepository.GetFileDescription(id);

            var path = _optionsApplicationConfiguration.Value.ServerUploadFolder + "\\" + fileDescription.FileName;
            var stream = new FileStream(path, FileMode.Open);
            return  File(stream, fileDescription.ContentType);
        }
    }
}


The IFormFile SaveAsAsync extension needs to be added.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace AspNet5FileUploadFileTable
{
    public static class FormFileExtensions
    {
        private static int DefaultBufferSize = 80 * 1024;
        /// <summary>
        /// Asynchronously saves the contents of an uploaded file.
        /// </summary>
        /// <param name="formFile">The <see cref="IFormFile"/>.</param>
        /// <param name="filename">The name of the file to create.</param>
        public async static Task SaveAsAsync(
            this IFormFile formFile,
            string filename,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            if (formFile == null)
            {
                throw new ArgumentNullException(nameof(formFile));
            }

            using (var fileStream = new FileStream(filename, FileMode.Create))
            {
                var inputStream = formFile.OpenReadStream();
                await inputStream.CopyToAsync(fileStream, DefaultBufferSize, cancellationToken);
            }
        }
    }
}

The upload method also uses a service filter to validate the mime type. The ValidateMimeMultipartContentFilter class implements the ActionFilterAttribute which provides virtual methods which can be overridden. This attribute throws an exception, if the mime type is incorrect. A file upload requires a multipart content type.

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace AspNet5FileUploadFileTable
{

    public class ValidateMimeMultipartContentFilter : ActionFilterAttribute
    {
        private readonly ILogger _logger;

        public ValidateMimeMultipartContentFilter(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger("ctor ValidateMimeMultipartContentFilter");
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!IsMultipartContentType(context.HttpContext.Request.ContentType))
            {
                context.Result = new StatusCodeResult(415);
                return;
            }

            base.OnActionExecuting(context);
        }

        private static bool IsMultipartContentType(string contentType)
        {
            return !string.IsNullOrEmpty(contentType) && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
        }
    }
}

All the required application configurations are implemented in the Startup class. Entity Framework, configuration, attribute, and class dependencies are defined here.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using AspNet5FileUploadFileTable.Controllers;
using DataAccess;
using Microsoft.EntityFrameworkCore;
using System.IO;

namespace AspNet5FileUploadFileTable
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("config.json", optional: true, reloadOnChange: true);

            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; set; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<ApplicationConfiguration>( Configuration.GetSection("ApplicationConfiguration"));

            var sqlConnectionString = Configuration["ApplicationConfiguration:SQLConnectionString"];

            services.AddDbContext<FileContext>(options =>
                options.UseSqlServer(
                    sqlConnectionString,
                    b => b.MigrationsAssembly("AspNet5FileUploadFileTable")
                )
            );
            
            services.AddMvc();

            services.AddScoped<IFileRepository, FileRepository>();
            services.AddScoped<ValidateMimeMultipartContentFilter>();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();
            loggerFactory.AddDebug();

            app.UseStaticFiles();

            app.UseMvc();
        }

        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

The razor view implements a HTML form which is used to upload the files. The form attributes enctype and method are important for file upload, these should be defined as follows: enctype=”multipart/form-data” method=”post”

<!doctype html>
<html>
    <head>
        <title>Test</title>
    </head>
    <body>
        <form enctype="multipart/form-data" method="post" action="http://localhost:20828/api/test/files" id="ajaxUploadForm" novalidate="novalidate">

            <fieldset>
                <legend style="padding-top: 10px; padding-bottom: 10px;">Uploaded Form</legend>

                <div class="col-xs-12" style="padding: 10px;">
                    <div class="col-xs-4">
                        <label>Description</label>
                    </div>
                    <div class="col-xs-7">
                        <textarea rows="2" placeholder="Description" class="form-control" name="description" id="description"></textarea>
                    </div>
                </div>

                <div class="col-xs-12" style="padding: 10px;">
                    <div class="col-xs-4">
                        <label>Upload</label>
                    </div>
                    <div class="col-xs-7">
                        <input type="file" id="fileInput" name="file" multiple>
                    </div>
                </div>

                <div class="col-xs-12" style="padding: 10px;">
                    <div class="col-xs-4">
                        <input type="submit" value="Upload" id="ajaxUploadButton" class="btn">
                    </div>
                    <div class="col-xs-7">

                    </div>
                </div>

            </fieldset>

        </form>
    </body>
</html>

Testing the application

The application displays all the existing files which where uploaded when started.
aspnet5upload_01

If clicked, the file can be downloaded:

http://localhost:20828/api/test/download/{id}

The files can be uploaded as follows:
aspnet5upload_02

Conclusion

The application is relatively easy to implement and is simple to understand. This is a big improvement compared to the same application implemented in .NET 4.5 with Web API. One problem which exists is the configuration when using separate projects. The paths depends on where the project is run. The config file can be saved to a common path to all the projects and the application will work. (Might be a problem with deployment.)

Links:

http://dotnetthoughts.net/file-upload-in-asp-net-5-and-mvc-6/

http://www.mikesdotnetting.com/article/288/asp-net-5-uploading-files-with-asp-net-mvc-6

Web API File Upload with MS SQL SERVER FileTable

http://senvichet.com/how-to-upload-file-from-web-form-in-asp-net-5-mvc-6/

8 comments

  1. […] ASP.NET 5 MVC 6 File Upload with MS SQL SERVER FileTable // Software Engineering […]

  2. […] ASP.NET 5 MVC 6 file upload with Sql Server FileTable by Damien Bod […]

  3. […] ASP.NET Core 1.0 MVC 6 file upload with Sql Server FileTable : Damien Bod 는 ASP.NET Core 1.0 MVC 6 기반의 환경에서 SQL 서버의 파일 테이블에 직접 파일을 올리는 방법을 설명합니다. […]

  4. xavier Watson · · Reply

    upon trying to run your solution I’m getting the following error: System.Data.SqlClient.SqlException was unhandled by user code
    Class=16
    HResult=-2146232060
    LineNumber=1
    Message=Invalid object name ‘FileDescriptions’.
    Number=208
    Procedure=””
    Server=DESKTOP-NT3CUN7\XWDESIGNS
    Source=Core .Net SqlClient Data Provider
    State=1
    StackTrace:
    at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
    at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
    at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
    at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
    at System.Data.SqlClient.SqlDataReader.get_MetaData()
    at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
    at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
    at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
    at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, String executeMethod, IReadOnlyDictionary`2 parameterValues, Boolean openConnection, Boolean closeConnection)
    at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues, Boolean manageConnection)
    at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable.Enumerator.MoveNext()
    at Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.d__3`1.MoveNext()
    at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
    at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
    at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
    at AspNet5FileUploadFileTable.Controllers.FileClientController.ViewAllFiles() in C:\Users\Xavier\Documents\Visual Studio 2015\Projects\AspNet5FileUploadFileTable-master\src\AspNet5FileUploadFileTable\Controllers\FileClientController.cs:line 47
    at lambda_method(Closure , Object , Object[] )
    at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__28.MoveNext()
    InnerException:

  5. Does the FileDescriptions table exist in your Database?

    Greetings Damien

    1. xavier Watson · · Reply

      No it wasn’t. That was the issue after making the migration and committing it to the database everything worked

  6. Martha Dibenedetto · · Reply

    Good comments ! I was fascinated by the info – Does someone know if I would be able to find a template Form A version to fill in ?

  7. Hi Damien,

    Great article! I would love another demo by you implementing this with the jQuery File Upload plugin. https://blueimp.github.io/jQuery-File-Upload/angularjs.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: