Move one folder back in server MVC - asp.net-mvc

I need to display an image in another project from an MVC project. While accessing the image from MVC, i need to go a folder back ( or no need of the View folder name ) in the path. Following is the controller code for the view Views\Booking
public virtual ActionResult ResendEticket(BookingViewModel resendTicketModel)
{
if (ModelState.IsValid)
{
if(!string.IsNullOrEmpty(resendTicketModel.Email))
{
BookingService resendEticket = new BookingService();
string result = resendEticket.ResendEticket(Convert.ToInt32(resendTicketModel.BookingReference), resendTicketModel.Email);
if(!string.IsNullOrEmpty(result))
{
ModelState.AddModelError(string.Empty, result);
}
}
}
ModelState.AddModelError(string.Empty, "Unable to do resend ticket due to system error");
return RedirectToAction("Index", "Booking");
}
The path i am getting in the receiving side currently is
C:\ Projects\Booking\Invoices\Templates\images\logo_eticket.png
What i need is C:\ Projects\Invoices\Templates\images\logo_eticket.png
In the receiving side , i am using following code
TaxInvoiceFilePath = HttpContext.Current.Server.MapPath(mtxr.TaxInvoiceFilePath);
I may not be able to change anything on above line, as this is shared with other applications.
How can i move HttpContext.Current.Server.MapPath one step back so that all will be fine

Related

Force garbage collection Azure Blobstorage after deletion container in ASP.net Core

I have the following code to delete a container on Azure Blobstorage:
public IActionResult DeleteDownloadLink(string container)
{
var connectionString = _configuration["StorageConnectionString"];
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient(container);
_moduleRepository.DeleteDownloadLink(container);
blobContainer.Delete();
return RedirectToAction("ManageDownloadLinks", "Admin");
}
This doesn't delete the container straight away, and I found the following in the documentation:
When a blob is successfully deleted, it is immediately removed from
the storage account’s index and is no longer accessible to clients.
The blob’s data is later removed from the service during garbage
collection.
However, I can still go to the link and download it. No error is returned. So I guess I have two questions:
Is there something wrong with my delete code?
If nothing is wrong with the code, can I force garbage collection so the link is no longer accessible?
I create below sample to test, I can delete container successfully. The only difference between my code and yours is that there is no _moduleRepository.DeleteDownloadLink(container); this line of code.
1. My first suggestion is that you can comment out this line and try again.
2. Check your `StorageConnectionString`, make sure you delete correct container.
[HttpGet("blobtest")]
public async Task<string> DeleteDownloadLink(string container)
{
var connectionString = "DefaultEn***indows.net";
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient blobContainer = blobServiceClient.GetBlobContainerClient(container);
if (await blobContainer.ExistsAsync())
{
Console.WriteLine("Created container {0}", blobContainer.Name);
try
{
var a= blobContainer.Delete();
return "delete successed";
}
catch (Exception)
{
return "delete failed";
throw;
}
}
return "don't exist";
//blobContainer.Delete();
//return RedirectToAction("ManageDownloadLinks", "Admin");
}

Image rendering via controller API sometimes breaks when being access by multiple users

I have here an image rendering method on my api that is being access via url by users in order to display images as source. I don't have a problem when testing locally since I'm only making single user requests. But by the time I deployed it, staging/production testers comes in. The image being rendered is broken because of an internal server error, I'm guessing it is because of the multiple request.
Here's how I do it:
[Route("userprofileimage")]
public class UserProfileImageController : Controller
{
[Route("render")]
public FileResult Render(string di, string tk, string fi)
{
if (!string.IsNullOrEmpty(di) && !string.IsNullOrEmpty(tk) && !string.IsNullOrEmpty(fi))
{
var filePath = Path.Combine(Path.Combine(Server.MapPath("~/App_Data/UserProfileImages"), fi));
var isValid = new UserTokenService().ValidateUserToken(Guid.Parse(di), tk);
if(isValid)
{
if (ImageFileNotAvailable(filePath))
return new FileStreamResult(new FileStream(Path.Combine(Server.MapPath("~/App_Data/UserProfileImages"), "default.png"), FileMode.Open), "image/png");
else
return new FileStreamResult(new FileStream(filePath, FileMode.Open), "image/png");
}
}
return new FileStreamResult(new FileStream(Path.Combine(Server.MapPath("~/App_Data/UserProfileImages"), "default.png"), FileMode.Open), "image/png");
}
private bool ImageFileNotAvailable(string fullFilePath)
{
return !System.IO.File.Exists(fullFilePath);
}
}
As you can see there are token and id checks before the actual rendering of the image.
I have no specific reason on my approach on this one. I just find it convenient to have links as image sources.
I was expecting that the requests from multiple users will wait until a previous execution is done before going in but I guess they go in simultaneously. So at some point the file is currently open or being manipulated and another request comes in. Hence the error.
Any ideas on how can I improve this?

