how can i save more than one image in the database? - asp.net

I just want to save the route of the images in the database.
So i try this.
And i get this error System.NullReferenceException: Object reference not set to an instance of an object.
This is my Controller
public ActionResult SaveImages(IEnumerable<HttpPostedFileBase> img, Imagenes images)
{
foreach (var n in img)
{
var PhotoUrl = Server.MapPath("/images" + n.FileName);
if (n != null && n.ContentLength > 0)
n.SaveAs(PhotoUrl);
images.imgUrl = "/images" + n.FileName;
db.Imagenes.Add(images);
db.SaveChanges();
}
return View("Index");
}
This is my model class
public partial class Imagenes
{
public int id { get; set; }
[StringLength(200)]
public string imgUrl { get; set; }
}
my View
#{
ViewBag.Title = "Home Page";}
#using (Html.BeginForm("SaveImages", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
<input type="file" name="img" id="img" multiple />
<input type="submit" name="submit" value="Save"/>
</div>}

The error you are getting is nothing about the image saving part, but I'm assuming it's the use of your images property...
As you didn't specify where that property comes from, MVC automatically assumes that's a POST Variable, and in your HTML, you have nothing of sorts...
change your action code to:
public ActionResult SaveImages(IEnumerable<HttpPostedFileBase> img)
{
const string folderToUpload = "/images";
foreach (var n in img)
{
var imageToUpload = folderToUpload + n.FileName;
var photoUrl = Server.MapPath(imageToUpload);
if (n != null && n.ContentLength > 0) {
n.SaveAs(photoUrl); // save to folder
var images = new Imagenes {
imgUrl = imageToUpload
};
db.Imagenes.Add(images); // add to repository
db.SaveChanges(); // save repositorychanges
}
}
return redirectToAction("Index");
}
I'm also assuming that db was already injected in your constructor, and it's not NULL
Code edited:
create a constant variable to have the folder to upload, so you don't repeat that code over and over
create a variable to hold the full path of the image, so you don't repeat that code over and over (remember: DRY - Don't Repeat Yourself)
save to database only if the file was saved
create a new variable to hold your object to be saved
redirect to the action using redirectToAction as you might have some calls in your Index and only redirecting to the View would give you an error
to be persistence, change the PhotoUrl to photoUrl (local variables = start with lowercase)

Related

Dynamically assign value to a Property

I have hard time understanding assigning value to a property dynamically, means during run time so that i can retrieve/display value in a razor page. I have following programming logic to accomplish my task, however this (LmitedWords) property does not render or hold any value to be displayed. How do I assign a value to this property during run time.
public class Post
{
public string Content { get; set; }
[NotMapped]
public string LimitedWords { get; set; }
}
My controller code follow:-
public async Task<IActionResult> GetAllPosts()
{
var myLimitProperty = new Post();
var result = await _repository.GetAllPosts();
foreach (var post in result)
{
var limitContent = ContentExtension.ReturnLimitedDescription(post.Content, size);
myLimitProperty.LimitedWords = limitContent;
}
return View(result);
}
my contentextension helper method returns value as expected and during debug it does show that local variable "limitContent" has the value but it somehow does not assign it to LimitedWords property, which is a property in Post class.
In my Post class there are other properties as well and i want them to be displayed as it is saved in the database.
My Razor page does not display content as it is null:
<div>
<markdown markdown="#Model.LimitedWords">
</div>
Thanks!
Well based on what you have posted, the result holds the posts returned by the repository.
You loop through these posts, update the myLimitProperty local variable in the action and return the original collection.
Nothing is actually being updated on objects being sent to the view
Create a projection from the list, populating the desired properties that should be displayed in the view.
public async Task<IActionResult> GetAllPosts() {
var posts = await _repository.GetAllPosts();
var result = posts.Select(post => {
var limitContent = ContentExtension.ReturnLimitedDescription(post.Content, size);
var model = new Post() {
Content = post.Content;
LimitedWords = limitContent;
};
return model;
}).ToList();
return View(result);
}

Model mismatch error when posting form

