Moving information from a controller to a view - c#

I need to move data read from a database from a controller to a view and display it to a table. I can read from the database a store information to an object successfully, but I'm getting a #foreach (var item in Model) error when I run the page. Here is the method that reads the database:
public class DatabaseRead
{
public static async Task MongoReader(string path)
{
{
MongoClient client = new MongoClient();
var db = client.GetDatabase("POWA");
var collection = db.GetCollection<files>("Imported");
var filter = Builders<files>.Filter.Eq("quote_number", path);
var result = await collection.Find(filter).ToListAsync();
foreach (var results in result)
{
ContentDisplay read = new ContentDisplay();
read.product_name = results.product_name;
read.catalog_number = results.catalog_number;
}
}
}
}
and here is my view:
#model List<ProductionOrderWebApp.Controllers.ContentDisplay>
#{ ViewBag.Title = "Display"; }
<h2>Order Table>
<table board="1", style ="width:auto">
<tr>
<th>Item Name</th>
<th>Catalog Number</th>
</tr>
#foreach (var item in Model)
{
<tr>
<th>#Html.Display(item.product_name);</th>
</tr>
}
How can I get the table to display all the entries of the object without getting the error?
EDIT
Here is the code for my controller. All it does is call two methods, one to read a CSV file and write to mongo, and one that reads from the database and attempts to display the contents.
namespace ProductionOrderWebApp.Controllers
pubic class Homecontroller : Controller
{
public ActionResult Index
{
return View();
}
public ActionResult Display()
{
return View();
}
[HttpPost]
public async Task<ActionResult> Index(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var fileName = System.IO.Path.GetFileName(file.FileName);
var path = System.IO.Path.Combine(("C:\\Dev\\ProductionOrderWebApp\\Uploads"), fileName);
file.SaveAs(path);
await CSVRead.CSVReader(path); //calls a method that reads and takes apart a CSV file
await DatabaseRead.MongoReader(path);
}
return View("Display");
}
}
}

Why are you creating a new object of ContentDisplay everytime in the loop, Because of it, you are not passing the required data to the view,
{
ContentDisplay read = new ContentDisplay();
read.product_name = results.product_name;
read.catalog_number = results.catalog_number;
}

First, DatabaseRead.MongoReader needs to return a Task<List<ContentDisplay>> instead of nothing. In that method, you need to declare your return list outside the foreach loop and add to it in there and return like
var returnList = new List<ContentDisplay>();
foreach (var results in result)
{
var read = new ContentDisplay();
read.product_name = results.product_name;
read.catalog_number = results.catalog_number;
returnList.add(read);
}
return returnList;
Then instead of
await DatabaseRead.MongoReader(path);
you need to actually catch it to use
var model = await DatabaseRead.MongoReader(path);
and then pass it along to the view
return View("Display", model);
CAVEATS
I've not done a lot of async await stuff, so there might be something screwy with returning lists.
I'm concerned that await CSVRead.CSVReader(path); is probably also doing nothing while you expect it to do something.
EDIT
public async Task<ActionResult> Index(HttpPostedFileBase file)
{
List<ContentDisplay> model = new List<ContentDisplay>();
if (file != null && file.ContentLength > 0)
{
var fileName = System.IO.Path.GetFileName(file.FileName);
var path = System.IO.Path.Combine(("C:\\Dev\\ProductionOrderWebApp\\Uploads"), fileName);
file.SaveAs(path);
await CSVRead.CSVReader(path); //calls a method that reads and takes apart a CSV file
model = await DatabaseRead.MongoReader(path);
}
return View("Display", model);
}

Related

How to get the last file in an array

I have this code to download a file in the database, but it only downloads the first user upload.
I tried using LastOrDefault and reverse to download the most recent file but I couldn't.
Does anyone have any idea how to do it?
[HttpGet]
[Route("~/DownloadFileCNPJ/{Id}")]
public async Task<IActionResult> DownloadFileCNPJ(long Id)
{
using (var context = new MyDbContext())
{
var cartaoCnpj = (from j in context.CartaoCNPJCedentes
where j.CedenteId.Equals(Id)
select j).Reverse().FirstOrDefault();
if (cartaoCnpj != null)
{
var content = new System.IO.MemoryStream(cartaoCnpj.CartaoCNPJ);
var contentType = cartaoCnpj.TipoCartãoCNPJ;
var fileName = cartaoCnpj.NomeArquivoCNPJ;
return File(content, contentType, fileName);
}
return null;
}
}

Display image in front end in ASP.NET MVC

In ASP.NET MVC, the image path is stored in the database and server folder (image) but not displayed on the front.
Images are shown like this:
I was trying to display the image on the front in ASP.NET MVC.
I also attached the code of the controller and view to display the image using the Entity Framework model
In my controller method:
[HttpPost]
public ActionResult Create(Personaltable p, Personal u)
{
try
{
// TODO: Add insert logic here
string filename = Path.GetFileNameWithoutExtension(u.AttachPicture.FileName);
string extension = Path.GetExtension(u.AttachPicture.FileName);
filename = filename + DateTime.Now.ToString("yymmssfff") + extension;
u.ImagePath = "~/Image/" + filename;
filename = Path.Combine(Server.MapPath("~/Image/"), filename);
u.AttachPicture.SaveAs(filename);
using (PersonaltableEntities entity = new PersonaltableEntities())
{
var t = new Personaltable()//Make Variable of Table
{
AttachPicture=SaveToPhysicalLocation(u.AttachPicture),
LastPayCertificate = SaveToPhysicalLocation(u.LastPayCertificate)
};
db.Personaltables.Add(t);
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch()
{}
}
private string SaveToPhysicalLocation(HttpPostedFileBase file)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
file.SaveAs(path);
return path;
}
return string.Empty;
}
This is the view Controller on which I only show the image uploaded by the user in a list form.
public ActionResult Index()
{
return View(db.Personaltables.ToList());
}
This is my view:
#foreach (var item in Model)
{
<tr> <td><img src="~/Image/" width="100",height="250"/></td></td>
}
You must write src="~/Image/#item.ImagePath"
#foreach (var item in Model)
{
<tr> <td><img src="~/Image/(????)" width="100",height="250"/></td></td>
}

IFormFile copy to memorystream ObjectDisposedException

