EF5 Entry not updating - asp.net-mvc-4

Im trying to update an entry with EF5 with the following actionresult:
[HttpPost]
public ActionResult Edit(int id, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
Reference reference = db.References.Single(x => x.Id == id);
db.Entry(reference).State = EntityState.Modified;
db.SaveChanges();
//Other stuff regarding files/images
return RedirectToAction("Index");
}
return View();
}
Nothing happens. When I debug it, it goes trough the code as everything was fine. But nothing is updated in the db.
Here's the model if needed:
public class Reference
{
public int Id { get; set; }
public string Headline { get; set; }
public string Text { get; set; }
public DateTime Date { get; set; }
public IEnumerable<HttpPostedFileBase> ImageUploadMain { get; set; }
public String MainFileName { get; set; }
public IEnumerable<HttpPostedFileBase> ImageUpload { get; set; }
public virtual ICollection<Image> Files { get; set; }
public virtual ICollection<RefProperties> Properties { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public class Image
{
public int Id { get; set; }
public string FileName { get; set; }
public virtual Reference Reference { get; set; }
}
public class RefProperties
{
public int Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Reference> References { get; set; }
}
Not only is the related entries not updated, a main property like "Headline" cant be updated either. What am I doing wrong? Create/delete works fine btw.

As Gert Arnold says, you're not actually modifying any of the Reference values so nothing will be updated. By calling db.Entry(reference).State = EntityState.Modified you're just setting the retrieved entity's state to modified in the ChangeTracker. When you call SaveChanges() it's just going to update the Reference record in the database with the same values that you fetched.
You need to update some of the Reference instance properties to see a change.
[HttpPost]
public ActionResult Edit(int id, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
Reference reference = db.References.Single(x => x.Id == id);
reference.HeaderText = "Changed";
/* No need to interact with the change tracker as the entity is already tracked and you've made a change */
// db.Entry(reference).State = EntityState.Modified;
/* Create/Modify/Update/Delete other entities */
db.SaveChanges();
//Other stuff regarding files/images
return RedirectToAction("Index");
}
return View();
}

Heres what I was looking for:
TryUpdateModel(reference, "");
It has a shitload of overloads. This works though

Related

Searching method in asp.net core

I'm building an asp.net core searching API which should return a list of videos who has the same searched QR code(the QR code is a ForeignKey)
This is the video model:
public class Video
{
[Key]
public int VideoId { get; set; }
public string Exercice { get; set; }
public string Titre { get; set; }
public int Sexe { get; set; }
public int Categorie { get; set; }
public int Level { get; set; }
public string FilePath { get; set; }
public DateTime DateUpload { get; set; } = DateTime.Now;
[ForeignKey("Machine")]
public string Machine_Qr { get; set; }
public Machine machine { get; set; }
public Coache Coache { get; set; }
}
And this is the search controller:
[HttpGet("{Qr}")]
public async Task<IEnumerable<Video>> Search(string qr)
{
IEnumerable<Video> query = _context.videos.Where(e => e.Machine_Qr == qr);
if ((query != null))
return query;
else
return Enumerable.Empty<Video>().ToList();
}
but I tested it and I got an empty list every time.
Edit: your edit fixed the first logic issue in the original question. But the query also needs to have an await since the function is an async Task.
Since your function returns an IEnumerable, you shouldn't need to call .ToList() ... try this:
[HttpGet("{Qr}")]
public async Task<IEnumerable<Video>> Search(string qr)
{
return await _context.videos.Where(e => e.Machine_Qr == qr) ?? Enumerable.Empty<Video>()
}

Can i add a parent record and a child record using the same _context.SaveChangesAsync()

I have the following 2 Parent/Child objects:-
public Submission()
{
SubmissionQuestionSubmission = new HashSet<SubmissionQuestionSubmission>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Npi { get; set; }
public bool Independent { get; set; }
public string Comment { get; set; }
public virtual ICollection<SubmissionQuestionSubmission> SubmissionQuestionSubmission { get; set; }
}
public partial class SubmissionQuestionSubmission
{
public int SubmissionQuestionId { get; set; }
public int SubmissionId { get; set; }
public string Answer { get; set; }
public virtual Submission Submission { get; set; }
}
and i created the following view model:-
public class SubmissionCreate
{
public Submission Submission {set; get;}
public IList<SubmissionQuestion> SubmissionQuestion { set; get; }
public IList<SubmissionQuestionSubmission> SubmissionQuestionSubmission { set; get; }
}
where i have the following action method to add a parent record (submission) and a child record (SubmissionQuestionSubmission ), but to do so, i have to issue 2 save requests to the database, one to save the parent and get its ID, while the other to save the child record and assign it the parent ID, as follow:-
public async Task<IActionResult> Create(SubmissionCreate sc)//Bind("Id,FirstName,LastName,Npi,Independent,Comment")]
{
if (ModelState.IsValid)
{
var newsubmission = _context.Submission.Add(sc.Submission);
await _context.SaveChangesAsync();
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.SubmissionId = sc.Submission.Id;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
TempData["message"] = "Thank You.. Your request has been submitted...";
return View("Confirmation");
}
return View(sc);
}
so my question is if i can do the above job, using one save statement instead of 2?
You don't need use two SaveChanges. You can assign newsubmission into Submission property instead of v.SubmissionId = sc.Submission.Id;.
In this case Id and ForeignKey created automatically by EF Core
var newsubmission = _context.Submission.Add(sc.Submission);
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.Submission = newsubmission;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
Another way
sc.Submission.SubmissionQuestionSubmission = new List<SubmissionQuestionSubmission>();
foreach (var v in sc.SubmissionQuestionSubmission)
{
sc.Submission.SubmissionQuestionSubmission.Add(v)
}
_context.Submission.Add(sc.Submission);
await _context.SaveChangesAsync();

How can I display the number of posts?

I create a forum and I have a problem: how can I add the number of posts for each category? I would like to use the viewmodel
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Post> Posts { get; set; }
}
public IActionResult Index()
{
var model = categoryService.GetAll();
return View(model);
}
You can add a property to your ViewModel that will retrieve the number of Post's for that Category.
It could look something like this:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Post> Posts { get; set; }
public int PostCount
{
get
{
return Posts != null ? Posts.Count : 0;
}
}
}

