Difficulties to send File to view using FileContentResult action method - asp.net-mvc

I need to display in image on the view this way
<img src = <% = Url.Action("GetImage", "Home", new { productID })%>
This is the action which's supposed to supply data
public FileContentResult GetImage(int ID)
{
var img = db.Images.Where(p => p.ID == ID).First();
return File(img.ImageData, img.ImageMimeType);
}
This example comes from Pro ASPNET.NET MVC (Steven Sanderson/APress). I'm getting the following error: The best overload method match for System.Web.Mvc.Controller.File(string, string) has some invalid argument. Cannot convert from System.Data.Linq.Binary to string.
Yet, the intellisense's telling me that there's an overload method (byte[] filecontents, string fileType).But, when I write the above code, I get the error. Am I missing something?
EDIT
Thanks for the answer. I've experienced a similar problem while uploading the image file. Here's my action method
public ActionResult AddImage(HttpPostedFileBase image)
{
if(image != null)
{
var img = new Image();//This Image class has been
//created by the DataContext
img.ImageMimeType = image.ImageMimeType
img.ImageData = new byte[image.ContentLength];
image.InputStream.Read(img.ImageData, 0, image.ContentLength);
}
}
I get error for the last line "image.InputStream.Read(myImage.ImageData, 0, image.ContentLength);" It's saying that it can't convert System.Data.Linq.Binary to Byte[]
What I did was (i) to create a new class, called ImageDataClass, (ii) do the above operation against that class, (iii) do the explicit conversion from ImageDataClass to Image, and (iv) save to the DB using Linq.
I don't think it should be that complicate. Is there any way to make it work using simply an extension method such as ToArray as for the other case???
Thanks for helping

There is an overload for File() that takes a byte array, but you are trying to pass in a type of System.Data.Linq.Binary, not a byte array. However, there is a method on Binary to convert to a byte array.
Try this:
public FileContentResult GetImage(int ID)
{
var img = db.Images.Where(p => p.ID == ID).First();
return File(img.ImageData.ToArray(), img.ImageMimeType);
}
The reason the compile error mentions "string" is purely because it can't work out which overload you were trying for, so it just picks one, in this case string, and then reports the type conversion error.
[EDIT: in response to OP edit]
You should be able to try something like this:
public ActionResult AddImage(HttpPostedFileBase image)
{
if(image != null)
{
var img = new Image();//This Image class has been
//created by the DataContext
img.ImageMimeType = image.ImageMimeType
var imageData = new byte[image.ContentLength];
image.InputStream.Read(imageData, 0, image.ContentLength);
img.ImageData = new System.Data.Linq.Binary(imageData);
}
}
Remember that although System.Data.Linq.Binary is probably just a byte array underneath, or is at least intended to represent byte data, it is not itself of type byte[]; you still have to convert to and from (a similar situation to System.IO.MemoryStream)

Related

Validate Data in a CSV file using MVC

