ASP.NET MVC, MVVM and file uploads - asp.net-mvc

I'm big fan of the MVVM pattern, in particular while using the ASP.NET MVC Framework (in this case v2 preview 2).
But I'm curious if anyone knows how to use it when doing file uploads?
public class MyViewModel
{
public WhatTypeShouldThisBe MyFileUpload { get; set; }
}

I have a production asp.net mvc site running with a jQuery multi-file uploader that i wrote. This answer has most of the code included in it so that should get you started with uploads in MVC. The question actually asks about storing the uploaded file in a Stream and i use a byte[], but you'll see in the answer how my code can apply to both scenarios.

Usually HttpFileCollection is enough if you are using the standard component (System.IO.Path).
take note that HttpFileCollection is a collection of HttpPostedFile, i.e. you can upload many files at once.

I would think byte[] would be enough to store the uploaded file, for example in my upload action, I would do sth like this:
foreach (string file in Request.Files)
{
HttpPostedFileBase hpf = Request.Files(file) as HttpPostedFileBase;
if (hpf.ContentLength == 0)
{
continue;
}
//This would be the part to get the data and save it to the view
byte[] origImageData = new byte[(int)hpf.ContentLength - 1 + 1];
hpf.InputStream.Read(origImageData, 0, (int)hpf.ContentLength);
}
Hope it helps some how.

Related

ASP.NET: Exception when accessing the request InputStream

