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

This article shows how to upload and download files in ASP.NET Core 1.1 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

2017.01.07: Updated to VS2017 csproj
02.07.2016: Updated to ASP.NET Core RTM
18.05.2016: Updated to ASP.NET Core RC2
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 csproj file in your project. The EF7 dependencies need to be added and also the ef commands.

Note: at present the EF Core migrations do not work with msbuild. Due to this, you need to use the old project.json file as well as the global.json to do the migrations from the command line using dotnet.

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <AssemblyName>DataAccess</AssemblyName>
    <OutputType>Library</OutputType>
    <PackageTargetFallback Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
    <GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
    <GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
    <GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="**\*.cs" />
    <EmbeddedResource Include="**\*.resx" />
    <EmbeddedResource Include="compiler\resources\**\*" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="NETStandard.Library" Version="1.6.1" />
    <PackageReference Include="Microsoft.NETCore.App" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.0.0-msbuild1-final" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink.Loader" Version="1.1.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.0.0-msbuild1-final" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.1.0-preview4-final" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0-msbuild1-final" />
  </ItemGroup>
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
  </PropertyGroup>
</Project>  

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 ASP.NET Core MVC 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.

Note at present the EF Core migrations do not work with msbuild. Due to this, you need to use the old project.json file as well as the global.json to do the migrations from the command line using dotnet.

<Project Sdk="Microsoft.NET.Sdk.Web" ToolsVersion="15.0">
  <PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <AssemblyName>AspNetCoreFileUploadFileTable</AssemblyName>
    <OutputType>Exe</OutputType>
    <PackageTargetFallback Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
  </PropertyGroup>
  <ItemGroup>
    <Content Include="wwwroot\**\*;**\*.cshtml;appsettings.json;web.config">
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </Content>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\DataAccess\DataAccess.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.App" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Routing" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.0.0-msbuild1-final" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink.Loader" Version="14.1.0" />
    <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.0.0-msbuild1-final" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.1.0-preview4-final" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0-msbuild1-final" />
  </ItemGroup>
   <ItemGroup>
    <Folder Include="Migrations\" />
  </ItemGroup>
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
  </PropertyGroup>
  <Target Name="PostpublishScript" AfterTargets="Publish" Condition=" '$(IsCrossTargetingBuild)' != 'true' " />
</Project>     

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

https://damienbod.com/2014/04/08/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/

Advertisements

23 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

    1. nice, thanks

  8. Thanks for the example!

    Anyway, you don’t shot the FileResult class in the post, so I was turning crazy looking for, until I donwload the entire project.

  9. I’m using VS2015.
    I get confused at Step 2.
    I installed EF 1.1.0
    I installed sdk “version”: “1.0.0-preview2-003131” and referenced from global.json
    In order to include ‘dependencies’ and add ‘the ef commands’, I:
    Install-Package Microsoft.EntityFrameworkCore.SqlServer
    Install-Package Microsoft.EntityFrameworkCore.Tools -Version 1.1.0-msbuild3-final
    Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
    per https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db
    Q: Am I on the wrong track?

  10. Hi Daniel, in VS2015 you should use the project.json and not the msbuild tooling. “Microsoft.EntityFrameworkCore.Tools”: “1.1.0-preview4-final”,
    and
    “tools”: {
    “Microsoft.AspNetCore.Razor.Tools”: “1.1.0-preview4-final”,
    “Microsoft.EntityFrameworkCore.Tools.DotNet”: “1.1.0-preview4”,

    It is very confusing at the moment because everything is being switched back to msbuild and VS2017 is only in RC and still not bug free or missing features from the project.json.

    Greetings Damien

  11. Thanks Damien, for the quick reply!

    I found those tools already listed in my project.json file, among others.

    In global.json I changed my sdk thus:

    “projects”: [ “src”, “test” ],
    “sdk”: {
    //”version”: “1.0.0-preview2-1-003177”
    “version”: “1.0.0-preview2-003131”

    I added these commands to project.json, per a book I just got:

    “commands”: {
    “web”: “Microsoft.AspNet.Server.Kestrel”,
    “ef”: “EntityFramework.Commands”
    }

    That got the thing to build, at least.

    I created the database tables directly because I couldn’t get migration to work.

    I edited the connection string in config.json

    But when I debug, the project doesn’t start.

    Any suggestions?

    Thanks again!

    1. seems like something is not installed on your PC, check that the VS version is correct and that the command line is working.

      $ dotnet restore
      $ dotnet run
      Does this work?
      Then VS 15.0.0-RC.3+26127.3

  12. Hi Damienbod,

    Thanks again for your help with this.

    I ran dotnet restore successfully.

    But dotnet run failed because I have framework versions 1.0.0 and 1.0.1 installed, but not 1.1.0.

    I thought EF 1.1.0 would require VS 2017. Should I (somehow) install EF 1.1?

    I am using VS Community 2015; 14.0.25431.01 Update 3

    Should I get a later version?

    Thanks again!!

    1. I would install 1.1 because it has a lot of features and fixes with 1.0 does not have. Install it from the .NET Core downloads (Click the LTS button first)

      Greetings Damien

  13. Thanks Damien,

    I went to the downloads page: https://www.microsoft.com/net/download/core
    I saw the LTS ‘Button’, but it was just an AJAX message when I hover over it, it wasn’t a real button. Clicking it did nothing. It only showed a description of that LTS means.

    It looks like I can download .NET Core 1.0.3 SDK – Installer from that page, but not EF 1.1.0.

    Earlier, I did install EF 1.1.0 from the NuGet package manager, but like you said before, that didn’t work.
    https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/

    Do you have a URL for a download page for EF 1.1.0?

    Do I need VS 2017?

    Thanks again!

    1. sorry, you need to click the current button.
      LTS: 1.0.3
      Current 1.1

  14. Woohoo!

    That did it!

    In debug, I was able to step through the code and then see a web page render in the browser!

    This is exciting!!

    Thanks again for your work on this and for your patience with me!!!

    Dan

  15. Wow!!
    And it works!
    It’s hitting my database and doing everything I would hope it would do!
    This is so wonderful!
    Now I just gotta learn how you did this.
    Thanks again *soo* much!!!

  16. Shoot. Spoke too soon.

    The app inserts to the FileDescriptions table and the page renders with a link to the file, but clicking on it breaks. The file has not actually been saved to the WebApiUploads table.

    I am able to insert/retrieve files to/from the WebApiUploads table via T-SQL and via the File Explorer, as you explained. But the app isn’t touching that table.

    I’m looking at \IISExpress\TraceLogFiles\AspNet5FileUploadFileTable for clues.

    Suggestions?

    Thanks!

    Dan

  17. I seem to have found my problem.

    It’s in the FileUploadController, with the line:

    await file.SaveAsAsync(Path.Combine(_optionsApplicationConfiguration.Value.ServerUploadFolder, fileName));

    It’s trying to combine the path to my database table with the full path to the uploaded file, including “C:\…etc.” where clearly we want only the file name and extension.

    .. still looking into the details.

    I’ll be checking back!

    Thanks again!!

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: