Incorrect upload multiple images in Edit method - c#

I have method Edit that uploads one image for Main Page and multiple images for gallery to the existing record in database. I have one to many relationship table (FurnitureImages where I store info about image) , also I use View Model
So here my code
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(FurnitureVM model)
{
if (model.MainFile != null && model.MainFile.ContentLength > 0)
{
string displayName = model.MainFile.FileName;
string extension = Path.GetExtension(displayName);
string fileName = string.Format("{0}{1}", Guid.NewGuid(), extension);
string path = "~/Upload/" + fileName;
model.MainFile.SaveAs(Server.MapPath( path));
model.MainImage = new ImageVM() { Path = path, DisplayName = displayName };
}
foreach (HttpPostedFileBase file in model.SecondaryFiles)
{
FurnitureImages images = new FurnitureImages();
if (file != null && file.ContentLength > 0)
{
string displayName = file.FileName;
string extension = Path.GetExtension(displayName);
string fileName = string.Format("{0}{1}", Guid.NewGuid(), extension);
var path = "~/Upload/" + fileName;
file.SaveAs(Server.MapPath(path));
model.SecondaryImages = new List<ImageVM> { new ImageVM { DisplayName = displayName, Path = path } };
}
}
if (!ModelState.IsValid)
{
model.CategoryList = new SelectList(db.Categories, "CategoryId", "Name",model.CategoryId); // repopulate the SelectList
return View(model);
}
Furniture furniture = db.Furnitures.Where(x => x.FurnitureId == model.ID).FirstOrDefault();
FurnitureImages main = furniture.Images.Where(x => x.IsMainImage).FirstOrDefault();
furniture.Name = model.Name;
furniture.Description = model.Description;
furniture.Manufacturer = model.Manufacturer;
furniture.Price = model.Price;
furniture.CategoryId = model.CategoryId;
furniture.Size = model.Size;
main.DisplayName = model.MainImage.DisplayName;
main.Path = model.MainImage.Path;
main.IsMainImage = model.MainImage.IsMainImage;
if (model.MainImage != null && !model.MainImage.Id.HasValue)
{
FurnitureImages image = new FurnitureImages
{
Path = model.MainImage.Path,
DisplayName = model.MainImage.DisplayName,
IsMainImage = true
};
furniture.Images.Add(image);
db.Entry(furniture).State = EntityState.Modified;
}
// Update secondary images
IEnumerable<ImageVM> newImages = model.SecondaryImages.Where(x => x.Id == null);
foreach (ImageVM image in newImages)
{
FurnitureImages images = new FurnitureImages
{
DisplayName = image.DisplayName,
Path = image.Path ,
IsMainImage = false
};
furniture.Images.Add(images);
}
ViewBag.CategoryId = new SelectList(db.Categories, "CategoryId", "Name", furniture.CategoryId);
db.SaveChanges();
return RedirectToAction("Index");
}
Main image uploads good , but when I try to upload multiple images from another input file
#Html.TextBoxFor(m => m.SecondaryFiles, new { type = "file", multiple = "multiple" , name = "SecondaryFiles" })
#Html.ValidationMessageFor(m => m.SecondaryFiles)
#for (int i = 0; i < Model.SecondaryImages.Count; i++)
{
#Html.HiddenFor(m => m.SecondaryImages[i].Id)
#Html.HiddenFor(m => m.SecondaryImages[i].Path)
#Html.HiddenFor(m => m.SecondaryImages[i].DisplayName)
<img src="#Url.Content(Model.SecondaryImages[i].Path)" />
}
It uploads only one image , And as much as I keep trying to upload many images, it always upload only one, so where are errors in my method?

Your issue is that inside the first foreach loop, you correctly save each file to the server, but in each iteration, your creating an new List<ImageVM> and overwriting the value of SecondaryImages so when the loop has completed, it contains only one item (based on the last image).
Change the loop to
foreach (HttpPostedFileBase file in model.SecondaryFiles)
{
// FurnitureImages images = new FurnitureImages(); -- DELETE
if (file != null && file.ContentLength > 0)
{
string displayName = file.FileName;
string extension = Path.GetExtension(displayName);
string fileName = string.Format("{0}{1}", Guid.NewGuid(), extension);
var path = "~/Upload/" + fileName;
file.SaveAs(Server.MapPath(path));
// Add a new ImageVM to the collection
model.SecondaryImages.Add(new ImageVM { DisplayName = displayName, Path = path });
}
}
Note that the above assumes you view model has a parameter-less constructor that initializes SecondaryImages. If not, then add model.SecondaryImages = new List<ImageVM> before the loop.
A few other minor issues to address.
The code for generating the SelectList should be just
model.CategoryList = new SelectList(db.Categories, "CategoryId", "Name"); - the last parameter of the SelectList constructor is
ignored when binding to a model property so its pointless.
Delete the ViewBag.CategoryId = new SelectList(...) line of code.
Your model already contains a property for the SelectList (as per
note 1) but in any case, your redirecting, so adding anything to
ViewBag is pointless.
Move your db.Entry(furniture).State = EntityState.Modified; line
of code to immediately before db.SaveChanges();

