I am using entity framework 4.3 in my MVC 3 application, when trying to update the entity(creating and deleting works fine) I am getting this error:
Store update, insert, or delete statement affected an unexpected number of rows (0)
When I got into debug mode I saw that on the [HttpPost] method no feed Id was supplied:
public ActionResult Edit(Feed feed)
{
if (ModelState.IsValid)
{
db.Entry(feed).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.FolderId = new SelectList(db.Folders, "FolderId", "Name", feed.FolderId);
return View(feed);
}
although in the normal Get method the id in being passed. those are my entities
feed:
public class Feed
{
[ScaffoldColumn(false)]
public int FeedId { get; set; }
[StringLength(150, ErrorMessage = "The {0} must be less then {1} charecters")]
public string Title { get; set; }
[ScaffoldColumn(false)]
public string Description { get; set; }
[Required(ErrorMessage = "you must enter a valid link")]
[StringLength(500, ErrorMessage = "The {0} must be less then {1} characters long.")]
public string LinkUrl { get; set; }
[ScaffoldColumn(false)]
public DateTime PublishDate { get; set; }
[ScaffoldColumn(false)]
public string Image { get; set; }
[ForeignKey("Folder")]
[Required(ErrorMessage="you must choose a folder")]
public int FolderId { get; set; }
public virtual Folder Folder { get; set; }
public Feed()
{
PublishDate = new DateTime(2012, 1, 1);
}
}
folder:
public class Folder
{
public int FolderId { get; set; }
[Required(ErrorMessage = "you must enter a folder name")]
[StringLength(150, ErrorMessage = "the {0} must be less then {1} charecters")]
public string Name { get; set; }
}
I have looked for a solution but none of them worked, like trying the refresh method, which doesn't exist in DbContext or defining a [Key] property above the FeedId and FolderId.
You shouldn't be manually maintaining the entity state - change tracking should be performed by the context.
You're using a view model and assuming it should be attached to the database.
You need to do something like..,
Feed DbFeed = DBSet.Where(f => f.id = feed.Id);
DbFeed.Property = NewValue;
db.SaveChanges();
(excuse possibly incorrect syntax - I work in VB by default)
That is, get a new instance of the Feed object from the DB Context, then perform changes on the object you're given.
This is because the context doesn't actually give you a plain Feed object, but rather an anonymous type which wraps it and has the same properties. The wrapper overrides your methods and detects property changes which is how it maintains state.
The feed object you get back from your view doesn't contain this wrapper, hence the problems
Entity Framework tracks objects, the feed you get from your view is untracked. The pattern for this situation is to fetch the object you want to update from the db, then call UpdateModel which will apply the changes from your untracked entity to your tracked entity, which you can then save..
if (ModelState.IsValid)
{
var trackedEntity = db.Find(feed.Id)
UpdateModel(trackedEntity);
db.SaveChanges();
return RedirectToAction("Index");
}
apparently but putting the [ScaffoldColumn(false)] attribute in my model it didn't create it in my view and there for the id was not passed.
I added #Html.HiddenFor(model => model.FeedId) to my model and that took care of the issue.
Apparently you were having concurrency problems.
Your update state should be running as:
UPDATE tableA SET colA = 'value' WHERE colX1 = 'compare1' AND colX2 = 'compare2';
This colXn could be your primary keys etc, or could be every column you are using. If you are not handling concurrency, if someone gets the data as the same time as you, alter and save it before you, your WHERE statement will never match, since the record information you are updating already has new information.
Related
I started working on my first serious MVC project for school unfortunately without defining the data annotations to the model first (so did not set "required" annotation, limit to size of attribute names etc.). I work with a viewmodel, and after adding the annotations the model is causing my ViewModel state to get invalid when posting the form.
It seems like it's the email required that is causing the issue. It is not used on viewmodel and in the form and it seems the viewmodel expects it will get it. Is there a way the form to stop demanding this field by setting some limitation in viewmodel (or controller). I would really prefer not to change the structure of the application (if I start from the scratch I would probably do this a bit different, but not much time is left to finalize the project)
Customer (Model)
public Class Customer(){
public int Id { get; set; }
[Required(ErrorMessage = "Required")]
[StringLength(25, ErrorMessage = "Message"]
public string Name { get; set; }
public string Logo { get; set; }
//[Required(ErrorMessage = "Email required")]
//[Display(Name = "E-mail")]
//[RegularExpression(xxxx, ErrorMessage = "not correct")]
public string Email { get; set; }
public int UserId { get; set; }
}
ViewModel
public class CustomerEditViewModel
{
public Customer Customer { get; set; }
[FileTypes("jpg,jpeg,png")]
[FileSize(1024 * 1024, ErrorMessage = "Max x bytes")]
public HttpPostedFileBase File { get; set; }
}
You can remove errors from the modelstate in your controller, e.g.
this.ModelState[key].Errors.Clear();
where key is the bit to be cleared, so if it's email it's most likely -
this.ModelState["Customer.Email"].Errors.Clear();
Useing Entity framework I want to include an only the first level of children objects and not the children of child
I have these two classes:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public ICollection<OffersTBL> OffersTBLs { get; set; }
}
public class OffersTBL
{
public int ID { get; set; }
public string Name { get; set; }
public int CatId { get; set; }
public string BusinessesTBLID { get; set; }
public virtual BusinessesTBL BusinessesTBLs { get; set; }
}
when I try to bring all offers according to CatId field, I need to return the BusinessesTBLs also, but the method also return offers again per each BusinessesTBL obj , My code is :
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs);
}
You can see the wrong result on :
http://mycustom.azurewebsites.net/api/OffersApi/GetOffersTBLsCat/4
As you can see it return all offers under each Business object while business object under each offer, And I want only to return offers with its Business object without offer under Business obj.
Could anyone help please?
I now see that a big part of the original answer is nonsense.
Sure enough, the reason for the endless loop is relationship fixup. But you can't stop EF from doing that. Even when using AsNoTracking, EF performs relationship fixup in the objects that are materialized in one query. Thus, your query with Include will result in fully populated navigation properties OffersTBLs and BusinessesTBLs.
The message is simple: if you don't want these reference loops in your results, you have to project to a view model or DTO class, as in one of the other answers. An alternative, less attractive in my opinion, when serialization is in play, is to configure the serializer to ignore reference loops. Yet another less attractive alternative is to get the objects separately with AsNoTracking and selectively populate navigation properties yourself.
Original answer:
This happens because Entity Framework performs relationship fixup, which is the process that auto-populates navigation properties when the objects that belong there are present in the context. So with a circular references you could drill down navigation properties endlessly even when lazy loading is disabled. The Json serializer does exactly that (but apparently it's instructed to deal with circular references, so it isn't trapped in an endless loop).
The trick is to prevent relationship fixup from ever happing. Relationship fixup relies on the context's ChangeTracker, which caches objects to track their changes and associations. But if there's nothing to be tracked, there's nothing to fixup. You can stop tracking by calling AsNoTracking():
db.OffersTBLs.Include(s => s.BusinessesTBLs)
.AsNoTracking()
If besides that you also disable lazy loading on the context (by setting contextConfiguration.LazyLoadingEnabled = false) you will see that only OffersTBL.BusinessesTBLs are populated in the Json string and that BusinessesTBL.OffersTBLs are empty arrays.
A bonus is that AsNoTracking() increases performance, because the change tracker isn't busy tracking all objects EF materializes. In fact, you should always use it in a disconnected setting.
You have deactivated lazy loading on OffersTBLs making it non-virtual. What if you activate lazy loading? like this:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
//put a virtual here
public virtual ICollection<OffersTBL> OffersTBLs { get; set; }
}
Then, be sure to not call/include OffersTBLs when serializing. If the OffersTBLs are still returning, it is because you are fetching them somewhere in your code. If this is happening, edit your question and paste all the code, including the serializing logic.
Since OffersTBL has an association to BusinessesTBL and BusinessesTBL to OffersTBL you can loop infinitly throw the Entities like OffersTBL.BusinessesTBL.OffersTBL.BusinessesTBL and so on.
To control the nested depth of the Entities i'm usually using helperclasses with the needed properties in them.
For BusinessesTBL
public class BusinessesTBLHelper
{
private BusinessesTBLHelper(BusinessesTBL o){
ID = o.ID;
FirstName = o.FirstName;
lastName = o.LastName;
OffersTBLids = new List<int>();
foreach(OffersTBL offersTbl in o.OffersTBLs){
OffersTBLids.Add(offersTbl.ID);
}
}
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public IEnumerable<int> OffersTBLids { get; set; } //no references anymore
}
And same for your OffersTBL Entity.
public class OffersTBLHelper
{
private OffersTBLHelper(OffersTBL o){
ID = o.ID;
Name = o.Name;
CatId = o.CatId;
BusinessesTBLID = o.BusinessesTBLID;
BusinessesTBLs = new BusinessesTBLHelper(o.BusinessesTBLs);
}
public string ID { get; set; }
public string Name{ get; set; }
public intCatId{ get; set; }
public string BusinessesTBLID { get; set; }
public BusinessesTBLHelper BusinessesTBLs { get; set; }
}
On quering database you can directly create the new helperobjects from queryresult:
public IEnumerable<OffersTBLHelper> GetOffersTBLsCat(int id)
{
return db.OffersTBLs.where(s => s.CatId == id).Select(x=> new OffersTBLHelper(x)).ToList();
}
Now you have all the OffersTBL with BusinessesTBLs under. The loop stops here because the BusinessesTBLs have no OffersTBL under it. However, it only has them Ids in a List for further referencing and identifying.
Assuming that the object isnt null and just empty:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs).Where(x => !x.BusinessesTBLs.OffersTBLs.Any());
}
Edit: Filter before the include:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Where(x => !x.BusinessesTBLs.OffersTBLs.Any())
.Include(s => s.BusinessesTBLs);
}
I have these two models:
public partial class Country
{
public Country()
{
this.Dinners = new HashSet<Dinner>();
}
public int CountryID { get; set; }
public string Name { get; set; }
public virtual ICollection<Dinner> Dinners { get; set; }
}
and
public partial class Dinner
{
public Dinner()
{
this.TRsvps = new HashSet<TRsvp>();
}
public int DinnerID { get; set; }
public System.DateTime EventDate { get; set; }
public string Description { get; set; }
public string HostedBy { get; set; }
public Nullable<int> CountryID { get; set; }
public virtual Country Country { get; set; }
}
I got a bit confused on how Entity Framework will act when a user tries to delete a parent entity (in our case it is the Country entity) that has child records (Dinners).
For example if I have the following code inside my mvc action method:-
public ActionResult DeleteConfirmed(int id)
{
Country country = db.Countries.Find(id);
db.Countries.Remove(country);
db.SaveChanges();
return RedirectToAction("Index");
}
And exception will be raised if I try to remove a country which has dinners, which sounds valid.
I tried modifying my code as follow, by including the Dinners when retrieving the Country object:
Country country = db.Countries.Include(a => a.Dinners).Single(a2 => a2.CountryId = id);
db.Countries.Remove(country);
db.SaveChanges();
return RedirectToAction("Index");
No exception will be raised, so I thought that EF would have deleted the child dinners, but what happens is that it updates the countryID FK inside the Dinners table to be null.... (Cascade Set to Null)
I tried looping over the Dinners collection as follows:
public ActionResult DeleteConfirmed(int id)
{
Country country2 = db.Countries.Find(id) ;
foreach(var d in country2.Dinners)
{
db.Dinners.Remove(d);
}
db.Countries.Remove(country2);
db.SaveChanges();
return RedirectToAction("Index");
}
but this raised the following error:
An exception of type 'System.InvalidOperationException' occurred in
System.Core.dll but was not handled in user code
Additional information: Collection was modified; enumeration operation
may not execute.
I realized that I should explicitly call the .Tolist() on the foreach to get the parent and all its children deleted as follows:
foreach(var d in country2.Dinners.ToList())
Can anyone advice if I getting things wrong, or this is the only way to support cascade on delete using EF ?
Thanks
If you want your deletes to cascade automatically, in your OnModelCreating method, you need to manually enable it:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Country>().WillCascadeOnDelete(true);
}
You are describing the documented behaviour for cascade delete when the foreign key is nullable:
If a foreign key on the dependent entity is nullable, Code First does
not set cascade delete on the relationship, and when the principal is
deleted the foreign key will be set to null.
If you want it to cascade delete then the relationship is required and the foreign key should not be nullable. Change public Nullable<int> CountryID { get; set; } to public int CountryID { get; set; }
Reference:
http://msdn.microsoft.com/en-us/data/jj591620.aspx#CascadeDelete
Additional info following your comment
You don't have to .Include to get cascade delete to work on required relationships i.e. once you have made the foreign key non-nullable. I am sure of this because that is how my application works.
I think you are observing that Remove marks your entire object graph for removal - whether required or not - in the same way that Add marks the entire graph for insertion. NB - I am not 100% sure of this bit so you should test this before you rely on it.
Further reading here:
using Dbset.Add Versus using EntityState.Added
Why Does Entity Framework Reinsert Existing Objects into My Database?
What is the difference between IDbSet.Add and DbEntityEntry.State = EntityState.Added?
I have a DbDataController which delivers a List of Equipment.
public IQueryable<BettrFit.Models.Equipment> GetEquipment() {
var q= DbContext.EquipmentSet.OrderBy(e => e.Name);
return q;
}
In my scaffolded view everything looks ok.
But the Equipment contains a HashSet member of EquipmentType. I want to show this type in my view and also be able to add data to the EquipmentType collection of Equipment (via a multiselect list).
But if I try to include the "EquipmentType" in my linq query it fails during serialisation.
public IQueryable<BettrFit.Models.Equipment> GetEquipment() {
var q= DbContext.EquipmentSet.Include("EquipmentType").OrderBy(e => e.Name);
return q;
}
"Object Graph for Type EquipmentType Contains Cycles and Cannot be Serialized if Reference Tracking is Disabled"
How can I switch on the "backtracking of references"?
Maybe the problem is that the EquipmentType is back-linking through a HashSet? But I do not .include("EquipmentType.Equipment") in my query. So that should be ok.
How is Upshot generating the model? I only find the EquipmentViewModel.js file but this does not contain any model members.
Here are my model classes:
public class Equipment
{
public Equipment()
{
this.Exercise = new HashSet<Exercise>();
this.EquipmentType = new HashSet<EquipmentType>();
this.UserDetails = new HashSet<UserDetails>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Picture { get; set; }
public string Link { get; set; }
public string Producer { get; set; }
public string Video { get; set; }
public virtual ICollection<EquipmentType> EquipmentType { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
public class EquipmentType
{
public EquipmentType()
{
this.Equipment = new HashSet<Equipment>();
this.UserDetails = new HashSet<UserDetails>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Equipment> Equipment { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
try decorating one of the navigation properties with [IgnoreDataMember]
[IgnoreDataMember]
public virtual ICollection<Equipment> Equipment { get; set; }
The model generated by upshot can be found on the page itself. In your Index view you will see the UpshotContext HTML helper being used (given that you are using the latest SPA version), in which the dataSource and model type are specified.
When the page is then rendered in the browser, this helper code is replaced with the actual model definition. To see that, view the source code of your page in the browser and search for a <script> tag that starts with upshot.dataSources = upshot.dataSources || {};
Check here for more info about how upshot generates the client side model.
As for the "backtracking of references", I don't know :)
I figured out - partially how to solve the circular reference problem.
I just iterated over my queried collection (with Include() ) and set the backreferences to the parent to NULL. That worked for the serialisation issue which otherwise already breaks on the server.
The only problem now is the update of a data entity - its failing because the arrays of the referenced entitycollection are static...
To solve the cyclic backreference, you can use the IgnoreDataMember attribute. Or you can set the back reference to NULL before returning the data from the DbDataController
I posted a working solution to your problem in a different question, but using Entity Framework Code First.
https://stackoverflow.com/a/10010695/1226140
Here I show how to generate your client-side model manually, allowing to you to map the data however you please
I've got this Venue object:
public class Venue
{
public int Id { get; set; }
[Required]
[MaxLength(512)]
public string Name { get; set; }
public string Description { get; set; }
[Required]
[Display(Name = "Venue Type")]
public int VenueTypeId { get; set; }
public virtual VenueType VenueType { get; set; }
[Required]
[Display(Name = "Company")]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
As you can see, it has a collection of VenueParts. I send the Venue to the view, and output the collection of VenueParts as a table of textboxes. This gets posted back to Edit(VenueDetailsViewModel venueDetailsViewModel). Using the debugger, I can verify that my change are in the VenueParts collection, so I think we're good on binding.
My controller tries to update the Venue. It succeeds on the properties directly on the object, such as Name. But, unless I loop through the collection, it does not update those objects. Is that typical behavior?
unitOfWork.VenueRepository.Update(venueDetailsViewModel.Venue);
// Should this loop be necessary?
foreach (var venuePart in venueDetailsViewModel.Venue.VenueParts)
{
unitOfWork.VenuePartRepository.Update(venuePart);
}
unitOfWork.Save();
At the moment, I'm not even worried about handling new stuff in the list or things that vanished from the list (although that is what I am tackling next). For my first step here, I just want to get the list updated. Is it necessary to loop through the collection and update each individual object? If I don't do this, they don't save. But it seems like they ought to without my loop. Are my expectations too high or am I doing something wrong?
My repository and unitOfWork objects are patterned after this tutorial if you are curious what that code looks like.
That is because unitOfWork.VenueRepository.Update(venueDetailsViewModel.Venue); will attach the object graph in Unchanged state and only change the venue as Modified. One alternative would be to move the foreach loop to the VenuePartRepository.Update method.
If you allow elements of VenueParts to be added or removed from the UI you will have a hard time applying the changes. If this is the case you will have to load the collection in the database and compare that with the changes coming in. Then manually change the states of VenuePart to Added or Deleted.