I am trying to validate the UK phone number contain in a CSV file before inserting the data in the table. At the moment the if statement is not working when the mobile number is valid the code doesn't jump to the else block. See the code below.
Controller code
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> BulkMEssage(TextMessage messagedetail,
HttpPostedFileBase attachmentcsv,
MobileRecipient messagelink, int ddlshedule, string date)
{
if (ModelState.IsValid)
{
CsvFileDescription csvFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
CsvContext csvContext = new CsvContext();
StreamReader streamReader = new
StreamReader(attachmentcsv.InputStream);
IEnumerable<MobileRecipient> list =
csvContext.Read<MobileRecipient>(streamReader,
csvFileDescription);
foreach (var mobilenumber in list)
{
var phoneno = #"/^ (\+44\s ? 7\d{ 3}|\(? 07\d{ 3}\)?)\s ?\d{ 3}\s ?\d{ 3}$/";
if ((!mobilenumber.MobileNumber.Equals(phoneno)))
{
error message Here with the wrong format number
}
else
{
Insert Data in the database
}
}
}
I think I am using the regular expression the wrong way. The column name in my CSV file is called "MobileNumber". Please suggest if you have any better way to do this.

View/Model data isn't refreshing/changing after post/postback, even though I'm using the PRG pattern

Update I have saved my problem a long time ago. The problem was that I was trying to call the view model on the wrong view method! I was calling the base view method (Document), instead of one of it's derived method (like NewDocument, PDFDocument, etc.) Thus it was only giving me the Documents data, which didn't change. I was looking and using the wrong view method all the time... Stephen, when you asked me
"Why do you create derived classes in a method but then return only the base class"
I couldn't answer the question at the time because I didn't even know myself, until I remember that originally, the method wasn't returning the base class. I only changed it so that it can work with the base view method, which was wrong in the first place!
That's what I get for only getting 3-4 hours of sleep in 3 days. Everything works right now. Thanks.
I'm having a hard time trying to figure out why the data in my view isn't changing after I do a post. Originally I was doing it via return View() and it worked, but since it was a partial view, the page didn't look great, so I was reading up and saw that it was better to do it by Post-Redirect-Get pattern (PRG) and to use an id value to retrieve the values instead of sending the entire model via Tempdata. I even used ModelState.Clear() and that didn't even work. When I debugged the code, the model only has the values from when I first called it.
Here's part of my Get controller:
NewDocument Get Controller
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// This doesn't work. The view keeps on showing the data from View(Services.CreateNewDocument()).
if (id != null)
{
return View(Services.GetdocumentViewModelData(DocEnum.Section.NEW_DOC_INDEX, (int)id));
}
// This works fine
return View(Services.CreateNewDocument());
}
And here's the post that calls the redirect:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
int? DocID = Services.AddingNewDocument(dvm);
// See, I even tried to clear it.
ModelState.Clear();
return base.RedirectToAction("NewDocument", new { id = DocID });
}
else
{
// Display errors in the modal
}
return base.RedirectToAction("NewDocument");
}
And here's the old way I did it:
NewDocument Post controller
[HttpPost]
[ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "AddDocuments")]
//[OutputCache(Duration = 30, VaryByParam = "*")]
public ActionResult AddDocumentViewModel(FormCollection frm, DocumentViewModel dvm)
{
try
{
if (ModelState.IsValid)
{
Services.AddingNewDocument(ref dvm);
dvm.NewRecordMode = DocEnum.Action.UPDATE;
// It worked, but only the partial view showed, and not the entire view.
return PartialView("_NewDocument", dvm);
}
else
{
// Display errors in the model
}
return base.RedirectToAction("NewDocument");
}
Could it be because I'm using a custom model binding?
My Custom Model Binding
public class BaseClassModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var modelType = bindingContext.ModelType;
var modelTypeValue = controllerContext.Controller.ValueProvider.GetValue("ViewModel");
if (modelTypeValue == null)
throw new Exception("View does not contain the needed derived model type name");
var modelTypeName = modelTypeValue.AttemptedValue;
var type = modelType.Assembly.GetTypes().SingleOrDefault(x => x.IsSubclassOf(modelType) && x.Name == modelTypeName);
if (type == null)
{
throw new Exception(String.Format("Derived model type {0} not found", modelTypeName));
}
var instance = bindingContext.Model ?? base.CreateModel(controllerContext, bindingContext, type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, type);
return base.BindModel(controllerContext, bindingContext);
}
}
EDIT: And here's the GetDocumentViewModelData code:
GetDocumentFromViewModelData
public static DocumentViewModel GetDocumentViewModelData(DocEnum.Section docType, int id)
{
switch (docType)
{
case DocEnum.Section.NEW_DOCUMENT_INDEX:
// NewDocumentTypeViewModel is a child to DocumentTypeViewModel
DocumentTypeViewModel nd = NewDocumentService.GetViewModelByID(id);
return nd;
case DocEnum.Section.PDF_DOCUMENT:
DocumentTypeViewModel pdfvm = PDFDocumentService.GetViewModelByID(id);
return pdfvm;
case DocEnum.Section.XLS_DOCUMENT:
DocumentTypeViewModel xlsvm = XLSDocumentService.GetViewModelByID(id);
return xlsvm;
}
return null;
}
Edit: Also adding the GetViewModelByID function
GetViewModelByID
public static DocumentTypeViewModel GetViewModelByID(int id)
{
docEntities db = new docEntities();
NewDocumentTypeViewModel vm = new NewDocumentTypeViewModel();
// Calls a stored procedure called Select_Documents_ByID(id) to get the note entry
// that was submitted.
List<Select_Documents_ByID_Result> prevNotes = db.Select_Documents_ByID(id).ToList();
StringBuilder sNotes = new StringBuilder();
foreach (var note in prevNotes)
{
sNotes.AppendFormat("{0} - {1}: {2}\n\n", note.CreatedDate.ToString("yyyy-MM-dd HH:mm"), note.username, note.Entry);
}
vm.PreviousNotes = sNotes.ToString();
return vm;
}
Edit: I did a direct creation of the view model inside the Get controller, and it's the same result. when i debugged the view itself, the values from the new view model don't show up. Instead, the values from the initial view model, View(Services.CreateNewDocument()), shows.
[DocumentAuthenticationFilter]
public ActionResult NewDocument(int? id = null)
{
// Right here I created the view model to test thing, but I'm getting the same results. Nothing has changed.
if (id != null)
{
var d = new NewDocumentTypeViewModel(1, "Help!");
// This property is from the base class, DocumentTypeViewModel
d.DocumentTitle = "Testing!";
return View(d);
// Inside the view itself, none of the values in the view model, including the one
// belonging to the base class. It still shows the initial values.
}
// This works fine
// Or maybe not...
return View(Services.CreateNewDocument());
}
Edit: I wanted to see if it was also doing the same thing for the initial call to the view return View(Services.CreateNewDocument()), and decided to change the value for documentTitle in the base class from New Document to a randomly-generated number, after the object has been created.
Here's the code for DocumentTypeViewModel's default constructor:
public DocumentTypeViewModel()
{
DocumentTitle = "New Document";
NewRecordMode = DocEnum.Action.ADD;
DocumentID = 0;
}
And here's the Services.CreateNewDocument() code where I change the DocumentTitle after the View Model has been created.
public DocumentTypeViewModel CreateNewDocument()
{
DocumentTypeViewModel dtvm = new DocumentTypeViewModel();
Random r = new Random();
dtvm.DocumentTitle = r.Next(5, Int32.MaxValue).ToString();
return dtvm;
}
Now in the View, when I call DocumentTitle:
<div class="label-text-group">
#Html.LabelFor(model => model.DocumentTitle)
#Html.EditorFor(model => model.DocumentTitle)
</div>
You would expect to see a randomly-generated number every time the View gets called. Nope, what you would see is "New Document". Weird.
It's seems that Services.GetDocumentViewModelData() is not exactly working correctly. It only carries the values created by the base class' constructor when a view is created, not any values that have been added or changed within GetDocumentViewModelData() itself. Why is that? What's going on? Please help anybody!
I have solved it. Look at the Update section on top. Thanks Stephen.