I am working on a simple image upload site in which users will have the ability to post comments on the images uploaded to the site, whenever posting a comment I am given this error :
The model item passed into the dictionary is of type '<>f__AnonymousType1`1[System.Int32]', but this dictionary requires a model item of type 'SilkMeme.Models.Meme'.
I know it has something to do with the model being defined at the top of my view being different to the one I am sending the post request to but I'm not entirely sure how to fix it
View
#model SilkMeme.Models.Meme
....
#using (Html.BeginForm("Comment", "Memes", new { id = Model.SilkId }))
{
<label for="thought">Thoughts?</label>
<input type="text" name="thought"/>
<label for="rating">Rating?</label>
<input name="rating" type="range" min="0" max="10" step="1" />
<input type="submit" value="Post Thoughts" />
}
<div class="thoughts">
#foreach (var c in ViewBag.thoughts)
{
<p>- #c.ThoughtWords , #c.ThoughtRating / 10 meme</p>
}
</div>
Controller
public ActionResult Details(int? id)
{
var thoughts = from comment in db.Thoughts where comment.SilkId == id select comment;
ViewBag.thoughts = thoughts;
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Meme meme = db.Memes.Find(id);
if (meme == null)
{
return HttpNotFound();
}
return View(meme);
}
[HttpPost]
public ActionResult Comment(int id)
{
int thoughtid = (from m in db.Thoughts select m).OrderByDescending(e => e.ThoughtId).FirstOrDefault().ThoughtId + 1;
if (Request["thought"].ToString() != "")
{
Thought thought = new Thought()
{
ThoughtId = thoughtid,
SilkId = id,
Meme = db.Memes.Find(id),
ThoughtWords = Request["thought"],
ThoughtRating = Int32.Parse(Request["rating"])
};
db.Thoughts.Add(thought);
}
return View("Details", new { id = id });
}
This line.
return View("Details", new { id = id });
It is basically passing an anonymous object with Id property to your view which is strongly typed to Meme type and expects an object of Meme class.
If you save your data successfully, Ideally,you should do a redirect to the GET action (following PRG pattern)
[HttpPost]
public ActionResult Comment(int id)
{
int thoughtid = (from m in db.Thoughts select m)
.OrderByDescending(e => e.ThoughtId).FirstOrDefault().ThoughtId + 1;
if (Request["thought"].ToString() != "")
{
Thought thought = new Thought()
{
ThoughtId = thoughtid,
SilkId = id,
Meme = db.Memes.Find(id),
ThoughtWords = Request["thought"],
ThoughtRating = Int32.Parse(Request["rating"])
};
db.Thoughts.Add(thought);
db.SaveChanges();
}
return RedirectToAction("Details", new { Id=id });
}
Also, I recommend using MVC Modelbinding to read the submitted form data. You will find a ton of examples on stackoverflow to do that. When using ModelBinding, you can return the posted view model back to the view (with an error message if needed) and the ValidationSummary /ValidationMessgeFor helper methods can show an error message to user as needed.

Asp.NET temporary file saving

