I have a Comment class with, among others, an Author property:
public class Comment : IEquatable<Comment>
{
public int ID { get; set; }
[Required]
public virtual User Author { get; set; }
// More properties here
}
I want users to be able to "like" a comment, much like here at StackOverflow. To that end I have the following action in my CommentController:
public virtual ActionResult Like(int id)
{
var comment = _session.Single<Comment>(c => c.ID == id);
comment.Likes++;
_session.CommitChanges();
return Json(new { comment.Likes });
}
Whenever I invoke this action I get the following Validation Error:
The Author field is required.
The Comment object comes from the db, so it does have an author. The "funny" thing is, whenever I use the Visual Studio debugger to check whether the Author really is missing, the validation error does not fire.
Am I correct in assuming here that the problem is that the lazy loading of the Author property never takes place? If so, how can I, for this situation only, force all navigation properties to be filled in? (I want to keep working with lazy loading otherwise)
What's the neatest way to solve this? Am I even on the right track?
And why isn't lazy loading happening while EF clearly requires it to save the entity?
Any help will be appreciated.
You could use Include to eagerly fetch a relation on the data context.
context.Comments.Include("Author").Single<Comment>(c => c.ID == id);
I have had a similar problem. The below should work (with LazyLoading enabled).
public virtual ActionResult Like(int id)
{
var comment = _session.Single<Comment>(c => c.ID == id);
comment.Likes++;
if(TryUpdateModel(comment))
{
_session.CommitChanges();
}
return Json(new { comment.Likes });
}
Related
An odd thing happened after an Add
context.Activities.Add(activity);
context.SaveChanges();
immediately after trying to use the navigation fields
CommandId = activity.CommandId;
if (CommandId != 0)
{
CommandName = activity.Command.CommandName;
}
ActivityCategoryId = activity.ActivityCategoryId;
if (ActivityCategoryId != 0)
{
ActivityCategoryName = activity.ActivityCategory.Name;
}
"Command" is null and "ActivityCategory" is not. Both were created as part of the "Update Model from Database" and both have public null argument constructors. Both work on normal queries say to populate a table. The proxies are present for this. However, after an Add, the proxy for command is not present.
generated code definitions
public virtual ActivityCategory ActivityCategory { get; set; }
public virtual Command Command { get; set; }
If I explicitly "Include", works fine.
public static IList<DAL.Activity> GetActivitiesByCommandI(DAL.MSMTEntities context, int id)
{
IList<DAL.Activity> list = context.Activities
.Include(a => a.Command)
.FilterActivitiesByCommandId(id)
.ToList();
return list;
}
public static IQueryable<DAL.Activity> FilterActivitiesByCommandId(this IQueryable<DAL.Activity> query, int id)
{
IQueryable<DAL.Activity> result = query
.Where(act => act.CommandId == id);
return result;
}
Not sure what to look for next. Will drill down into the context to looks for clues. Appreciate any guidance.
Sorry, this turned out to be a stray entitydatasource in the markup. Why it hacked up on the spatial type, I have not a clue. I downloaded the ef6 from nuget until I can exercise the demon.
Thanks
I've been scratching my head and reading tons of posts but couldn't figure this out.
I have a Person table (see following). Entities in that table can have Children and Parents, who also are in the same table.
public class Person
{
public int ID { get; set; }
public int Name { get; set; }
public virtual ICollection<Person> Children { get; set; }
public virtual ICollection<Person> Parents { get; set; }
}
My model builder in DataContext is like this :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(c => c.Children)
.WithMany(p => p.Parents)
.Map(m=>
{
m.ToTable("ParentChild");
m.MapLeftKey("ParentID");
m.MapRightKey("ChildID");
});
}
This works fine and automatically creates a table named ParentChild with ParentID and ChildID.
Now the thing is, I want to delete a Person who has children, but without deleting the children. So I guess I need to also delete the corresponding line in my ParentChild table (which is auto-generated), but don't know how to do that.
Note : I've also tried to explicitly create the ParentChild table, but this doesn't work, it's rejected when doing add-migration.
To give you more information, here is the error I get when I try to delete one of the row in Person :
The DELETE statement conflicted with the REFERENCE constraint
"FK_dbo.PersonPerson_dbo.Person_Person_ID". The conflict occurred
in database "MyDatabase", table "dbo.ParentChild", column 'ParentID'.
(By the way, I don't know why it talks about a PersonPerson database since I give my auto-generated relation database the name ParentChild. But even if I don't give it a name, it doesn't work).
Last thing, here's the Remove() stub in PersonController (I left it as it was originally made by Scaffolding) :
public ActionResult Delete(int id)
{
try
{
Person person = db.Persons.Find(id);
db.Persons.Remove(person);
db.SaveChanges();
}
catch (RetryLimitExceededException dex)
{
return RedirectToAction("Delete", new { id = id, saveChangesError = true });
}
return RedirectToAction("Index");
}
I hope someone can help me!
Thanks in advance for your help and cheers,
Marius
OK, I finally managed to figure this out.
The solution is modifying the Delete() stub in PersonController, like this :
using (var context = new DataContext())
{
Person person= context.Persons.Find(id);
foreach (var child in person.Children.ToList())
{
person.Children.Remove(child);
}
foreach (var parent in person.Parents.ToList())
{
person.Parents.Remove(parent);
}
context.Persons.Remove(patient);
context.SaveChanges();
}
This way, the Delete() method deletes references to Parents and Children, and reference only. And as it does it at the same time when I call SaveChanges, it avoids trouble with ForeignKey constraints!
Hope this helps someone.
Cheers!
I have problem with updating entites that have many-to many relationship. Below my User and category class:
public class User : IEntity
{
[Key]
public virtual long Id { get; set; }
private ICollection<Category> _availableCategories;
public virtual ICollection<Category> AvailableCategories
{
get { return _availableCategories ?? (_availableCategories = new List<Category>()); }
set { _availableCategories = value; }
}
}
public class Category : IEntity
{
[Key]
public long Id { get; set; }
/// <summary>
/// Full name or description of a category
/// </summary>
[StringLength(255)]
public string FullName { get; set; }
}
This is code snippet from my repository
public override void Edit(User user)
{
var dbUser = _context.Users.Include(x => x.AvailableCategories)
.Single(x => x.Id == user.Id);
var categories = _context.Categories;
dbUser.AvailableCategories.Clear();
foreach (var cat in user.AvailableCategories)
{
dbUser.AvailableCategories.Add(cat);
}
_context.Entry(dbUser).State = EntityState.Modified;
}
However the categories don't get updated. What EF does is insert empty rows into category table and sets relations to this new rows with user.
How can I update User so that I change only categories that already exist in the database?
User that I pass to Edit method has AvailableCategories with only Ids set (rest of properties are empty).
When you're doing something like posting back M2M relationships, you either must post the full object, as in every single property on those objects, or simply post a list of ids and then use those to query the associated objects back from the database. Otherwise, Entity Framework understands your purpose to be to update the properties on the objects as well, in this case with empty values.
Obviously the first option is quite unwieldy, so the second way is the preferred and standard way. Generally, for this, you'd want to use a view model so you could have a property like the following, that you would post into:
public List<long> SelectedCategories { get; set; }
But, if you insist on using the entity directly, you can get much the same result by simply doing:
var selectedCategories = user.AvailableCategories.Select(m => m.Id)
Once you have the ids:
var newAvailableCategories = _context.Categories.Where(m => selectedCategories.Contains(m.Id));
And then finally set that on your user:
dbUser.AvailableCategories = newAvailableCategories;
I notice you are also adding the user.AvailableCategories directly into dbUser.AvailableCategories. I've noticed when binding back complex objects from an MVC view that DB Entities are no longer attached to the DbContext. If you look at the entity, you can verify by checking dbContext.Entry(cat).State is "detached" (or something unexpected) I believe.
You must query those entities back out of the dbContext (possibly by using the returned cat.Id's). Or otherwise manually set the entities as "unchanged". And then add those "non-detached" items into dbUser.AvailableCategories. Please see Chris's answer as it shows with specific code how to get this done.
Also, I might use a linking entity. Possibly something like this:
public class UserCategory
{
public User User {get;set;}
public Category Category {get;set;}
}
And add it to DB context. Also, drop the linking lists in your current User and Category class. This way you can manipulate the UserCategory class (and DbSet) to manage your many-to-many relationship.
I would like to use the built-in validation features as far as possible. I would also like to use the same model for CRUD methods.
However, as a drop down list cannot be done using the standard pattern, I have to validate it manually. In the post back method, I would like to just validate the drop down list and add this result to ModelState so that I don't have to validate all the other parameters which are done with Data Annotation. Is it possible to achieve this?
I may be mistaken about the drop down list, but from what I read, the Html object name for a drop down list cannot be the same as the property in the Model in order for the selected value to be set correctly. Is it still possible to use Data Annotation with this workaround?
Thanks.
You can use the addModelError
ModelState.AddModelError(key,message)
when you use that, it will invalidate the ModelState so isValid will return false.
Update
after seeing the comment to #Pieter's answer
If you want to exclude an element from affecting the isValid() result, you can use the ModelState.Remove(field) method before calling isValid().
Another option is to inherit IValidatableObject in your model. Implement its Validate method and you can leave all other validation in place and write whatever code you want in this method. Note: you return an empty IEnumerable<ValidationResult> to indicate there were no errors.
public class Class1 : IValidatableObject
{
public int val1 { get; set; }
public int val2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var errors = new List<ValidationResult>();
if (val1 < 0)
{
errors.Add(new ValidationResult("val1 can't be negative", new List<string> { "val2" }));
}
if (val2 < 0)
{
errors.Add(new ValidationResult("val2 can't be negative", new List<string> { "val2" }));
}
return errors;
}
}
EDIT: After re-reading the question I don't think this applicable to this case, but I'm leaving the answer here in case it helps someone else.
You cannot manually set the ModelState.IsValid property but you can add messages to the ModelState that will ensure that the IsValid is false.
ModelState.AddModelError();
yes, you can achieve this (also you will use the same model for CRUD methods) :
Example MODEL
public class User
{
public virtual int Id{ get; set; }
public virtual Role Role { get; set; }
}
public class Role
{
[Required(ErrorMessage = "Id Required.")]
public virtual int Id { get; set; }
[Required(ErrorMessage = "Name Required.")]
public virtual string Name { get; set; }
}
Example VIEW with validation on the dropdownlist
#Html.DropDownListFor(m => m.Role.Id, (SelectList)ViewBag.gRoles, "-- Select --")
#Html.ValidationMessageFor(m => m.Role.Id)
CONTROLLER: clearing the required (but not needed here) fields
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Creedit(User x)
{
x.Role = db.RoseSet.Find(x.Role.Id);
if (x.Role != null)
{
ModelState["Role.Name"].Errors.Clear();
}
if (ModelState.IsValid)
{
// proceed
}
else
{
// return validation error
}
}
Might be more recent methods, since this is an old post, but this might help future readers.
One can set a field to valid with this two methods:
ModelState.ClearValidationState("Password");
ModelState.MarkFieldValid("Password");
Need to use both because the second one without the first one it gives an error stating that the state is already marked.
To set a field to invalid, just use ModelState.AddModelError() method as already referred.
I just wondered how people were approaching this situation. It's something that seems like a weak point in my usage of MVC with ORMs (NHibernate in this case)...
Say you have a fine-grained and complicated entity in your model. You will likely have an admin page to manage objects of this type. If the entity is complicated, it is unlikely that you will be modifying the whole entity in one form. You still need to pass the relevant properties to the view, and incorporate changes to those properties in the model when the view returns them.
What does anyone do in this situation?
Create a view model which is (or contains) a subset of the entities properties. Pass this to and from the view. In 'edit' action method in controller, get the object from repository, go though all the properies in the ViewModel and apply them to the Model object (model.a = viewmodel.a, modelb = viewmodel.b). This seems the obvious sensible route, but generates a lot of tedious plumbing code. Also this complicates validation a bit.
Something else?
I've looked briefly at automapper - but this doesn't seem to fit the bill exactly, maybe I'm wrong?
Thanks.
This sounds like the perfect scenario for automapper. You create a view model class which contains a subset of the fields or your real model, and you let AutoMapper take care extraccting values from the domain model object into your view model object. What issues are you having with this approach?
Consider this example:
Here is your domain model and your view model
public class Person
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public string HomeNumber
{ get; set; }
public string Address1
{ get; set; }
public string Address2
{ get; set; }
}
public class PersonViewModel
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public string HomeNumber
{ get; set; }
}
Here is your mapping, you have to create a mapping in both directions from dm->vm and vm->dm.
From what I've seen when using Automapper is that if you map from object A to B and B has a property which A doesn't have, it will be reset. So when I create the map I direct it to ignore those missing properties. I'm not a Automapper expert so I may be using it wrong.
Mapping
Mapper.CreateMap<Person, PersonViewModel>();
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them!
Mapper.CreateMap<PersonViewModel, Person>()
.ForMember(dest => dest.HomeNumber, opt => opt.Ignore());
Finally usage:
Person p = new Person()
{
FirstName = "First",
LastName = "Last",
Address1 = "add 1",
Address2 = "add 2"
};
PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p);
// Map to a new person
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm);
// Map to the existing person just to update it
Person p3 = new Person()
{
HomeNumber = "numberHere"
};
// This will update p3
Mapper.Map<PersonViewModel, Person>(pvm, p3);
Because of the exclusion, this is obviously less than ideal, but much better than manually doing the whole thing.
Have your view model map one-to-one with your domain model.
Specify Model as argument for the routeValues as below. This means your view model will be initialized with the values from the domain model. Only the sub set of fields in the form will be overwritten in the resulting personViewData.
Update View:
#model ViewModel.PersonView
#using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post))
{
...Put your sub set of the PersonView fields here
}
ProfileController:
public ActionResult Update(string userName)
{
Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault();
PersonView personView = new PersonView();
Mapper.Map(person, personView);
return View(personView);
}
[HttpPost]
public ActionResult Update(PersonView personViewData)
{
Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault();
Mapper.Map(personViewData, person);
_unitOfWork.Person.Update(person);
_unitOfWork.Save();
return Json(new { saved = true, status = "" });
}
Why don't you use TryUpdateModel with the form collection.
If your view is editing a person
public class Person
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Address { get; set; }
}
And your view is only editing first name and last name, you can do this:
public ActionResult Action(FormCollection form)
{
Person personToUpdate = Repository.GetPerson(form["ID"]);
TryUpdateModel<Person>(personToUpdate, form);
Repository.Update(personToUpdate)
return View();
}
That will only update Person with the items that a part of the form collection. If you don't want a field updated, don't submit it with the form.
What if you have full model but each page uses and updates only the required part? Then you update the business model using complete view data at the last page.
I use a similar approach to yours (in my case Entity Framework) with Entity -> ViewModel -> View but only on views with "complex" entities that have either 1:M or M:M relationships. In most cases I took the low road and went for Entity->View when I have a simple entity.
My ViewModel is defined as Entity+supporting properties: SelectList or MultiSelectList and either a string or List<string>. I'll also use a ViewModel for instances where I have properties I need for the view but may not necessarily need in the entity (database).
Http Get controller methods are straightforward ActionResults with return View(repository.FetchNewViewModel()) for Create or repository.FetchModelById(id) for Edit. In both instances I'm initializing my entities before passing them to the view.
Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) and Edit(ViewModel model) are the Http Post controller methods of Create and Edit. My Edit view has a hidden input field for EntityId to pass it back and forth.
By the time the Http Post method has the viewmodel, I lose all Entity.Relation and ViewModel.(Multi)SelectList values. I have to rebuild the object if I want my view to display properly:
`
try
{
var tags = model.TagIds; // List<string> or <int> depending on your Id type
if (model.TagsList == null) // It will be
{
model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList
}
if (!ModelState.IsValid)
{
return View(model);
}
_repository.Add(model.Article, tags); // or Save() for Edit
}
catch
{
return View(model); // Generally means something screwed in the repository class
}
return RedirectToAction("Index");
`
There is maybe 30% of my entity base using a ViewModel so I definitely only use it as needed. If you have complex views and model data in most instances you can probably break it down to smaller views.
Right now i´m working on a large project using S#arp Architecture and im also using the approach:
Model -> ViewModel -> Model
I use the ViewModel for the Binding part and Validations, the other approach is to use the Model Directly (with tryUpdateModel / UpdateModel which we used during the prototype develop) but for complex scenarios we end up handling special situation like SelectLists/Checkbox/Projections/HMAC Validations in a little ViewModel anyway and using a lot of Request.Form["key"] =( , the other drawback is handling the errors situations where you want to repopulate the form with the user input, i found it a little more complicated using the Model directly (using a ViewModel we take a lot of advantage of ModelState attempted value, saving us a couple of trips to the DB, anyone who have faced this scenario will know what i mean).
This approach is a bit time consuming, just like you said, you end up matching properties, but in my opinion is the way to go for complex forms.
It worth mentioning that we just use ViewModels for the Create/Edit scenarios, for almost everything else we use directly the model.
I have not use autommapers so far, but definitely i ll give it a try.