I'm currently using tinyMCE to create some news post for a website.
I need to be able to upload images, however i've hit a stopblock.
When I hit my controller I get an
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'FileBufferingReadStream'.
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ThrowIfDisposed()
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.set_Position(Int64 value)
at Microsoft.AspNetCore.Http.Internal.ReferenceReadStream..ctor(Stream inner, Int64 offset, Int64 length)
at Microsoft.AspNetCore.Http.Internal.FormFile.OpenReadStream()
at Microsoft.AspNetCore.Http.Internal.FormFile.CopyToAsync(Stream target, CancellationToken cancellationToken)
at HardwareOnlineDk.Web.Areas.Admin.Controllers.ImageController.Upload(IFormFile inputFile) in D:\Kode\HardwareOnlineRider\HOL\SourceCode\Main\Web\Areas\Admin\Controllers\ImageController.cs:line 139
My code looks like this:
[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload(IFormFile inputFile)
{
try
{
var filesCount = Request.Form.Files.Count;
if (filesCount == 0)
return BadRequest("Ingen fil fundet");
// Get HTTP posted file based on the fieldname.
var file = Request.Form.Files.GetFile("file");
if (file == null)
return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");
// Check if the file is valid.
if (!Check(file.FileName, file.ContentType))
return BadRequest("Fil ikke gyldig");
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
var medie = new Medie
{
Name = file.FileName.Trim('\"'),
ParentId = _imageService.TempFolderGuid,
ContentLength = file.Length,
Content = memoryStream.ToArray()
};
try
{
var imageId = await _imageService.Medier_InsertMedie(medie);
//TODO Her skal vi gemme ImageId i Session
return Json(new
{
location = $"/api/media/{imageId.Id}.jpg"
});
}
catch
{
return BadRequest("Kunne ikke gemme billede");
}
}
catch
{
return StatusCode(500);
}
}
And the Check Method if needed
private static bool Check(string filePath, string mimeType)
{
return AllowedImageExts.Contains(GetFileExtension(filePath)) &&
AllowedImageMimetypes.Contains(mimeType.ToLower());
}
The code fails when i'm doing:
await file.CopyToAsync(memoryStream)
Can anyone help me here. I'm lost.
UPDATE 1
I just tried to fix it with the suggested answer, so my code now looks like this:
[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload([FromForm]IFormFile file)
{
try
{
if (file == null)
return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");
var memoryStream = new MemoryStream();
await file.CopyToAsync(memoryStream);
var filesCount = Request.Form.Files.Count;
if (!Request.ContentType.StartsWith(MultipartContentType))
return BadRequest("Contenttype er ikke korrekt");
if (filesCount == 0)
return BadRequest("Ingen fil fundet");
// Get HTTP posted file based on the fieldname.
// Check if the file is valid.
if (!Check(file.FileName, file.ContentType))
return BadRequest("Fil ikke gyldig");
var medie = new Medie
{
Name = file.FileName.Trim('\"'),
ParentId = _imageService.TempFolderGuid,
ContentLength = file.Length,
Content = memoryStream.ToArray()
};
try
{
var imageId = await _imageService.Medier_InsertMedie(medie);
//TODO Her skal vi gemme ImageId i Session
return Json(new
{
location = $"/api/media/{imageId.Id}.jpg"
});
}
catch
{
return BadRequest("Kunne ikke gemme billede");
}
}
catch
{
return StatusCode(500);
}
}
The input parameter is no longer null, but it still throws the same exception
You indicated that inputFile is always null.
Binding matches form files by name.
Reference Upload files in ASP.NET Core
Based on
// Get HTTP posted file based on the fieldname.
var file = Request.Form.Files.GetFile("file");
the name of the posted field is "file"
rename the action parameter to match and explicitly state when to bind the data using [FromForm]
[HttpPost, ActionName("Upload")]
public async Task<IActionResult> Upload([FromForm]IFormFile file) {
try {
if (file == null)
return BadRequest("Fejlkonfiguration: Filnavn ikke korrekt");
// Check if the file is valid.
if (!Check(file.Name, file.ContentType))
return BadRequest("Fil ikke gyldig");
var medie = new Medie {
Name = file.Name.Trim('\"'),
ParentId = _imageService.TempFolderGuid
};
var fileStream = file.OpenStreamRead();
using (var memoryStream = new MemoryStream()) {
await fileStream.CopyToAsync(memoryStream);
medie.Content = memoryStream.ToArray();
medie.ContentLength = memoryStream.Length,
}
try {
var imageId = await _imageService.Medier_InsertMedie(medie);
//TODO Her skal vi gemme ImageId i Session
return Json(new {
location = $"/api/media/{imageId.Id}.jpg"
});
} catch {
return BadRequest("Kunne ikke gemme billede");
}
} catch {
return StatusCode(500);
}
}
I feel like I should post how I solved it
The issue was that I have a log in middleware, that reads the httpcontext.
Using the answer found here: How can I read http request body in netcore 3 more than once?
solved the issue for me.
you can pass additional parameters with a class with a IFormFile element. pass the byte data to a stored procure as a byte[] array. where the FileData field is a varbinary parameter in the stored procedure.
public class FormData
{
public string FileName { get; set; }
public string FileDescription { get; set; }
public IFormFile file { get; set; }
}
controller end point
public async Task<IActionResult> UploadDocument([FromForm] FormData formData)
{
if (formData.file.Length > 0)
{
using (MemoryStream ms = new MemoryStream())
{
await formData.file.CopyToAsync(ms);
data = ms.ToArray();
await _repository.spProcedure(FileData: data);
}
}

Read Text file without copying to hard disk

I'm using Asp.Net Core 3.0 and I find myself in a situation where the client will pass text file(s) to my API, the API will then parse the text files into a data model using a function that I have created called ParseDataToModel(), and then store that data model into a database using Entity Framework. Since my code is parsing the files into a data model, I really don't need to copy it to the hard disk if it isn't necessary. I don't have a ton of knowledge when it comes to Streams, and I've googled quite a bit, but I was wondering if there is a way to retrieve the string data of the uploaded files without actually copying them to the hard drive? It seems like a needless extra step.... Below is my code for the file Upload and insertion into the database:
[HttpPost("upload"), DisableRequestSizeLimit]
public IActionResult Upload()
{
var filePaths = new List<string>();
foreach(var formFile in Request.Form.Files)
{
if(formFile.Length > 0)
{
var filePath = Path.GetTempFileName();
filePaths.Add(filePath);
using(var stream = new FileStream(filePath, FileMode.Create))
{
formFile.CopyTo(stream);
}
}
}
BaiFiles lastFile = null;
foreach(string s in filePaths)
{
string contents = System.IO.File.ReadAllText(s);
BaiFiles fileToCreate = ParseFileToModel(contents);
if (fileToCreate == null)
return BadRequest(ModelState);
var file = _fileRepository.GetFiles().Where(t => t.FileId == fileToCreate.FileId).FirstOrDefault();
if (file != null)
{
ModelState.AddModelError("", $"File with id {fileToCreate.FileId} already exists");
return StatusCode(422, ModelState);
}
if (!ModelState.IsValid)
return BadRequest();
if (!_fileRepository.CreateFile(fileToCreate))
{
ModelState.AddModelError("", $"Something went wrong saving file with id {fileToCreate.FileId}");
return StatusCode(500, ModelState);
}
lastFile = fileToCreate;
}
return CreatedAtRoute("GetFile", new { fileId = lastFile.FileId }, lastFile);
}
It would be nice to just hold all of the data in memory instead of copying them to the hard drive, just to turn around and open it to read the text.... I apologize if this isn't possible, or if this question has been asked before. I'm sure it has, and I just wasn't googling the correct keywords. Otherwise, I could be wrong and it is already doing exactly what I want - but System.IO.File.ReadAllText() makes me feel it's being copied to a temp directory somewhere.
After using John's answer below, here is the revised code for anyone interested:
[HttpPost("upload"), DisableRequestSizeLimit]
public IActionResult Upload()
{
var filePaths = new List<string>();
BaiFiles lastFile = null;
foreach (var formFile in Request.Form.Files)
{
if (formFile.Length > 0)
{
using (var stream = formFile.OpenReadStream())
{
using (var sr = new StreamReader(stream))
{
string contents = sr.ReadToEnd();
BaiFiles fileToCreate = ParseFileToModel(contents);
if (fileToCreate == null)
return BadRequest(ModelState);
var file = _fileRepository.GetFiles().Where(t => t.FileId == fileToCreate.FileId).FirstOrDefault();
if (file != null)
{
ModelState.AddModelError("", $"File with id {fileToCreate.FileId} already exists");
return StatusCode(422, ModelState);
}
if (!ModelState.IsValid)
return BadRequest();
if (!_fileRepository.CreateFile(fileToCreate))
{
ModelState.AddModelError("", $"Something went wrong saving file with id {fileToCreate.FileId}");
return StatusCode(500, ModelState);
}
lastFile = fileToCreate;
}
}
}
}
if(lastFile == null)
return NoContent();
else
return CreatedAtRoute("GetFile", new { fileId = lastFile.FileId }, lastFile);
}
System.IO.File.ReadAllText(filePath) is a convenience method. It essentially does this:
string text = null;
using (var stream = FileStream.OpenRead(filePath))
using (var reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}
FormFile implements an OpenReadStream method, so you can simply use this in place of stream in the above:
string text = null;
using (var stream = formFile.OpenReadStream())
using (var reader = new StreamReader(stream))
{
text = reader.ReadToEnd();
}

How to get TempData in an integration test

I have a controller with the following actions:
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
[Route(
"/MyShop/OrderDetails/CancelOrder",
Name = UrlRouteDefinitions.MyShopOrderDetailsCancelOrder)]
[ValidateAntiForgeryToken]
public IActionResult CancelOrder(MyViewModel viewModel)
{
var isCancelSuccessful = _orderBusinessLogic.CancelOrderById(viewModel.Order.Id);
if (isCancelSuccessful)
{
//to show a success-message after the redirect
this.TempData["SuccessCancelOrder"] = true;
}
return RedirectToRoute(UrlRouteDefinitions.MyShopOrderDetailsIndex, new
{
orderId = viewModel.Order.Id
});
}
Then I also have the following piece of HTML in the View for the Controller mentioned above:
<div class="panel-body">
#if (TempData["SuccessCancelOrder"] != null)
{
//show the message
#TempData["SuccessCancelOrder"].ToString();
}
</div>
Now I'm writing an integration test (code-summary below) to check if the CancelOrder() method works as expected. There I'd like to access the value of the TempData dictionary to check its correctness.
[TestMethod]
public void MyTest()
{
try
{
//Arrange: create an Order with some Products
var orderId = CreateFakeOrder();
//Open the Order Details page for the arranged Order
var httpRequestMessage = base.PrepareRequest(
HttpMethod.Get,
"/MyShop/OrderDetails?orderId=" + orderId,
MediaTypeEnum.Html);
var httpResponse = base.Proxy.SendAsync(httpRequestMessage).Result;
var responseContent = httpResponse.Content.ReadAsStringAsync().Result;
var viewModel = base.GetModel<MyViewModel>(responseContent);
Assert.AreEqual(HttpStatusCode.OK, httpResponse.StatusCode);
//Try to execute a POST to cancel the Order
httpRequestMessage = base.PrepareRequest(
HttpMethod.Post,
"/MyShop/OrderDetails/CancelOrder",
MediaTypeEnum.Html,
httpResponse, content: viewModel);
httpResponse = base.Proxy.SendAsync(httpRequestMessage).Result;
var expectedRedirectLocation = $"{this.RelativeHomeUrl}/MyShop/OrderDetails?orderId=" + orderId;
var receivedRedirectLocation = WebUtility.UrlDecode(httpResponse.Headers.Location.ToString());
//we expect that the Order Details page will be reloaded
//with the ID of the already cancelled Order
Assert.AreEqual(HttpStatusCode.Redirect, httpResponse.StatusCode);
//-----------------------------------------------------------
//here I'm going to re-execute a GET-request
//on the Order Details page.
//
//Then I need to check the content of the message
//that's been written in the TempData
//-----------------------------------------------------------
}
finally
{
//delete the arranged data
}
}
In any case, I don't know how to access it from my integration test. Does anybody know how to do it and if there's a way at all?
Controller.TempData is public so you can easily access it and check if your key/value exists
[TestMethod]
public void TempData_Should_Contain_Message() {
// Arrange
var httpContext = new DefaultHttpContext();
var tempData = new TempDataDictionary(httpContext, Mock.Of<ITempDataProvider>());
var controller = new TestController();
controller.TempData = tempData;
// Act
var result = controller.DoSomething();
//Assert
controller.TempData["Message"]
.Should().NotBeNull()
.And.BeEquivalentTo("Hello World");
}

Categories

Resources