Get link to the file returned by a FileContentResult

In my controller I have created an action that returns images from a database. The images are stored as bytearrays, so I'm returning the contents as a FileContentResult.
In the view, I'm linking to this action with:
<img src="#Url.Action("GetImage", new { id=a.Id })"/>
The controller action looks like
public ActionResult GetImage(long id)
{
var article = SolrOperations.GetArticleById(id);
if (article != null && !string.IsNullOrEmpty(article.Image) && !string.IsNullOrEmpty(article.ImageType))
{
var imageBytes = Convert.FromBase64String(article.Image);
return new FileContentResult(imageBytes, article.ImageType);
}
return null;
}
This does not yield the desired behavior (even though the images are displayed), as I need the full link to the image, ie: /getimage/id.jpg instead of just /getimage/id. The reason for this is that I want to use ImageProcessor.Web to further process (and cache) the images by supplying a query string in the image src attribute (fx src="myimage.jpg?filter=greyscale").
If you want to axtend the link you can just extend your controller method signature, like this:
public ActionResult GetImage(long id , string src, string filter)
ASP.NET MVC will bind this values and on your View you can write like this:
<img src="#Url.Action("GetImage", new { id=a.Id, src = "myimage.jpg", filter = "greyscale" })"/>

Compare Byte Arrays Before Saving To Database