In my ASP.NET MVC 5 application I'm trying to access the Request.InputStream property but I get the following exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at Telerik.Web.UI.Upload.RequestParser.MergeArrays(Byte[] array1, Byte[] array2)
at Telerik.Web.UI.Upload.RequestParser.get_FirstBoundary()
at Telerik.Web.UI.Upload.RequestParser..ctor(Byte[] boundary, Encoding encoding, RequestStateStore requestStateStore)
at Telerik.Web.UI.Upload.ProgressWorkerRequest.get_Parser()
at Telerik.Web.UI.Upload.ProgressWorkerRequest.UpdateProgress(Byte[] buffer, Int32 validBytes
at Telerik.Web.UI.Upload.ProgressWorkerRequest.GetPreloadedEntityBody()
at System.Web.HttpRequest.GetEntireRawContent()
at System.Web.HttpRequest.get_InputStream()
As you can see, the exception is thrown by a Telerik component. I'm indeed using Telerik web controls in my project but none of them are related to this controller. The exception occurs even if I generate a request using a tool. Looks to me like Telerik somehow injected this ProgressWorkerRequest object into my HttpRequest.
Any clues on how to get rid of it?
It doesn't have to be related to your controller. It is related to the process that the Telerik uploader is conducting and is not something to "get rid of". Basically, it is telling you that the Telerik uploader process didn't complete because it didn't find what it was expecting to.
Since Telerik controls are generally straightforward to use, you should only need something like this to get the input stream for your file:
public ActionResult UploadFile(IEnumerable<HttpPostedFileBase> fileUploader)
{
if (Request.Files.Count == 1)
{
string fileName = Request.Files[0].FileName;
Stream s = Request.Files[0].InputStream;
int size = Request.Files[0].ContentLength;
byte[] myFile = new byte[length];
s.Read(myFile, 0, size);
// Now, myFile should have the file, in bytes
}
}
If you are not getting the file, I would ensure the application has the permissions via the account it is acting under (either yours, or a service account, if a web application) to the resource you are pointing at.

ModelState.IsValid is false - But Which One - Easy Way

In ASP.NET MVC, when we call a post action with some data, we check ModelState and in case some validation error, it would be falst. For a big Enter User Information form, it is annoying to expand each Value and look at the count to see which Key (9 in attached example image) has validation error. Wondering if someone knows an easy way to figure out which element is causing validation error.
In VS2015+, you can use LINQ in the Immediate Window, which means you can just run the following:
ModelState.SelectMany(
x => x.Value.Errors,
(state, error) => $"{state.Key}: {error.ErrorMessage}"
)
I propose to write a method:
namespace System.Web
{
using Mvc;
public static class ModelStateExtensions
{
public static Tuple<string, string> GetFirstError(this ModelStateDictionary modelState)
{
if (modelState.IsValid)
{
return null;
}
foreach (var key in modelState.Keys)
{
if (modelState[key].Errors.Count != 0)
{
return new Tuple<string, string>(key, modelState[key].Errors[0].ErrorMessage);
}
}
return null;
}
}
}
Then during debugging open Immediate Window and enter:
ModelState.GetFirstError()
Sounds like you're looking for debugger enhancements. I recently came across this product in the visual studio gallery.
http://visualstudiogallery.msdn.microsoft.com/16acdc63-c4f1-43a7-866a-67ff7022a0ac
I have no affiliation with them, and haven't used it. It's also a trial version and have no idea how much it costs for the full thing.
If you're more focused on the debugger side of things, have a go with the trial copy of OzCode. It enhances the Visual Studio IDE by replacing the usual debugging tooltip with it's own, more powerful, debugging tooltip. It's hard to epxlain with words, check out their website, they have a gallery of features on there.
I've been playing around with the beta for a few weeks, and it's proved a very valuable tool. You can query against data in the debugger using OzCode. For example, you could query items in the ModelState by filtering against the Values collection.

How to write to Excel using MVC and EF Code First - Best Practices

Vs'12 asp.net C# MVC, Internet Application Template, Kendo UI, EF Code First
What I am trying to do is use all my dataModels together and Build an Excel file, nicely formatted and all that good stuff - placing certain data into certain fields.These files would be fairly complex, - adding and checking checkboxes In Excel, creating boxes around fields other formatting
Now from what i can tell were exporting data from HTML into excel, however this is not something i would want, the only other way I can think of is to run C# code from a controller through a class, and then make it downloadable / and or export it?
Is this a "Best Practice" and if not what is?
You need to create your Excel to a byte[] or a Stream using a library like EPPlus. Then you can serve this using the FileActionResult:
public ActionResult CreateExcel()
{
var data=_repository.GetExcelData();
byte[] excel=CreateExcelAsByteArray(data);
return File(excel, "application/vnd.ms-excel", "excel.xlsx");
}
With this method you can serve a dinamically created Excel file.
Update: To create the excel file you can use EPPlus:
public byte[] CreateExcelAsByteArray(IEnumerable<int> elems)
{
using(var ms=new MemoryStream())
using(var package=new ExcelPackage())
{
var ws = package.Workbook.Worksheets.Add("worksheet");
int i=1;
foreach(var elem in elems)
{
ws.Cells("A"+i).Value=elem;
i++;
}
package.SaveAs(ms);
return ms.ToArray();
}
}

How to set up a multitenancy application using ASP.NET MVC?

A multitenancy application is an app that is shared by multiple organizations (medical practices, law offices..) and each organization, in turn, has it's own users. They all log on a centralized environment.
To be identified within the application, the organization must be expressed in the URL. There are two major URL forms for that. Subdomains and folders:
[tenancy_name].appname.com/projects/view/123
www.appname.com/[tenancy_name]/projects/view/123
At first I tried the second because this solution does not involve dealing with DNSs. But then the problem: Everytime the developer needs to express an url (#Html.Action or #Url.Action) it has to explicitly pass the [tenancy_name]. This adds an unwanted overhead to the development. A possible workaround would be to implement custom versions of these HTML helpers that automatically take into account the tenancy name. I'm considering this option but looking for something more straitghtforward. I also realized ASP.NET MVC automatically passes route values for outgoing URLs but only when the controller and action are the same as the current. It would be nice if route values were always passed.
To implement the first option, the subdomain one, I think, I would need some third party DNS manager. I heard of DynDNS and took a look at it but I thought it unclear how they work just looking at their site. Would I need to trigger a web-service to tell them to create another subdomain everytime a new tenancy is created? Do they support wildcards in the DNS? Do they work on Windows Azure or shared hostings?
I'm here looking for directions. Which way should I go?
look this project on codeplex, the "baseRoute" maybe can help you.
http://mvccoderouting.codeplex.com/
Regards.
Following made View resolution trivial in our app:
How to use:
For views that you need to overload for a particular tenant - treat them same way as custom display modes:
Following will work:
Index.cshtml
Index.cust2.mobile.cshtml
or
Partials/CustomerAgreement.cust1.cshtml
Partials/CustomerAgreement.cust2.cshtml
as far as I remember display/editor templates also work same way
Known issues:
1. You have to create Layouts for all combinations of primary+secondary (for whatever MVC-reason)
2. Regardless of what resharper is saying about its support of display modes - it does not support "." as part of the display mode name (here's an issue to track progress http://youtrack.jetbrains.com/issue/RSRP-422413)
//put in application start --------
DisplayModeProvider.Instance.Modes.Clear();
foreach (var displayMode in GetDisplayModes())
{
DisplayModeProvider.Instance.Modes.Add(displayMode);
}
private IEnumerable<IDisplayMode> GetDisplayModes()
{
return new CompoundDisplayModeBuilder()
.AddPrimaryFilter(_ => dependencyResolver.GetService(typeof(IResolveCustomerFromUrl)).GetName(),
"cust1",
"cust2")
.AddSecondaryFilter(ctx => ctx.Request.Browser.IsMobileDevice, "mobile")
.BuildDisplayModes();
}
//end of application start part
//and the mode builder implementation:
public class CompoundDisplayModeBuilder
{
private readonly IList<DefaultDisplayMode> _primaryDisplayModes = new List<DefaultDisplayMode>();
private readonly IList<DefaultDisplayMode> _secondaryDisplayModes = new List<DefaultDisplayMode>();
//NOTE: this is just a helper method to make it easier to specify multiple tenants in 1 line in global asax
//You can as well remove it and add all tenants one by one, especially if resolution delegates are different
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, string> contextEval, params string[] valuesAsSuffixes)
{
foreach (var suffix in valuesAsSuffixes)
{
var val = suffix;
AddPrimaryFilter(ctx => string.Equals(contextEval(ctx), val, StringComparison.InvariantCultureIgnoreCase), val);
}
return this;
}
public CompoundDisplayModeBuilder AddPrimaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_primaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public CompoundDisplayModeBuilder AddSecondaryFilter(Func<HttpContextBase, bool> contextCondition, string suffix)
{
_secondaryDisplayModes.Add(new DefaultDisplayMode(suffix) { ContextCondition = contextCondition });
return this;
}
public IEnumerable<IDisplayMode> BuildDisplayModes()
{
foreach (var primaryMode in _primaryDisplayModes)
{
var primaryCondition = primaryMode.ContextCondition;
foreach (var secondaryMode in _secondaryDisplayModes)
{
var secondaryCondition = secondaryMode.ContextCondition;
yield return new DefaultDisplayMode(primaryMode.DisplayModeId + "." + secondaryMode.DisplayModeId){
ContextCondition = ctx => primaryCondition(ctx) && secondaryCondition(ctx)
};
}
}
foreach (var primaryFilter in _primaryDisplayModes)
{
yield return primaryFilter;
}
foreach (var secondaryFilter in _secondaryDisplayModes)
{
yield return secondaryFilter;
}
yield return new DefaultDisplayMode();
}
}

Long running process that will return a file

I'm using ASP.NET MVC and have a long running process. Specifically I am generating a large PDF for the user to download.
I understand the basic concept:
Action method gets called
New thread started to generate process
Return a View that tells the user the (pdf) is being generated
Use AJAX to call the server and ask for progress
Once finished, present the file to the user for download.
The parts I don't fully understand are:
The management of the thread across separate AJAX calls. I will possibly need some way of finding the running thread and requesting a status. Is there a static context I can keep a reference to the thread in? I'm aware of the Data Caching in HttpContext.Application, would that be suitable for this?
And how to present the completed file. Do I create a temp file and present a download link? Or can I make a final AJAX call that returns the file?
It works!
Here's what I've done:
Step 1 & 2 - Action Method gets called, long running thread is started
When my action method gets called, it generates a unique ID. I then instantiate an instance of my PdfGenerator class, create a new thread that calls PdfGenerator.Generate and start it.
public class PdfGenerator
{
public string State;
public byte[] Data;
public void Generate()
{
// Generate PDF/Long running process
// Should update State as it goes
// ...
// Once finished, Data is populated with the binary byte[]
}
}
Once the thread has started (or before starting) the generator instance is stored in the cache:
HttpContext.Cache[guid] = generator;
I also attach the guid to the ViewData so that it can be reference in my view script.
Step 3 & 4 - Display and update status/progress view
Now that the thread is running and PDF generation has begun, I can display my progress view script. Using jQuery's $.getJSON I am able to poll a separate Action to find the status of the generation:
[OutputCache(Duration = 0, VaryByName = "none", NoStore = true)]
public JsonResult CheckPdfGenerationStatus(string guid)
{
// Get the generator from cache
var generator = HttpContext.Cache[guid] as PdfGenerator;
if (generator == null)
return Json(null);
else
return Json(generator.State);
}
My view script interprets the Json and displays the appropriate progress information.
Step 5 - Present file to user
Once the generation is completed, the generators state is set accordingly and when jQuery receives this information, it can either make available a link, or directly send the file using javascripts location.href.
The Action method that sets up and returns the file simply gets the generator out of the cache and returns the attached byte[]
public ContentResult DownloadPdf(string guid)
{
var generator = HttpContext.Cache[guid] as PdfGenerator;
if (generator == null)
return Content("Error");
if (generator.State == "Completed")
{
return Content(generator.Data);
}
else
{
return Content("Not finished yet");
}
}
My my actual work I've got more detailed state such as Initialised, Running and Completed. As well as a progress percentage (expressed as a decimal, 1.0 being complete).
So yeah, hope that helps anyone else trying to do something similar.
The Cashe is very well suitable for that. Only one thing is to make sure the item cached is never removed while the process is running (You can use ItemPriority.NotRemovable for that).
You can save the file on disk in a temp folder or you can keep it in cache for some time (it depends).
I personally don' like to pollute hard disk with files so I would keep the file in the cache (with MediumPriority for a couple of minutes). But if the file is large and can be generated often consider using a Database of file system instead.
On the client, when the last Ajax request returns result( can look like {progress: "100%", resultUrl: "http://your.url/Where/ToGet/TheFile.aspx?file=GUID-OR-CACHE-KEY"} ) you can redirect the browser to a URL provided.
It, in turn, will render that file as a binary result.
Client redirect can be done using Javascript like this:
location.href = response.resultUrl;
BTW, how do you generate PDF? NFOP?

Resources