Convert DataTable to Entity Objects and add to Table

I am quite new to Programming/MVC, so apologies for any incorrect terminology.
In my MVC application using Entity Framework, I am struggling to create some functionality that allows a user to upload a CSV file, parse it, and add it to my database (generated by code first).
I have a Project Model, and I want a user to be able to upload a CSV file of Deliverables for specific Projects. I will then have a View that displays Project Details, and a full list of all Deliverables as well.
Following a pretty straight forward tutorial, I have the CSV parser in my view which presents the DataTable after parsing. The View renders fine, and the this is the controller I am using:
public ActionResult Upload()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Upload(HttpPostedFileBase upload)
{
if (ModelState.IsValid)
{
if (upload != null && upload.ContentLength > 0)
{
if (upload.FileName.EndsWith(".CSV"))
{
Stream stream = upload.InputStream;
DataTable csvTable = new DataTable();
using (CsvReader csvReader = new CsvReader(new StreamReader(stream), true))
{
csvTable.Load(csvReader);
}
return View(csvTable);
}
else
{
ModelState.AddModelError("File", "This file format is not supported");
return View();
}
}
else
{
ModelState.AddModelError("File", "Please Upload Your file");
}
}
return View();
}
Tutorial: http://techbrij.com/read-csv-asp-net-mvc-file-upload
I now can't for the life of me understand how I can now put this Datatable into my Deliverables table, whilst referencing the specific Project the Deliverables are for. For example, how can the code know that the deliverables in the CSV file are for Project#123 and not Project#122...
I am hoping for nudge in the right direction, and really appreciate any assistance.

MVC download of clickonce Setup.exe and display confirmation view

I'll start by saying i'm a C# MVC newbie, but I've set up a site with Identity Management and extended the database with some custom tables to store additional info about my users, so I'm not a total neophyte. I've been working on a VB WPF application that I want to deploy from my new website and that is where I'm running into an issue.
I've created a new controller (User) and a couple of views (Download) & (Setup). I created a downloadmodel used by the download view.
In abstract what I am doing is displaying the download view (get) which has three checkboxes to confirm the user has read the Overview, Installation, and Terms of Service. These are boolean values in the model. I also have a string response message in the model, that displays just above the submit button. Here is the model:
public class DownloadModel
{
public bool Overview { get; set; }
public bool Installation { get; set; }
public bool TermsOfService { get; set; }
public string Response { get; set; }
public DownloadModel()
{
Overview = false;
Installation = false;
TermsOfService = false;
Response = "After checking boxes click the button to begin installation";
}
}
My User Controller handles the Get to initially display the download view, and then in the Post it checks to see if all the checkboxes were ticked, if not it updates the response message and returns the view.
If the checkboxes are all checked then it pulls the subscriber (which must exist because it was created when the user verified their e-mail via the account controller - identity management), then proceeds to update the subscriber with the original (if new) or last download date(s). At this point I want to begin downloading the clickonce setup.exe file, before returning the setup view.
[Authorize]
public class UserController : Controller
{
// GET: User/Download
public ActionResult Download()
{
return View(new DownloadModel { });
}
// Post: User/Download
[HttpPost]
public ActionResult Download(DownloadModel downloadcheck)
{
if (!ModelState.IsValid)
{
return View(downloadcheck);
}
//check to see if all the boxes were checked
if (downloadcheck.Overview == true &
downloadcheck.Installation == true &
downloadcheck.TermsOfService == true)
{
//yes - so let's proceed
//first step is to get the subscriber
Subscriber tSubscriber = new Subscriber();
tSubscriber.Email = User.Identity.Name;
bool okLoad = tSubscriber.LoadByEmail();
if (okLoad == false)
{
//we have a real problem. a user has logged in but they are not yet
//a valid subscriber?
throw new Exception("Subscriber not found");
}
// update subscriber with download in process...
if (tSubscriber.OriginalDownload == DateTime.MinValue)
{
tSubscriber.OriginalDownload = DateTime.Now;
tSubscriber.LastDownload = tSubscriber.OriginalDownload;
}
else
{
tSubscriber.LastDownload = DateTime.Now;
}
if (tSubscriber.UpdateDownloaded() == false)
{
//update of download dates failed
//another problem that shouldnt occur.
downloadcheck.Response = "A problem occured downloading your setup."
+ "Try again. If this error continues please contact support.";
return View(downloadcheck);
}
//download dates have been updated for the subscriber so let's start the download!
//THIS IS WHERE I NEED TO BEGIN THE DOWNLOAD
return View("Setup");
}
else
{
// all boxes were not checked - update message
downloadcheck.Response = "Please confirm you have reviewed the above information "
+ "by checking all of the boxes before clicking on the button.";
return View(downloadcheck);
}
}
}
The download view is pretty straight forward, and the setup view simply confirms the download was started and provides a link to the help-setup page.
I'm really a bit lost here. I thought I'd plug in a return new filepathresponse, but I can't do that and return the setup view.
My other thought was to somehow trigger the download of my /xxx/setup.exe from within the setup view as it is returned - but I'm at a loss as to how to accomplish this.
I'll be the first to admit that my mvc c# code is probably overly verbose and my approach to how I've done this may be totally wrong, but I'm just scrambling to get this done so I can deploy my WPF app to select Beta users for testing. It's been a long time living off savings and I can smell go-live from here.
One final note, I'm using setup.exe clickonce deployment of my wpf app for simplicity, as there are .net and localsqldb prerequisites, but I will not be using automated updates - not that this is really relevant.
Appreciate all input and advice.
After more digging and hacking I've found a solution that works. Firstly in my setup view (confirmation page) I added a simple script to initiate a new function in my user controller:
<script type="text/javascript">
window.location.href = "/user/sendfile/"
</script>
The controller change was simple too. For testing I just used a txt file.
// User/SendFile
public ActionResult SendFile()
{
string path = #"~/xxxx/anyfile.txt";
string content = "application/txt";
//string content = "application/x-ms-application";
return new FilePathResult(path, content)
{
FileDownloadName = "mynewtext.txt"
};
}
What is really interesting about this solution is that the FileDownloadName is what the file content is downloaded as. So in this way I can refer to the actual setup.exe file in the path, but then name the downloaded exe anything I want. Bonus :)