I got a path from the jquery code URL.createObjectURL(event.target.files[0]);
It returns something like this : blob:http%3A/localhost%3A59105/f7dae0f7-088f-48cf-b446-eeda0bf23705
I tried to save this file like
byte[] data;
using (WebClient client = new WebClient())
{
data = client.DownloadData("blob:http%3A/localhost%3A59105/f7dae0f7-088f-48cf-b446-eeda0bf23705");
}
File.WriteAllBytes(#"~/a.jpg", data);
But it gives an error about the code above:
The URI prefix is not recognized.
How exactly I can copy this file?
Thanks for your suggestions.
1.Create simple GET method
[HttpGet]
public ActionResult GetFile(){
return View();
}
2. Create View with #Html.BeginForm helper
#using (Html.BeginForm("GetFile","YourController", FormMethod.Post, { enctype = "multipart/form-data" }))
{
<input type="file" id="fileup" name="file"/>
<input type="submit" value="Send">
}
Rembember to use name attribute and overloaded version of Html.BeginForm()
3.Get data in Backend
[HttpPost]
public ActionResult GetFile(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var filePath = Path.Combine(Server.MapPath("~/Temp/"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Success");
}
Name in html attribute must have same name as HttpPostedFileBase.

How to Display multiple images from db using MVC3

I am new to MVC.I have saved the image and some data but can't display the saved images. I want to display all saved images in the main page.
Model: Get the db list from model
public List<Products> GenreList()
{
}
Controller
public ActionResult MYsample()
{
var MyList = storeDB.GenreList();
var a= MyList.Count;
if (a != null)
{
foreach (var li in MyList)
{
return File(li.fileContent, li.mimeType,li.fileName);
}
}
return View(MyList);
}
View
#foreach (var item in Model)
{
<img src="#Url.Action("MYsample", "HomeController", new { id = item.ProductID })" alt="#item.Productname" />
}
You could start by writing a controller action that will serve the images to the response stream:
public ActionResult Image(int id)
{
// you should add a method to your repository that returns the image
// record from the id
Products image = storeDB.Get(id);
return File(image.fileContent, image.mimeType);
}
and then in your main controller action send the list of images to the view:
public ActionResult MySample()
{
var model = storeDB.GenreList();
return View(model);
}
and then in your strongly typed view loop through the images and generate an <img> tag for each image by pointing its src property to the newly created controller action:
#model MyList
#foreach (var li in MyList)
{
<img src="#Url.Action("Image", new { id = li.Id })" alt="" />
}
If you don't want to have a separate controller action that will query the database and retrieve the image record from an id you could use the data URI scheme. Bear in mind though that this is not supported by all browsers.
So the idea is that your controller action will send the image data to the view:
public ActionResult MySample()
{
var model = storeDB.GenreList();
return View(model);
}
and then inside your strongly typed view you could loop through the list and generate the proper <img> tag:
#model MyList
#foreach (var li in MyList)
{
<img src="src="data:#(li.mimeType);base64,#(Html.Raw(Convert.ToBase64String(li.fileContent)))" alt="" />
}

Making TPL Async calls from mvc controller on click of submit button

Basically I want to implement simple search functionality, whenever user enters some keyword in the text box on view and clicks submit button I want to make ASYNC calls to predefined website urls using TPL Async mechanism. When I do the same with console application it works like a charm but not with ASP.NET MVC3.
I couldn't find the reason
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
[HttpPost]
public ActionResult Index(string text)
{
string[] url = { "http://www.msnbc.com", "http://www.yahoo.com",
"http://www.nytimes.com", "http://www.washingtonpost.com",
"http://www.latimes.com", "http://www.newsday.com" };
Task<string[]> webTask = this.GetWordCounts(url, text);
string[] results = null;
try
{
results = webTask.Result;
}
catch (AggregateException e)
{
}
return View("Index", results);
}
//Taken from MSDN
Task<string[]> GetWordCounts(string[] urls, string name)
{
TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
WebClient[] webClients = new WebClient[urls.Length];
object m_lock = new object();
int count = 0;
List<string> results = new List<string>();
for (int i = 0; i < urls.Length; i++)
{
webClients[i] = new WebClient();
#region callback
// Specify the callback for the DownloadStringCompleted
// event that will be raised by this WebClient instance.
webClients[i].DownloadStringCompleted += (obj, args) =>
{
if (args.Cancelled == true)
{
tcs.TrySetCanceled();
return;
}
else if (args.Error != null)
{
// Pass through to the underlying Task
// any exceptions thrown by the WebClient
// during the asynchronous operation.
tcs.TrySetException(args.Error);
return;
}
else
{
// Split the string into an array of words,
// then count the number of elements that match
// the search term.
string[] words = null;
words = args.Result.Split(' ');
string NAME = name.ToUpper();
int nameCount = (from word in words.AsParallel()
where word.ToUpper().Contains(NAME)
select word)
.Count();
// Associate the results with the url, and add new string to the array that
// the underlying Task object will return in its Result property.
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
}
// If this is the last async operation to complete,
// then set the Result property on the underlying Task.
lock (m_lock)
{
count++;
if (count == urls.Length)
{
tcs.TrySetResult(results.ToArray());
}
}
};
#endregion
// Call DownloadStringAsync for each URL.
Uri address = null;
try
{
address = new Uri(urls[i]);
// Pass the address, and also use it for the userToken
// to identify the page when the delegate is invoked.
webClients[i].DownloadStringAsync(address, address);
}
catch (UriFormatException ex)
{
// Abandon the entire operation if one url is malformed.
// Other actions are possible here.
tcs.TrySetException(ex);
return tcs.Task;
}
}
// Return the underlying Task. The client code
// waits on the Result property, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
this is my view - for now I have hard coded keyword as microsoft
#using (Html.BeginForm("Index", "Home", new { text = "Microsoft" }))
{
<input type="submit" />
}
Update: It stays forever and inside the try block of Index Post method
I would recommend you using an AsyncController for this task to avoid jeopardizing ASP.NET worker threads which is one the worst thing that might happen to an ASP.NET application => running out of worker threads. It's like running out of fuel in the middle of the desert. You most certainly die.
So let's start by writing an extension method that will allow us converting the legacy WebClient event based pattern into the new task based pattern:
public static class TaskExtensions
{
public static Task<string> DownloadStringAsTask(this string url)
{
var tcs = new TaskCompletionSource<string>(url);
var client = new WebClient();
client.DownloadStringCompleted += (sender, args) =>
{
if (args.Error != null)
{
tcs.SetException(args.Error);
}
else
{
tcs.SetResult(args.Result);
}
};
client.DownloadStringAsync(new Uri(url));
return tcs.Task;
}
}
Armed with this extension method in hand we could now define a view model that will basically reflect the requirements of our view:
public class DownloadResultViewModel
{
public string Url { get; set; }
public int WordCount { get; set; }
public string Error { get; set; }
}
Then we move on to an asyncrhonous controller that will contain 2 actions: a standard synchronous Index action that will render the search form and an asynchronous Search action that will perform the actual work:
public class HomeController : AsyncController
{
public ActionResult Index()
{
return View();
}
[AsyncTimeout(600000)]
[HttpPost]
public void SearchAsync(string searchText)
{
AsyncManager.Parameters["searchText"] = searchText;
string[] urls =
{
"http://www.msnbc.com",
"http://www.yahoo.com",
"http://www.nytimes.com",
"http://www.washingtonpost.com",
"http://www.latimes.com",
"http://www.unexistentdomainthatwillcrash.com",
"http://www.newsday.com"
};
var tasks = urls.Select(url => url.DownloadStringAsTask());
AsyncManager.OutstandingOperations.Increment(urls.Length);
Task.Factory.ContinueWhenAll(tasks.ToArray(), allTasks =>
{
var results =
from task in allTasks
let error = task.IsFaulted ? task.Exception.Message : null
let result = !task.IsFaulted ? task.Result : string.Empty
select new DownloadResultViewModel
{
Url = (string)task.AsyncState,
Error = error,
WordCount = result.Split(' ')
.Where(x => string.Equals(x, searchText, StringComparison.OrdinalIgnoreCase))
.Count()
};
AsyncManager.Parameters["results"] = results;
AsyncManager.OutstandingOperations.Decrement(urls.Length);
});
}
public ActionResult SearchCompleted(IEnumerable<DownloadResultViewModel> results)
{
return View("index", results);
}
}
Now we define an ~/Views/Home/Index.cshtml view that will contain the search logic as well as the results:
#model IEnumerable<DownloadResultViewModel>
#using (Html.BeginForm("search", null, new { searchText = "politics" }))
{
<button type="submit">Search</button>
}
#if (Model != null)
{
<h3>Search results</h3>
<table>
<thead>
<tr>
<th>Url</th>
<th>Word count</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
}
And of course the corresponding display template that will be rendered automatically for each element of our model (~/Views/Shared/DisplayTemplates/DownloadResultViewModel.cshtml):
#model DownloadResultViewModel
<tr>
<td>#Html.DisplayFor(x => x.Url)</td>
<td>
#if (Model.Error != null)
{
#Html.DisplayFor(x => x.Error)
}
else
{
#Html.DisplayFor(x => x.WordCount)
}
</td>
</tr>
Now, since the search operation could take quite a long time your users could quickly get bored without being able to use some of the other hundredths of functionalities that your webpage has to offer them.
In this case it is absolutely trivial to invoke the Search controller action using an AJAX request and showing a spinner to inform the users that their search is in progress but without freezing the webpage allowing them to do other things (without navigating away from the page obviously).
So let's do that, shall we?
We start by externalizing the results into a partial (~/Views/Home/_Results.cshtml) without touching at the display template:
#model IEnumerable<DownloadResultViewModel>
#if (Model != null)
{
<h3>Search results</h3>
<table>
<thead>
<tr>
<th>Url</th>
<th>Word count</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
}
and we adapt our ~/Views/Home/Index.cshtml view to use this partial:
#model IEnumerable<DownloadResultViewModel>
#using (Html.BeginForm("search", null, new { searchText = "politics" }))
{
<button type="submit">Search</button>
}
<div id="results">
#Html.Partial("_Results")
</div>
and of course the SearchCompleted controller action that must now return only the partial result:
public ActionResult SearchCompleted(IEnumerable<DownloadResultViewModel> results)
{
return PartialView("_Results", results);
}
Now all that's left is to write a simple javascript that will AJAXify our search form. So this could happen into a separate js that will reference in our layout:
$(function () {
$('form').submit(function () {
$.ajax({
url: this.action,
type: this.method,
success: function (results) {
$('#results').html(results);
}
});
return false;
});
});
Depending on whether you referenced this script in the <head> section or at the end of the body you might not need to wrap it in a document.ready. If the script is at the end you could remove the wrapping document.ready function from my example.
And the last part is to give some visual indication to the user that the site is actually performing a search. This could be done using a global ajax event handler that we might subscribe to:
$(function () {
$(document).ajaxStart(function () {
$('#results').html('searching ...');
});
});

Resources