Can't correctly add associated objects into Entity Framework Context

I have and entity framework project exposed via a data service:
public class VersionContext : DbContext
{
public DbSet<VersionTreeEntry> VersionTreeEntries { get; set; }
public DbSet<PluginState> PluginStates { get; set; }
public static void SetForUpdates()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<VersionContext, Configuration>());
}
}
public class VersionTreeEntry
{
public VersionTreeEntry()
{
Children = new List<VersionTreeEntry>();
PluginStates = new List<PluginState>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public virtual ICollection<VersionTreeEntry> Children { get; set; }
public virtual ICollection<PluginState> PluginStates { get; set; }
public virtual VersionTreeEntry Ancestor { get; set; }
/// <summary>
/// Links to the ProtoBufDataItem Id for the session state.
/// </summary>
public int DataId { get; set; }
public string Notes { get; set; }
[Required]
public DateTime TimeStamp { get; set; }
[MinLength(1, ErrorMessage = "Tag cannot have a zero length")]
[MaxLength(20, ErrorMessage = "A tag name cannot contain over 20 characters")]
public string Tag { get; set; }
public bool IsUiNodeExpanded { get; set; }
[Required]
public string Version { get; set; }
[Required]
public string SessionName { get; set; }
}
public class PluginState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string PluginName { get; set; }
[Required]
public byte[] Data { get; set; }
}
As far as I can see, the data classes are defined correctly. I try to create some new objects and add them into the context, with their relations intact:
var session = new Session();
session.SessionName = "My new session";
VersionTreeEntry versionTreeEntry = new VersionTreeEntry();
versionTreeEntry.SessionName = session.SessionName;
versionTreeEntry.Version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
versionTreeEntry.TimeStamp = DateTime.Now;
_versionContext.AddToVersionTreeEntries(versionTreeEntry);
foreach (var plugin in session.Plugins)
{
using (var ms = new MemoryStream())
{
plugin.SaveState(ms);
PluginState state = new PluginState();
state.PluginName = plugin.PluginName;
state.Data = ms.ToArray();
versionTreeEntry.PluginStates.Add(state);
}
}
_versionContext.SaveChanges();
The problem is that the PluginState instances never actually get added to the database. If I add code to add them manually to the context, they do get added, but the foreign key pointing back to the VersionTreeEntry is null.
Again, this is a WCF DataService rather than vanilla EF, any idea what might be wrong?
Cheers
Posting the answer here from the comment section.
Agreed. The best way to do this is to call the following API:
_versionContext.AddRelatedObject(versionTreeEntry, "PluginStates", state);
Thanks
Pratik