Multiple views modifying one object MVC

I am building a service which requires a somewhat lengthy setup process. I have it broken into 4 models and 4 corresponding views. They are Setup, Setup2, Setup3, and Setup4. Each of these views gathers information from the user which is stored in a User object. I have been passing the user along like this:
[HttpPost]
public ActionResult Setup(FormCollection values)
{
User registeringUser = new User();
registeringUser.email = User.Identity.Name;
registeringUser.fName = values["fName"];
registeringUser.lName = values["lName"];
registeringUser.phone = values["phone"];
return RedirectToAction("/Setup2", registeringUser);
}
For some reason, this seems to work just fine for the first jump (from Setup to Setup2) but after that I'm getting weird behavior, such as User. getting set to null when the User is passed to another View.
In a related, but slightly different issue, I need the last screen (Setup4) to be recursive. This screen adds a course in which the user is enrolled, and if they don't check the "This was my last class" button, it needs to basically clear the form so they can enter another course.
The entire Controller looks like this:
[HttpPost]
public ActionResult Setup4(FormCollection values, User registeringUser)
{
// values["allClassesAdded"] returns "false" as a string if box is unchecked, returns "true,false" if checked.
// Solution: parse string for "true"
if (utils.parseForTrue(values["allClassesAdded"]))
{
// TODO Redirect to "congratulations you're done" page.
database.CreateUserInDB(registeringUser);
return Redirect("/Home");
}
else
{
// Build course and add it to the list in the User
course c = new course(values);
if (Request.IsAuthenticated)
{
//registeringUser.currentCourses.Add(c);
registeringUser.AddCourse(c);
return RedirectToAction("/Setup4", registeringUser); // <---- This doesn't really work right
//return View();
}
else
{
return Redirect("/Account/Login");
}
}
}
This is my first project with MVC, so if you find that I'm doing the entire thing completely incorrectly, feel free to not answer the question I asked and offer the proper solution to this need. I'm moving an existing (pure) C# project to MVC and I'm mainly just stuck on how to work within MVC's interesting structure. I'm very grateful for any help you can give!
Thanks!
You can store user related data in session without passing it between requests
Smth like this
[HttpPost]
public ActionResult Step1(Step1Model model)
{
Session["UserRegistration"] = new UserRegistration
{
FirstName = model.fName,
....
}
....
}
[HttpPost]
public ActionResult Step2(Step2Model model)
{
var userRegistration = Session["UserRegistration"] as UserRegistration;
if (userRegistration == null) { return Redirrect("Step1"); }
userRegistration.SomeField = model.someField;
...
Session["UserRegistration"] = userRegistration;
....
}

Resources