What is the best way of comparing that my image saved to a database isnt different, thus saving I/O.
Scenario:
Im writing an ASP.NET Application in MVC3 using Entity Framework. I have an Edit action method for my UserProfile Controller. Now i want to check that the image i have posted back to the method is different, and if it is, then i want to call the ObjectContext .SaveChanges() if it is the same image, then move on.
Here is a cut down version of my code:
[HttpPost, ActionName("Edit")]
public ActionResult Edit(UserProfile userprofile, HttpPostedFileBase imageLoad2)
{
Medium profileImage = new Medium();
if (ModelState.IsValid)
{
try
{
if (imageLoad2 != null)
{
if ((db.Media.Count(i => i.Unique_Key == userprofile.Unique_Key)) > 0)
{
profileImage = db.Media.SingleOrDefault(i => i.Unique_Key == userprofile.Unique_Key);
profileImage.Amend_Date = DateTime.Now;
profileImage.Source = Images.ImageToBinary(imageLoad2.InputStream);
profileImage.File_Size = imageLoad2.ContentLength;
profileImage.File_Name = imageLoad2.FileName;
profileImage.Content_Type = imageLoad2.ContentType;
profileImage.Height = Images.FromStreamHeight(imageLoad2.InputStream);
profileImage.Width = Images.FromStreamWidth(imageLoad2.InputStream);
db.ObjectStateManager.ChangeObjectState(profileImage, EntityState.Modified);
db.SaveChanges();
}
}
}
}
So i save my image as a varbinary(max) in nto a SQL Server Express DB, which is referenced as a byte array in my entities.
Is it just a case of looping around the byte array from the post and comparing it to the byte array pulled back into the ObjectContext?
Rather than directly comparing the byte array, I would compare the hash of the images. Perhaps something like the following could be extracted into a comparison method:
SHA256Managed sha = new SHA256Managed();
byte[] imgHash1 = sha.ComputeHash(imgBytes1);
byte[] imgHash2 = sha.ComputeHash(imgBytes2);
// compare the hashes
for (int i = 0; i < imgHash1.Length && i < imgHash2.Length; i++)
{
//found a non-match, exit the loop
if (!(imgHash1[i] == imgHash2[i]))
return false;
}
return true;

How to Unit Test JsonResult and Collections in MSTest

I am very new to unit testing even though i have been coding for a very long time. I want to make this a part of my way of development. I run into blocks on how to unit test things like a collection. I generally have my jQuery script calling ASP.Net Server side methods to get data and populate tables and the like. They look like
Get_*Noun*()
which generally returns a JsonResult. Any ideas on what and how to test these using Unit tests using MSTest?
You should be able to test this just like anything else, provided you can extract the values from the JsonResult. Here's a helper that will do that for you:
private T GetValueFromJsonResult<T>(JsonResult jsonResult, string propertyName)
{
var property =
jsonResult.Data.GetType().GetProperties()
.Where(p => string.Compare(p.Name, propertyName) == 0)
.FirstOrDefault();
if (null == property)
throw new ArgumentException("propertyName not found", "propertyName");
return (T)property.GetValue(jsonResult.Data, null);
}
Then call your controller as usual, and test the result using that helper.
var jsonResult = yourController.YourAction(params);
bool testValue = GetValueFromJsonResult<bool>(jsonResult, "PropertyName");
Assert.IsFalse(testValue);
(I am using NUnit syntax, but MSUnit shouldn't be far off)
You could test your JsonResult like this:
var json = Get_JsonResult()
dynamic data = json.Data;
Assert.AreEqual("value", data.MyValue)
Then in the project that contains the code to be tested, edit AssemblyInfo.cs file to allow the testing assembly access to the anonymous type:
[assembly: InternalsVisibleTo("Tests")]
This is so the dynamic can determine the type of anonymous object being returned from the json.Data value;
This is the best blog I've found on this subject.
My favorite was the 4th approach using dynamics. Note that it requires you to ensure that the internals are visible to your test project using [assembly:InternalsVisibleTo("TestProject")] which I find is a reasonably good idea in general.
[TestMethod]
public void IndexTestWithDynamic()
{
//arrange
HomeController controller = new HomeController();
//act
var result = controller.Index() as JsonResult;
//assert
dynamic data = result.Data;
Assert.AreEqual(3, data.Count);
Assert.IsTrue(data.Success);
Assert.AreEqual("Adam", data.People[0].Name);
}
You could use PrivateObject to do this.
var jsonResult = yourController.YourAction(params);
var success = (bool)(new PrivateObject(jsonResult.Data, "success")).Target;
Assert.IsTrue(success);
var errors = (IEnumerable<string>)(new PrivateObject(jsonResult.Data, "errors")).Target;
Assert.IsTrue(!errors.Any());
It's uses reflection similar to David Ruttka's answer, however it'll save you a few key strokes.
See http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.privateobject.aspx for more info.
Here's a small extension to easily convert a Json ActionResult into the object it represents.
using System.Web.Mvc;
public static class WebExtensions
{
public static T ToJson<T>(this ActionResult actionResult)
{
var jsonResult = (JsonResult)actionResult;
return (T)jsonResult.Data;
}
}
With this, your 'act' in the test becomes smaller:
var myModel = myController.Action().ToJson<MyViewModel>();
My suggestion would be to create a model for the data returned and then cast the result into that model. That way you can verify:
the structure is correct
the data within the model is correct
// Assert
var result = action
.AssertResultIs<JsonResult>();
var model = (UIDSearchResults)result.Data;
Assert.IsTrue(model.IsValid);
Assert.AreEqual("ABC", model.UIDType);
Assert.IsNull(model.CodeID);
Assert.AreEqual(4, model.PossibleCodes.Count());

Resources