CodeFirst - Update single property

We are using EF5, Code First approach to an MVC4 app that we're building. We are trying to update 1 property on an entity but keep getting errors. Here's what the class looks like which the context created:
public partial class Room
{
public Room()
{
this.Address = new HashSet<Address>();
}
public int RoomID { get; set; }
public Nullable<int> AddressID { get; set; }
public Nullable<int> ProductVersionID { get; set; }
public string PhoneNumber { get; set; }
public string AltPhone { get; set; }
public string RoomName { get; set; }
public string Description { get; set; }
public string Comments { get; set; }
public string Notes { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
Here's our ViewModel for the view:
public class RoomDetailsViewModel
{
//public int RoomID { get; set; }
public string RoomName { get; set; }
public string PhoneNumber { get; set; }
public string AltPhone { get; set; }
public string Notes { get; set; }
public string StateCode { get; set; }
public string CountryName { get; set; }
public string ProductVersion { get; set; }
public int PVersionID { get; set; }
public List<SelectListItem> ProductVersions { get; set; }
public Room Room { get; set; }
}
Here's the Controller Action being called on "Save":
[HttpPost]
public virtual ActionResult UpdateRoom(RoomDetailsViewModel model)
{
var db = new DBContext();
bool b = ModelState.IsValid;
var rooms = db.Rooms;
var rm = rooms.Where(r => r.RoomID == model.Room.RoomID).Single();
//List<Address> address = db.Addresses.Where(a => a.AddressID == rm.AddressID).ToList<Address>();
rm.ProductVersionID = model.PVersionID;
//rm.Address = address;
db.Entry(rm).Property(r => r.ProductVersionID).IsModified = true;
//db.Entry(rm).State = System.Data.EntityState.Modified;
db.SaveChanges();
return View("RoomSaved", model);
}
All this view does is display data and allow the user to change the Product Version (from a SelectList), so, in the Room Entity, all we are updating is the ProductVersionID property, nothing else. We can get the data to display properly but when we click "save", we get this error:
An object of type 'System.Collections.Generic.List`1[[Models.Address,
Web.Mobile.TestSite, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]' cannot be set or removed from the Value
property of an EntityReference of type 'Models.Address'.
As you can see by the Controller Action, we've tried several different things but all seem to produce this error. I've tried to populate the model.Room.Address collection with an Address, without, but still get this error.
I read this StackOverflow article and this article as well but neither have solved my problem.
ANY help with this would be greatly appreciated!
After hours and hours of digging, turns out that EF did not import some of the PK's for my DB tables. What tipped me off to this was on the Room class, the PK RoomID did not have the [Key] attribute on it. I tried to reimport the table through the edmx but it never came through as a key (even though it's clearly marked PK in the DB). So, to get around it, I created a partial class of my DBContext and override the OnModelCreating event and included the key, like so:
public partial class DBContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Models.Room>().HasEntitySetName("Rooms");
modelBuilder.Entity<Models.Room>().HasKey(r => r.RoomID);
}
}
Once this was done, the Action saved the record as hoped.
I hope this helps someone else!