I'm trying to create ASP.NET MVC Application with Entity Framework, which has One to Many relationship. For that I have successfully managed to load the appropriate list of items to a dropdown control (in Create view), But when I click the Create button (in Create view) page validation is faild, validation error message is The value '1' is invalid..
Error
Model
public class Post
{
public int Id { get; set; }
...
public virtual Person Author { get; set; }
}
DataBaseContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Post>()
.HasOptional(p => p.Author);
}
Controller
public ActionResult Create()
{
PopulateAuthorDropDownList();
return View();
}
[HttpPost]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
PopulateAuthorDropDownList(post.Author);
return View(post);
}
private void PopulateAuthorDropDownList(object selectedPerson = null)
{
var personQuery = from d in db.People
orderby d.Name
select d;
ViewBag.Author = new SelectList(personQuery, "Id", "Name", selectedPerson);
}
View
<div class="editor-label">
#Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
#Html.DropDownList("Author", String.Empty)
#Html.ValidationMessageFor(model => model.Author)
</div>
When I check Author table in database there is Record with Id 1, so I guess 1 is a valid value. What am I missing here?
Thanks in advance...
You didn't show how the Author object looks like,
Suppose if it is like this,
public class Author
{
public int Id{get;set;}
public string Name{get;set;}
}
Try this,
<div class="editor-label">
#Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Author.Id, ViewBag.Author, "Select an Author")
#Html.ValidationMessageFor(model => model.Author)
</div>
Managed to fix this by
changing Model to
public class Post
{
public int Id { get; set; }
...
public int Author { get; set; } // changed type to int
}
changing View to
<div class="editor-label">
#Html.LabelFor(model => model.Author)
</div>
<div class="editor-field">
#Html.DropDownList("AuthorId", String.Empty)
#Html.ValidationMessageFor(model => model.Author)
</div>
changing Controller to
private void PopulateAuthorDropDownList(object selectedPerson = null)
{
var personQuery = from d in db.People
orderby d.Name
select d;
ViewBag.AuthorId = new SelectList(personQuery, "Id", "UserName", selectedPerson); //Changed Author to AuthorId
}
and removing
modelBuilder.Entity<Post>()
.HasOptional(p => p.Author);
from DataBaseContext
Anyway Thanks for the answers... :)
You should have something like this:
<div class="editor-label">
#Html.LabelFor(model => model.Author.Name)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Author.Name, (SelectList)ViewBag.AuthorList)
#Html.ValidationMessageFor(model => model.Author.Name)
</div>
Note that you should name ViewBag.AuthorList instead of ViewBag.Author
You have to provide a list of options to DropDownList where you are just passing string.empty.
And i You should use the strongly typed version DropDownListFor:
#Html.DropDownListFor(model => model.Author.Name, Model.AuthorList)
Or maybe even better by not using the name but the Id:
#Html.DropDownListFor(model => model.Author.Id, Model.AuthorList)
And your model:
class Model {
...
public SelectList AuthorList {get;set;}
}
And in your controller:
model.AuthorList = new SelectList(fetchAuthors()
.Select(a => new SelectListItem {
Text = a.Name,
Value = a.Id
}, "Value", "Text");
Related
I have encountered the error shown as my title. I have tried to search for solutions but all I got is solution about using try catch code blocks.
I have been using a course documentation that I have made to guide me on doing this project but the error that I have encountered this time, I am clueless about which part has gone wrong and how to check the wrong part.
There are two parts that I have commented it with // strange comments which means that I have no idea is it where the error occur or something like that.
Thanks for reading my question.
This is my PetRescued Model
public class PetRescued
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string PetName { get; set; }
public int PetAge { get; set; }
[Required]
[StringLength(6)]
public string PetGender { get; set; }
public short PetWeightInKg { get; set; }
public DateTime DateWhenRescued { get; set; }
public PetSpecies PetSpecies { get; set; }
public byte PetSpeciesId { get; set; }
}
This is my PetRescued Controller
public ActionResult New() //populate form
{
var petspecies = _context.PetSpecieses.ToList();
var viewModel = new PetRescuedViewModel
{
PetSpecies = petspecies
};
return View("PetRescued", viewModel);
}
[HttpPost]
public ActionResult Save(PetRescued petRescued)
{
if (petRescued.Id == 0)
_context.PetRescueds.Add(petRescued);
else
{
var petRescuedInDb = _context.PetRescueds.Single(c => c.Id == petRescued.Id);
petRescuedInDb.PetName = petRescued.PetName;
petRescuedInDb.PetAge = petRescued.PetAge;
petRescuedInDb.PetGender = petRescued.PetGender;
petRescuedInDb.PetWeightInKg = petRescued.PetWeightInKg;
petRescuedInDb.PetSpeciesId = petRescued.PetSpeciesId; //strange
petRescuedInDb.DateWhenRescued = petRescued.DateWhenRescued;
}
_context.SaveChanges();
return RedirectToAction("Index", "PetRescued");
}
This is my PetRescued ViewModel
public class PetRescuedViewModel
{
public IEnumerable<PetSpecies> PetSpecies { get; set; }
public PetRescued PetRescueds { get; set; }
public PetRescuedViewModel()
{
PetRescueds = new PetRescued();
}
}
This is my PetRescued Form
#using (Html.BeginForm("Save", "PetRescued"))
{
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetName)
#Html.TextBoxFor(m => m.PetRescueds.PetName, new { #class = "form-control" })
</div>
//strange
<div class="form-group">
#Html.LabelFor(m => m.PetSpecies)
#Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {#class = "form-control"})
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetAge)
#Html.TextBoxFor(m => m.PetRescueds.PetAge, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetGender)
#Html.TextBoxFor(m => m.PetRescueds.PetGender, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetWeightInKg)
#Html.TextBoxFor(m => m.PetRescueds.PetWeightInKg, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.DateWhenRescued)
#Html.TextBoxFor(m => m.PetRescueds.DateWhenRescued, "{0:d MMM yyyy}", new { #class = "form-control" })
</div>
#Html.HiddenFor(m => m.PetRescueds.Id)
<button type="submit" class="btn btn-primary">Save</button>
}
Look at your model definition
// This means this value is required
// and should not be greater than 255 characters
[Required]
[StringLength(255)]
public string PetName { get; set; }
// This means this value is required
// and should not be greater than 6 characters
[Required]
[StringLength(6)]
public string PetGender { get; set; }
So either you are not sending a value from your client app or it is larger than the restrictions you stated.
Change your action method to this to validate your model in your backend (you should never trust the client input)
[HttpPost]
public ActionResult Save(PetRescued petRescued)
{
if (ModelState.IsValid) // Check for errors
{
if (petRescued.Id == 0)
_context.PetRescueds.Add(petRescued);
else
{
var petRescuedInDb = _context.PetRescueds.Single(c => c.Id == petRescued.Id);
petRescuedInDb.PetName = petRescued.PetName;
petRescuedInDb.PetAge = petRescued.PetAge;
petRescuedInDb.PetGender = petRescued.PetGender;
petRescuedInDb.PetWeightInKg = petRescued.PetWeightInKg;
petRescuedInDb.PetSpeciesId = petRescued.PetSpeciesId; //strange
petRescuedInDb.DateWhenRescued = petRescued.DateWhenRescued;
}
_context.SaveChanges();
return RedirectToAction("Index", "PetRescued");
}
else
return View(petRescued); // Return the same view with the original data
// or with the correct model of your view, at least
}
UPDATE
Correct your view model to reflect your correct data. That means, make sure you are sending the correct model to the backend. ASP.Net MVC has something called Model Binding, which is the mechanism used to convert the data received from the client into your C# model. By default, it works by detecting the name of the values passed from the client and finding an exact mapping with the properties of the model. That means that in your view you are declaring this
#Html.TextBoxFor(m => m.PetRescueds.PetName, new { #class = "form-control" })
So, if you inspect the data sent by the browser you will see that the form data includes something like
PetRescueds.PetAge: whatever_the_client_typed
That will not be mapped to your model, because your model doesn't have a property named PetRescueds with a subproperty named PetName, your action model is directly a PetRescued model. So either change your view by specifying directly the name attr like this
#Html.TextBox("PetName", Model.PetRescueds.PetName, new { #class = "form-control" })
Or change your action model to reflect your view model definition. Either way, your view model should be consistent through your action and view. Otherwise, you will end up receiving null values in your action model in spite of filling them correctly on your view, or showing empty values in your views regardless of what you actually created on your controller action.
So, basically, check your model definitions. Make sure you are using a correct model definition to display in your views. Make sure your view is correctly defined as to what you are expecting to receive in your backend controller.
Then, change your view to include validation errors retrieved from the server
#using (Html.BeginForm("Save", "PetRescued"))
{
<!-- This will show your errors-->
#Html.ValidationSummary()
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetName)
<!-- Or you can show errors for each model property -->
<!-- like this -->
#Html.ValidationMessageFor(m => m.PetRescueds.PetName);
#Html.TextBox("PetName", Model.PetRescueds.PetName, new { #class = "form-control" })
</div>
//strange
<div class="form-group">
#Html.LabelFor(m => m.PetSpecies)
#Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {#class = "form-control"})
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetAge)
#Html.TextBoxFor(m => m.PetRescueds.PetAge, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetGender)
#Html.TextBoxFor(m => m.PetRescueds.PetGender, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetWeightInKg)
#Html.TextBoxFor(m => m.PetRescueds.PetWeightInKg, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.DateWhenRescued)
#Html.TextBoxFor(m => m.PetRescueds.DateWhenRescued, "{0:d MMM yyyy}", new { #class = "form-control" })
</div>
#Html.HiddenFor(m => m.PetRescueds.Id)
<button type="submit" class="btn btn-primary">Save</button>
}
You can read more about data validation at Microsofts's
Let's try and fix this.
First, let's change your controller to be able to do something with the errors returned by the model binder.
[HttpGet]
public ActionResult New() //populate form
{
var petspecies = _context.PetSpecieses.ToList();
var viewModel = new PetRescuedViewModel
{
PetSpecies = petspecies
};
return View("PetRescued", viewModel);
}
[HttpPost]
public ActionResult Save(PetRescuedViewModel viewModel)
{
if (ModelState.IsValid) // Check for errors
{
if (petRescued.Id == 0)
_context.PetRescueds.Add(petRescued);
else
{
var petRescuedInDb = _context.PetRescueds.Single(c => c.Id == petRescued.Id);
petRescuedInDb.PetName = viewModel.PetRescued.PetName;
petRescuedInDb.PetAge = viewModel.PetRescued.PetAge;
petRescuedInDb.PetGender = viewModel.PetRescued.PetGender;
petRescuedInDb.PetWeightInKg = viewModel.PetRescued.PetWeightInKg;
petRescuedInDb.PetSpeciesId = viewModel.PetRescued.PetSpeciesId; //strange
petRescuedInDb.DateWhenRescued = viewModel.PetRescued.DateWhenRescued;
}
_context.SaveChanges();
return RedirectToAction("Index", "PetRescued");
}
viewModel.PetSpecies = _context.PetSpecieses.ToList(); // populate the list again as the contents are lost when the form is submitted.
return View("PetRescued", viewModel); // validation errors found, so redisplay the same view
}
Then, change your view to display the errors. We're basically doing what this answer suggests.
#using (Html.BeginForm("Save", "PetRescued"))
{
// Displays a summary of all the errors.
#Html.ValidationSummary()
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetName)
#Html.TextBoxFor(m => m.PetRescueds.PetName, new { #class = "form-control" })
// Or you can add this to each property
#Html.ValidationMessageFor(m => m.PetRescueds.PetName)
</div>
//strange
<div class="form-group">
#Html.LabelFor(m => m.PetSpecies)
#Html.DropDownListFor(m => m.PetRescueds.PetSpeciesId, new SelectList(Model.PetSpecies, "Id", "SpeciesName"), "Select A Species", new {#class = "form-control"})
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetAge)
#Html.TextBoxFor(m => m.PetRescueds.PetAge, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetGender)
#Html.TextBoxFor(m => m.PetRescueds.PetGender, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.PetWeightInKg)
#Html.TextBoxFor(m => m.PetRescueds.PetWeightInKg, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.PetRescueds.DateWhenRescued)
#Html.TextBoxFor(m => m.PetRescueds.DateWhenRescued, "{0:d MMM yyyy}", new { #class = "form-control" })
</div>
#Html.HiddenFor(m => m.PetRescueds.Id)
<button type="submit" class="btn btn-primary">Save</button>
}
The above changes will at least give you which properties are having the problem.
The next step would be to fix the actual problem. If you do the above and can't figure it out further let me know which properties it is and I'll take a look.
I'm guessing it is public byte PetSpeciesId { get; set; } but let's see.
Hope this helps.
You should use the try and catch method to see which fields cause the 'EntityValidationErrors' :
ActionResult Save =>
try
{
_context.SaveChanges();;
}
catch (DbEntityValidationException ex)
{
var sb = new StringBuilder();
foreach (var failure in ex.EntityValidationErrors)
{
sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
foreach (var error in failure.ValidationErrors)
{
sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
sb.AppendLine();
}
}
throw new DbEntityValidationException(
"Entity Validation Failed - errors follow:\n" +
sb.ToString(), ex
);
}
You will know then which records do the exception.
I adding an admin area to an Asp.net MVC4 Application and I would consider myself a entry-level developer. My edit [HttpGet] action is working fine as it returns the values of asset. I have poured through a bunch of MVC4 specials online and most of them use the following for [HttpPost] Edit method:
[HttpPost]
public ActionResult Edit(ITTESI.AssetTracker.Web.UI.ViewModels.AssetDetailsViewModel models)
{
try
{
if (ModelState.IsValid)
{
_entities.Entry(models).State = EntityState.Modified;
_entities.SaveChanges();
return RedirectToAction("Index", new { ID = models.ID });
}
}
catch (DataException)
{
ModelState.AddModelError("", "Unable able to save changes....epic fail!!!");
}
return View(models);
}
The problem is that I do not have access to use .Entry in the following:
_entities.Entry(models).State = EntityState.Modified;
I am obviously missing a reference that will enable me to use it, but I am not sure what it is.
Here is my ViewModel(I would have used DbContext, but they did not):
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using ITTESI.AssetTracker.Web.UI.Content.Classes;
namespace ITTESI.AssetTracker.Web.UI.ViewModels
{
public class AssetDetailsViewModel
{
public int ID { get; set; }
public string AssetIdentifier { get; set; }
public string ManufacturerName { get; set; }
public string Model { get; set; }
public string SchoolLocation { get; set; }
public string Status { get; set; }
public string Condition { get; set; }
// [DataType(DataType.MultilineText)]
public string Notes { get; set; }
public Utils.AssignReturn AssignReturnEligible { get; set; }
public string SchoolLocationCd { get; set; }
public string SchoolLocationDisplayValue { get; set; }
public AssignedUserViewModel AssignedUserViewModelObj { get; set; }
}
}
Here is my Admin Controller:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Metadata.Edm;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using ITTESI.AssetTracker.Data.EntityModel.Entity;
using ITTESI.AssetTracker.Data.EntityModel.Definition;
using System.Data.Entity;
using ITTESI.AssetTracker.Web.UI;
using ITTESI.AssetTracker.Web.UI.Content;
using ITTESI.AssetTracker.Web.UI.Content.Classes;
using ITTESI.AssetTracker.Web.UI.ViewModelBuilders;
using ITTESI.AssetTracker.Web.UI.ViewModels;
namespace ITTESI.AssetTracker.Web.UI.Controllers
{
public class AdminController : Controller
{
ExtendedITTESI_AssetTrackerEntities _entities = new ExtendedITTESI_AssetTrackerEntities();
public ActionResult Index(ViewModels.AssetDetailsViewModel assetDetails)
{
ViewBag.PageTitle = "Admin Search";
ViewBag.HideShowLocation = "hide";
ViewBag.SubmitButtonValue = "Search";
return View("Index", assetDetails);
}
[HttpGet]
public ActionResult Edit(ITTESI_AssetTracker_Asset asset) // 'ITTESI.AssetTracker.Web.UI.ViewModels.AssetDetailsViewModel'
{
ViewBag.PageTitle = "Edit Asset";
ViewBag.SubmitButtonValue = "Save";
ViewBag.ShowLocation = true;
var model = _entities.ITTESI_AssetTracker_Asset.FirstOrDefault();
return View(model);
}
[HttpPost]
//public ActionResult Edit(ExtendedITTESI_AssetTrackerEntities ate)
public ActionResult Edit(ITTESI.AssetTracker.Web.UI.ViewModels.AssetDetailsViewModel models)
{
try
{
if (ModelState.IsValid)
{
_entities.Entry(models).State = EntityState.Modified;
_entities.SaveChanges();
return RedirectToAction("Index", new { ID = models.ID });
}
}
catch (DataException)
{
ModelState.AddModelError("", "Unable able to save changes....epic fail!!!");
}
return View(models);
}
}
}
Here is my Edit View:
#model ITTESI.AssetTracker.Data.EntityModel.Entity.ITTESI_AssetTracker_Asset
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ITTESI_AssetTracker_Asset</legend>
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.AssetIdentifier)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AssetIdentifier)
#Html.ValidationMessageFor(model => model.AssetIdentifier)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AssetConditionID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AssetConditionID)
#Html.ValidationMessageFor(model => model.AssetConditionID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SchoolID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SchoolID)
#Html.ValidationMessageFor(model => model.SchoolID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AssetCategoryID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AssetCategoryID)
#Html.ValidationMessageFor(model => model.AssetCategoryID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.VendorID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.VendorID)
#Html.ValidationMessageFor(model => model.VendorID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AssignedPersonID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AssignedPersonID)
#Html.ValidationMessageFor(model => model.AssignedPersonID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AssetStatusID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AssetStatusID)
#Html.ValidationMessageFor(model => model.AssetStatusID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ManufacturerID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ManufacturerID)
#Html.ValidationMessageFor(model => model.ManufacturerID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ModelDetail)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ModelDetail)
#Html.ValidationMessageFor(model => model.ModelDetail)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreatedOn)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreatedOn)
#Html.ValidationMessageFor(model => model.CreatedOn)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreatedByIdentifier)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreatedByIdentifier)
#Html.ValidationMessageFor(model => model.CreatedByIdentifier)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ModifiedOn)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ModifiedOn)
#Html.ValidationMessageFor(model => model.ModifiedOn)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ModifiedByIdentifier)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ModifiedByIdentifier)
#Html.ValidationMessageFor(model => model.ModifiedByIdentifier)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Notes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Notes)
#Html.ValidationMessageFor(model => model.Notes)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
I also certain the problem is that DbContext for ExtendedITTESI_AssetTrackerEntities does not exist and after research a bit more that is part of the System.Data.Entity. The only code I have for ExtendedITTESI_AssetTrackerEntities is:
using System;
using Common.Logging;
using EFCachingProvider;
using EFCachingProvider.Caching;
using EFProviderWrapperToolkit;
using EFTracingProvider;
using ITTESI.AssetTracker.Data.EntityModel.Entity;
namespace ITTESI.AssetTracker.Data.EntityModel.Definition
{
public class ExtendedITTESI_AssetTrackerEntities : ITTESI_AssetTrackerEntities
{
private ILog logOutput;
public ExtendedITTESI_AssetTrackerEntities()
: this("name=ITTESI_AssetTrackerEntities")
{
}
public ExtendedITTESI_AssetTrackerEntities(string connectionString)
: base(EntityConnectionWrapperUtils.CreateEntityConnectionWithWrappers(
connectionString,
"EFTracingProvider",
"EFCachingProvider"
))
{
CachingPolicy = AssetTrackerEntitiesCachingPolicy.CachingPolicy();
Cache = AssetTrackerEntitiesCache.Cache();
}
#region Tracing Extensions
private EFTracingConnection TracingConnection
{
get { return this.UnwrapConnection<EFTracingConnection>(); }
}
public event EventHandler<CommandExecutionEventArgs> CommandExecuting
{
add { this.TracingConnection.CommandExecuting += value; }
remove { this.TracingConnection.CommandExecuting -= value; }
}
public event EventHandler<CommandExecutionEventArgs> CommandFinished
{
add { this.TracingConnection.CommandFinished += value; }
remove { this.TracingConnection.CommandFinished -= value; }
}
public event EventHandler<CommandExecutionEventArgs> CommandFailed
{
add { this.TracingConnection.CommandFailed += value; }
remove { this.TracingConnection.CommandFailed -= value; }
}
private void AppendToLog(object sender, CommandExecutionEventArgs e)
{
if (this.logOutput != null)
{
this.logOutput.Debug(e.ToTraceString().TrimEnd());
}
}
public ILog Log
{
get { return this.logOutput; }
set
{
if ((this.logOutput != null) != (value != null))
{
if (value == null)
{
CommandExecuting -= AppendToLog;
}
else
{
CommandExecuting += AppendToLog;
}
}
this.logOutput = value;
}
}
#endregion
#region Caching Extensions
private EFCachingConnection CachingConnection
{
get { return this.UnwrapConnection<EFCachingConnection>(); }
}
public ICache Cache
{
get { return CachingConnection.Cache; }
set { CachingConnection.Cache = value; }
}
public CachingPolicy CachingPolicy
{
get { return CachingConnection.CachingPolicy; }
set { CachingConnection.CachingPolicy = value; }
}
#endregion
}
}
I might have to create a model with DbContext is that fair to say?
Can I use anything besides .Entry? I appreciate the help, but with the way the app is set up I am not sure how to properly save edits back to the database.
there is a way besides .Entry for the Edit Action. EF is very clever, you can just fetch the object by ID form the database and change all the properties manually.
Ex:
[HttpPost]
public ActionResult Edit(ITTESI.AssetTracker.Web.UI.ViewModels.AssetDetailsViewModel models)
{
try
{
if (ModelState.IsValid)
{
ITTESI.AssetTracker.Web.UI.ViewModels.AssetDetailsViewModel temp = _entities.dbname.firstOrDefault(x = > x.ID == models.ID);
temp.AssetIdentifier = models.AssetIdentifier ;
temp.ManufacturerName = models.ManufacturerName ;
.
.
.
_entities.SaveChanges();
return RedirectToAction("Index", new { ID = models.ID });
}
}
catch (DataException)
{
ModelState.AddModelError("", "Unable able to save changes....epic fail!!!");
}
return View(models);
}
EF will understand what entity you want to modify, and will update it with "_entities.saveChanges();"
I don't really know that this will work for you, but I think it's worth trying
I have a model class named Server and I have created a new ServerToEdit viewModel class, but when I am trying to submit the viewModel I will get the following error on the repository.save() method.
The model item passed into the dictionary is of type
'TMS.Models.Server', but this dictionary requires a model item of type
'TMS.ViewModels.ServerToEdit'.
The viewModel class is :-
public class ServerToEdit
{
public Server Server { get; set; }
[Required]
public String IPAddress { get; set; }
}
Part of the Create view is:-
model TMS.ViewModels.ServerToEdit
#* This partial view defines form fields that will appear when creating and editing entities *#
#Html.AntiForgeryToken()
<div class="editor-label">
#Html.LabelFor(model => model.Server.CustomerName)
</div>
<div class="editor-field">
#Html.EditorFor(model =>model.Server.CustomerName)
#Html.ValidationMessageFor(model =>model.Server.CustomerName)
</div>
<div class="editor-label">
IP Address
</div>
<div class="editor-field">
#Html.EditorFor(model => model.IPAddress)
#Html.ValidationMessageFor(model => model.IPAddress)
</div>
IPAddress
<div class="editor-label">
#Html.LabelFor(model =>model.Server.ILOIP)
</div>
<div class="editor-field">
#Html.EditorFor(model =>model.Server.ILOIP)
#Html.ValidationMessageFor(model =>model.Server.ILOIP)
</div>
The Create actin method is :-
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Server server, TechnologyIP technologyIP)
{
try
{
if (ModelState.IsValid)
{
repository.InsertOrUpdateServer(server,technologyIP);
repository.Save();
return RedirectToAction("Index");
}
Finally the InsertOrUpdateServer repository method is:-
public void InsertOrUpdateServer(Server server, TechnologyIP technologyIP)
{
if (server.ServerID == default(int))
{
// New entity
int technologyypeID = GetTechnologyTypeID("Server");
Technology technology = new Technology
{
IsDeleted = true,
TypeID = technologyypeID,
Tag = "S" + GetTagMaximumeNumber(technologyypeID).ToString()
};
InsertOrUpdateTechnology(technology);
Save();
var auditinfo = IntiateAudit(tms.AuditActions.SingleOrDefault(a => a.Name.ToUpper() == "ADD").ActionID,
tms.TechnologyTypes.SingleOrDefault(a => a.Name.ToUpper() == "Server").AssetTypeID,
"TDMGROUP\administrator", technology.TechnologyID);
server.ServerID = technology.TechnologyID;
technologyIP.TechnologyID = technology.TechnologyID;
tms.Servers.Add(server);
InsertOrUpdateTechnologyIP(technologyIP);
technology.IsDeleted = false;
InsertOrUpdateTechnology(technology);
InsertOrUpdateAudit(auditinfo);
}
else
{
// Existing entity
var auditinfo = IntiateAudit(tms.AuditActions.SingleOrDefault(a => a.Name.ToUpper() == "EDIT").ActionID,
tms.TechnologyTypes.SingleOrDefault(a => a.Name.ToUpper() == "Server").AssetTypeID,
"TDMGROUP\administrator", server.ServerID);
tms.Entry(server).State = EntityState.Modified;
tms.Entry(technologyIP).State = EntityState.Modified;
InsertOrUpdateAudit(auditinfo);
}
}
thanks in advance for any help ?
First of all, change your ViewModel to this:
public class ServerToEdit
{
public Server Server { get; set; }
public TechnologyIP TechnologyIP { get; set; }
}
And, change your Action to this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ServerToEdit serverToEdit)
{
if (ModelState.IsValid)
{
try
{
repository.InsertOrUpdateServer(serverToEdit.Server, serverToEdit.TechnologyIP);
repository.Save();
return RedirectToAction("Index");
}
catch
{
// Some code...
}
}
}
Then, in your View, you'll have:
#model TMS.ViewModels.ServerToEdit
#* This partial view defines form fields that will appear when creating and editing entities *#
#Html.AntiForgeryToken()
<div class="editor-label">
#Html.LabelFor(model => model.Server.CustomerName)
</div>
<div class="editor-field">
#Html.EditorFor(model =>model.Server.CustomerName)
#Html.ValidationMessageFor(model =>model.Server.CustomerName)
</div>
#Html.HiddenFor(model => model.TechnologyIP.TechnologyID)
<div class="editor-label">
IP Address
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TechnologyIP.IPAddress)
#Html.ValidationMessageFor(model => model.TechnologyIP.IPAddress)
</div>
<div class="editor-label">
#Html.LabelFor(model =>model.Server.ILOIP)
</div>
<div class="editor-field">
#Html.EditorFor(model =>model.Server.ILOIP)
#Html.ValidationMessageFor(model =>model.Server.ILOIP)
</div>
I created a content part and then added it to a content type in Orchard. But when I try to create a content item of that type, the fields for the part's properties are not displayed. I'm looking for suggestions of where the problem might be.
UPD: the relevant code:
using JetBrains.Annotations;
using ArealAds.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using ArealAds.Models;
using ArealAds.Services;
using ArealAds.ViewModels;
namespace ArealAds.Drivers {
[UsedImplicitly]
public class AdDriver : ContentPartDriver<AdPart> {
private readonly IAdService _adService;
public AdDriver (IAdService adService)
{
_adService = adService;
}
protected override string Prefix {
get { return "AdPart"; }
}
protected override DriverResult Display(
AdPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Ad", () => shapeHelper.Parts_Ad(
Title: part.Title,
Url: part.Url,
Email: part.Email,
Phone1: part.Phone1,
Phone2: part.Phone2,
AreaName: part.AreaRecord.Name,
AreaId: part.AreaRecord.Id,
DistrictName: part.DistrictRecord.Name,
DistrictId: part.DistrictRecord.Id,
AllDistricts: part.AllDistricts));
}
//GET
protected override DriverResult Editor(
AdPart part, dynamic shapeHelper) {
return ContentShape("Parts_Ad_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Ad",
Model: BuildEditorViewModel(part),
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(
AdPart part,
IUpdateModel updater,
dynamic shapeHelper) {
var model = new EditAdViewModel();
updater.TryUpdateModel(model, Prefix, null, null);
if (part.ContentItem.Id != 0) {
_adService.Update(
part.ContentItem, model);
}
return Editor(part, shapeHelper);
}
private EditAdViewModel BuildEditorViewModel(AdPart part) {
var avm = new EditAdViewModel {
Title = part.Title,
Url = part.Url,
Email = part.Email,
Phone1 = part.Phone1,
Phone2 = part.Phone2,
AllDistricts = part.AllDistricts,
Areas = _adService.GetAreas(),
Districts = _adService.GetDistricts()
};
if (part.AreaRecord != null) {
avm.AreaName = part.AreaRecord.Name;
avm.AreaId = part.AreaRecord.Id;
}
if (part.DistrictRecord != null) {
avm.DistrictName = part.DistrictRecord.Name;
avm.DistrictId = part.DistrictRecord.Id;
}
return avm;
}
}
}
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
namespace ArealAds.Models {
public class AdRecord : ContentPartRecord {
public virtual string Title { get; set; }
public virtual string Url { get; set; }
public virtual string Email { get; set; }
public virtual string Phone1 { get; set; }
public virtual string Phone2 { get; set; }
public virtual AreaRecord AreaRecord { get; set; }
public virtual DistrictRecord DistrictRecord { get; set; }
public virtual bool AllDistricts { get; set; }
}
public class AdPart : ContentPart<AdRecord> {
[Required]
public string Title {
get { return Record.Title; }
set { Record.Title = value; }
}
public string Url {
get { return Record.Url; }
set { Record.Url = value; }
}
public string Email {
get { return Record.Email; }
set { Record.Email = value; }
}
public string Phone1 {
get { return Record.Phone1; }
set { Record.Phone1 = value; }
}
public string Phone2 {
get { return Record.Phone2; }
set { Record.Phone2 = value; }
}
public AreaRecord AreaRecord {
get { return Record.AreaRecord; }
set { Record.AreaRecord = value; }
}
public DistrictRecord DistrictRecord {
get { return Record.DistrictRecord; }
set { Record.DistrictRecord = value; }
}
[Required]
public bool AllDistricts {
get { return Record.AllDistricts; }
set { Record.AllDistricts = value; }
}
}
}
#model ArealAds.ViewModels.EditAdViewModel
<fieldset>
<legend>Area Fields</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Url)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Url)
#Html.ValidationMessageFor(model => model.Url)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Phone1)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Phone1)
#Html.ValidationMessageFor(model => model.Phone1)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Phone2)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Phone2)
#Html.ValidationMessageFor(model => model.Phone2)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AllDistricts)
</div>
<div class="editor-field">
#Html.CheckBoxFor(model => model.AllDistricts)
#Html.ValidationMessageFor(model => model.AllDistricts)
</div>
<table>
<tr>
<td>
<div class="editor-label">
#Html.LabelFor(model => model.AreaId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.AreaId,
Model.Areas.Select(s => new SelectListItem {
Selected = s.Id == Model.AreaId,
Text = s.Name,
Value = s.Id.ToString()
}),
"Выберите район...")
#Html.ValidationMessageFor(model => model.AreaId)
</div>
</td>
<td>или</td>
<td>
<div class="editor-label">
#Html.LabelFor(model => model.DistrictId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.DistrictId,
Model.Districts.Select(s => new SelectListItem {
Selected = s.Id == Model.DistrictId,
Text = s.Name,
Value = s.Id.ToString()
}),
"Выберите округ...")
#Html.ValidationMessageFor(model => model.DistrictId)
</div>
</td>
</tr>
</table>
</fieldset>
SchemaBuilder.CreateTable("AdRecord", table => table
.ContentPartRecord()
.Column<string>("Title")
.Column<string>("Url")
.Column<string>("Email")
.Column<string>("Phone1")
.Column<string>("Phone2")
.Column<int>("AreaRecord_Id")
.Column<int>("DistrictRecord_Id")
.Column<bool>("AllDistricts")
);
ContentDefinitionManager.AlterPartDefinition(
typeof(AdPart).Name, cfg => cfg.Attachable());
ContentDefinitionManager.AlterTypeDefinition(
"ArealAds_Ad", cfg => cfg
.WithPart("CommonPart")
.WithPart("AdPart")
.Creatable()
);
You're probably missing an entry in placement.info.
Add this into the placement.info file in your module (not the placement.info for yoru theme, since the theme is not active while you're in the dashboard):
`<Place Parts_Ad_Edit="Content:1" />`
I'm stuck and after looking this up for hours, I think I need more eyeballs.
The situation is the following:
It's an Asp.Net MVC3 with Entity Framework 4 project. And I have two classes. One ConfigurationFile and another one Action. There is a one-to-many relationship between the two. Here is a simplified view on the code:
public class ConfigurationFile
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "uniqueidentifier")]
[Required]
public Guid ActionId { get; set; }
public virtual Models.Action Action { get; set; }
}
public class Action
{
[Key, Required]
[Column(TypeName = "uniqueidentifier")]
public Guid Id { get; set; }
[Required]
public string ActionValue { get; set; }
}
Then I want to create a new ConfigurationFile, and are my two controller methods (and at this point, this is 95% Visual Studio 10 generated code):
// db is my context class.
//
// GET: /Configuration/Create
public ActionResult Create()
{
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue");
return View();
}
//
// POST: /Configuration/Create
[HttpPost]
public ActionResult Create(Models.ConfigurationFile configurationfile)
{
if (ModelState.IsValid)
{
configurationfile.Id = Guid.NewGuid();
db.ConfigurationFiles.Add(configurationfile);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ActionId = new SelectList(db.Actions, "Id", "ActionValue", configurationfile.ActionId);
return View(configurationfile);
}
And here is a snippet of my Create view:
#model MyProject.Areas.ConfigurationFile.Models.ConfigurationFile
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Configuration File</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ActionId, "Action")
</div>
<div class="editor-field">
#Html.DropDownList("ActionId", String.Empty)
#Html.ValidationMessageFor(model => model.ActionId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
When I open the Create page, I can clearly see that my dropdown for the Action class is fine (correct value -- the Action.Id -- and text -- Action.ActionValue -- ) but when I submit the form, I have the following error: "The parameter conversion from type 'System.String' to type 'MyProject.Models.Action' failed because no type converter can convert between these types."
Help please !!
Right now MVC has no way of connecting your dropdownlist from your view to the ActionId of your ConfigurationFile object.
I would try replacing this line:
#Html.DropDownList("ActionId", String.Empty)
for this
#Html.DropDownListFor(model => model.ActionId, ViewBag.ActionId)
Other than that, I can't think of what else you might have done wrong.
I hope that helps!
This is how I did to circumvent the problem. I just changed my controller this way:
Models.Action act = db.Actions.Find(configurationfile.ActionId);
ModelState.Clear();
configurationfile.Action = act;
TryValidateModel(configurationfile);
And after that, the validation was Ok. A bit hacky (and another possible hit on the DB), but at least, I can keep going.