Related

Downloading file from a variable thats passed from another class to Controller

I am trying to download files from my controller. I am receiving a variable from from a different class and need to download what is on that variable. I know I am probably doing a lot wrong right now. Any guidance would help a lot
Here is my class and I am returning the value allPaths
public class DBQueries
{
public List<string> GetSpecificYears(string[] response)
{
List<string> allPaths = new List<string>();
if (response != null)
{
using (var db = new ....())
{
foreach (var aYear in response)
{
// access a DbSet from DbContext here!
List<string> paths = db.ClientStatement_Inventory
.Where(x => x.statementYear == aYear)
.Select(y => y.statementPath).ToList();
allPaths.AddRange(paths);
}
}
}
return allPaths;
}
}
}
I am then passing allPaths variable to my controller by setting var allYears = getyears.GetSpecificYears()
Here is my Controller:
public ActionResult ExportFile(string[] years, string[] months, string[]radio, string[] acctNum)
{
ClientStatement_Inventory theStatementPath = new ClientStatement_Inventory();
var thePath = theStatementPath.statementPath;
DBQueries getyears = new DBQueries();
var allYears = getyears.GetSpecificYears(years);
var document = new ClientStatement_Inventory();
var cd = new System.Net.Mime.ContentDisposition
{
FileName = document.statementYear,
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.statementPath, document.statementYear);
How can I download these files using the allyears variable that is holding all the info?

Making a default in a DropDownListFor when displaying from a model

How can I make a blank default to be displayed like " " in this #Html.DropDownListFor.
I have tried the over-rides and they don't work for this.
HTML:
<td>#Html.DropDownListFor(o => o.TerminalsDDL, Model.TerminalsDDL, new { id = "ddlTerminalID", #class = "form-control align-middle" })</td>
Controller:
public ActionResult Index()
{
var model = TCBL.GetTerminalData();
return View(model);
}
//POST: TerminalCommand/Index
/*This function will insert a user selecter terminal command into the TerminalCommand table*/
public ActionResult AddTerminalCommand(AddTerminalCommandVM input)
{
TerminalCommand terminalCommand = new TerminalCommand();
terminalCommand.TerminalID = input.TerminalID;
terminalCommand.Command = input.CommandID;
terminalCommand.CommandValue = input.CommandValue;
TCBL.AddTerminalCommand(terminalCommand);
var redirectUrl = new UrlHelper(Request.RequestContext).Action("Index", "TerminalCommand");
return Json(new { Url = redirectUrl });
}
Data Layer:
/*Gets All termianls for the terminalsDDL and all terminal Cmds for Model.TerminalCommands*/
public TerminalCommandVM GetTerminalData()
{
TerminalCommandVM terminals = new TerminalCommandVM();
//For Terminal drop downs
terminals.TerminalsDDL = TCDA.GetTerminalsDropDown();
//For terminal cmd lists
terminals.TerminalCommands = TCDA.GetAll();
//For helpdescription
terminals.HelpDescriptions = TCDA.GetAllHelpDescriptionValues();
terminals.HelpDescriptionID = TCDA.GetAllHelpDescriptionIDs();
//For TerminalCommandLookupsDDL
List<SelectListItem> terminalCommandLookups = new List<SelectListItem>();
var terminalCommandLookupsResults = TCDA.GetAllTerminalCommandLookups().OrderBy(o => o.Name); //.Where(x => x.Name.Contains("S3"));
if (terminalCommandLookupsResults != null)
{
foreach (var item in terminalCommandLookupsResults)
{
SelectListItem newItem = new SelectListItem();
newItem.Text = item.Name;
newItem.Value = item.ID.ToString();
terminalCommandLookups.Add(newItem);
}
}
var terminalCommandValues = TCDA.GetAllTerminalCommandValues();
terminals.TerminalCommandValues = terminalCommandValues;
terminals.TerminalCommandLookupsDDL = terminalCommandLookups;
return terminals;
}
Bottom is data access layer, where the CA gets the data for display. I believe HTML should have some sort of default blank selection though..
You can add a blank default before the for loop in your Data Layer
SelectListItem newItem = new SelectListItem();
newItem.Text = "";
newItem.Value = "";
terminalCommandLookups.Add(newItem);
you can use this overload of Dropdownlistfor -
Html.DropDownListFor(Expression<Func<dynamic,TProperty>> expression, IEnumerable<SelectLestItem> selectList, string optionLabel, object htmlAttributes)
like this
<td>#Html.DropDownListFor(o => o.TerminalsDDL, Model.TerminalsDDL,"", new { id = "ddlTerminalID", #class = "form-control align-middle" })</td>

Passing List of Data to Other Controller

So I have action Method in my controller which get data from the CSV file which I uploaded through web
I want to pass that data to Insert controller so data from the CSV will automatically inserted to tables in my DB and pass it to view
I'm using CSV HELPER, MVC
public ActionResult ImportCSV(HttpPostedFileBase file, int compID)
{
var compName = db.CourierCompanies.Find(compID);
string path = null;
List<MyViewModel> csvD = new List<MyViewModel>();
try
{
if(file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
path = AppDomain.CurrentDomain.BaseDirectory + "upload\\" + fileName;
file.SaveAs(path);
var csv = new CsvReader(new StreamReader(path));
var invoCSV = csv.GetRecords<ImportCsV>();
foreach(var i in invoCSV)
{
MyViewModel iCSV = new MyViewModel();
iCSV.CustID = i.cust_id;
iCSV.Fullname = i.fullname;
iCSV.CustComp = i.company;
iCSV.InvoiceNo = i.rec_no;
iCSV.InsertDate = DateTime.Parse(i.doc_dt);
iCSV.Road = i.w_addr1;
iCSV.City = i.w_city;
iCSV.Zip = i.w_zip;
iCSV.Phone = i.w_phone;
iCSV.Status = "BelumTerkirim";
iCSV.compID = compID;
iCSV.CompName = compName.CompName;
iCSV.StatDate = DateTime.Now;
csvD.Add(iCSV);
}
}
}
catch
{
ViewData["Error"] = "Upload Failed";
}
return View();
}
Insert Controller
public ActionResult Create( MyViewModel model, int compID, HttpPostedFileBase file)
{
if (file != null)
{
using (MemoryStream ms = new MemoryStream())
{
file.InputStream.CopyTo(ms);
model.Image = ms.GetBuffer();
}
}
var cust = new Customer()
{
CustID = model.CustID,
Phone = model.Phone,
CustComp = model.CustComp,
Fullname = model.Fullname
};
var addrDet = new AddrDetail()
{
Road = model.Road,
City = model.City,
Zipcode = model.Zip
};
var invoice = new Invoice()
{
InvoiceNo = model.InvoiceNo
};
var stat = new Status()
{
Status1 = model.Status,
StatDate = model.StatDate,
Ket = model.Ket
};
var image = new Models.Image()
{
Image1 = model.Image
};
var detail = new DetailPengiriman()
{
NamaPenerima = model.NamaPenerima,
StatusPenerima = model.StatusPenerima,
TrDate = model.TrDate,
InsertDate = model.InsertDate
};
if (ModelState.IsValid )
{
//customer
db.Customers.Add(cust);
detail.CustID = cust.CustID;
invoice.CustID = cust.CustID;
//addrDet
db.AddrDetails.Add(addrDet);
cust.AddrDetID = addrDet.AddrDetID;
//invoice
db.Invoices.Add(invoice);
stat.InvoiceNo = invoice.InvoiceNo;
image.InvoiceNo = invoice.InvoiceNo;
detail.InvoiceNo = invoice.InvoiceNo;
//status
db.Status.Add(stat);
detail.StatusID = stat.StatusID;
////image
db.Images.Add(image);
detail.ImageID = image.ImageID;
//detail
detail.CompID = compID;
db.DetailPengirimen.Add(detail);
db.SaveChanges();
return RedirectToAction("Index", new { compID = detail.CompID});
}
return View();
}
You can abstract that business logic in another class and instantiate it inside your CSV action.
This way your can call your methods for inserting customers from both actions!

Delete the value in the database if the file does not exist

I'am scanning a folder like this
DirectoryInfo di = new DirectoryInfo(path);
var folders = di.GetDirectories().ToList().Select(d => d.Name);
var files = di.GetFiles();
I'am saving the filenames to the database and can get all the files from the database like this
var image = db.Images.ToList()
I want to delete the row from the database if the file does not exists
I'am trying something like this but it's not working
var myimages = db.Images.ToList();
foreach (var img in myimages) {
var fileExist = files.FirstOrDefault(x => x.Name.ToString().Equals(img));
if (fileExist == null)
{
Delete from database
}
If you have id for each row in your database, you can try this :
var myimages = db.Images.ToList();
string ids = "";
foreach (var img in myimages) {
var fileExist = files.FirstOrDefault(x => x.Name.ToString().Equals(img));
if (fileExist != null)
ids+=img.id.ToSting()+",";
}
if(ids=="")
return
ids = ids.Remove(ids.Length-1,1);
db.Database.ExecuteSqlCommand("delete from [Images] where id not in ("+ids+")");
should be your dbobject.yourtable.remove(img);
You can try this:
if(fileexist == null)
{
db.Images.Remove(img);
db.SaveChanges();
}
It is a simple question,Hope this works. Good luck
I think your Image class contains a name field if so do this way. otherwise just modify this. Just ask if you have doubt
var myimages = db.Images.ToList();
foreach (var img in myimages)
{
var fileExist = files.FirstOrDefault(x => x.Name.ToString().Equals(img.name));
if (fileExist == null)
{
db.Images.Remove(img);
db.SaveChanges();
}
}

Trying to access variable from outside foreach loop

The application I am building allows a user to upload a .csv file, which will ultimately fill in fields of an existing SQL table where the Ids match. First, I am using LinqToCsv and a foreach loop to import the .csv into a temporary table. Then I have another foreach loop where I am trying to loop the rows from the temporary table into an existing table where the Ids match.
Controller Action to complete this process:
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new TestEntities();
var tc = new TemporaryCsvUpload();
foreach (var item in model)
{
tc.Id = item.Id;
tc.CreditInvoiceAmount = item.CreditInvoiceAmount;
tc.CreditInvoiceDate = item.CreditInvoiceDate;
tc.CreditInvoiceNumber = item.CreditInvoiceNumber;
tc.CreditDeniedDate = item.CreditDeniedDate;
tc.CreditDeniedReasonId = item.CreditDeniedReasonId;
tc.CreditDeniedNotes = item.CreditDeniedNotes;
entity.TemporaryCsvUploads.Add(tc);
}
var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id);
foreach (var number in idMatches)
{
number.CreditInvoiceDate = tc.CreditInvoiceDate;
number.CreditInvoiceNumber = tc.CreditInvoiceNumber;
number.CreditInvoiceAmount = tc.CreditInvoiceAmount;
number.CreditDeniedDate = tc.CreditDeniedDate;
number.CreditDeniedReasonId = tc.CreditDeniedReasonId;
number.CreditDeniedNotes = tc.CreditDeniedNotes;
}
entity.SaveChanges();
entity.Database.ExecuteSqlCommand("TRUNCATE TABLE TemporaryCsvUpload");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
The issue in the above code is that tc is inside the first loop, but the matches are defined after the loop with var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id);, so I am only getting the last item of the first loop.
So I would need to put var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id); in the first loop, but then I can't access it in the second. If I nest the second loop then it is way to slow. Is there any way I could put the above statement in the first loop and still access it. Or any other ideas to accomplish the same thing? Thanks!
Instead of using multiple loops, keep track of processed IDs as you go and then exclude any duplicates.
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new TestEntities();
var tcIdFound = new HashSet<string>();
foreach (var item in model)
{
if (tcIdFound.Contains(item.Id))
{
continue;
}
var tc = new TemporaryCsvUpload();
tc.Id = item.Id;
tc.CreditInvoiceAmount = item.CreditInvoiceAmount;
tc.CreditInvoiceDate = item.CreditInvoiceDate;
tc.CreditInvoiceNumber = item.CreditInvoiceNumber;
tc.CreditDeniedDate = item.CreditDeniedDate;
tc.CreditDeniedReasonId = item.CreditDeniedReasonId;
tc.CreditDeniedNotes = item.CreditDeniedNotes;
entity.TemporaryCsvUploads.Add(tc);
}
entity.SaveChanges();
entity.Database.ExecuteSqlCommand("TRUNCATE TABLE TemporaryCsvUpload");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
If you want to make sure you get the last value for any duplicate ids, then store each TemporaryCsvUpload record in a dictionary instead of using only a HashSet. Same basic idea though.
Declare idMatches before the first loop, but don't instantiate it or set its value to null. Then you'll be able to use it inside both loops. After moving the declaration before the first loop, you'll still end up having the values from the last iteration using a simple Where. You'll need to concatenate the already existing list with results for the current iteration.

Categories

Resources