MVC datetime list not saving - c#

I have a simple MVC4 model that adds a DateTime.Now to a List<DateTime>() list.
However when I do an EntityState.Modified, the changes are not being kept.
I've debugged this by modifying another property in the model and that saves fine.
So I really am at a loss as to why this is not saving. If anyone has any ideas as to why its not saving that would be life saver material:
The Model:
public class Page
{
public int Id { get; set; }
public string PageURL { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<DateTime> Visits { get; set; }
public Page()
{
Visits = new List<DateTime>();
}
}
Here's my code:
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult CookiePolicy()
{
var page = db.Pages.FirstOrDefault(c => c.PageURL == "cookiepolicy");
page.Visits.Add(DateTime.Now); // this list of datetime objects does not get updated
page.Title = "test "; //but this property does
ViewBag.Title = page.Title;
db.Entry(page).State = EntityState.Modified;
db.SaveChanges();
return View(page);
}

Edit Fabio Luz mentioned:
"collection of primitive types (like int, DateTime, bool) are not
supported"
So the solution below seems like the right option.
Ok, so after some deliberation. I decided to create a new model called vist and have this as the list instead of datetime:
public class Visit
{
public int Id { get; set; }
public DateTime DateTime { get; set; }
public BrowserType BrowserType { get; set; }
public String Duration { get; set; }
public int PageId { get; set; }
public virtual Page Page { get; set; }
public Visit()
{
DateTime = DateTime.Now;
BrowserType = BrowserType.Other;
}
}
There are benefits to this. Now I can store more information then just the datetime.
So for anyone who had the same problem as me. Consider pushing it out into its own model for greater flexibility.

Like Fabio Luz mentioned in his comment primitive type collections aren't supported. A collection within an class retrieved from a context is generally assumed to represent a One-to-Many / Many-to-Many relationship.
When building models keep in mind how they would be represented in a SQL table, and having a column that has a collection within is not supported in such a structure. Now, if you were referencing another object (table) than the object (table record) would have certain properties, such as a primary key etc.
Hope this helps.
Edit:
Here is a sample model you might want to consider:
public class Page
{
public int Id { get; set; }
public string PageURL { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public virtual IQueriable<Visit> Visits { get; set; }
}
public class Visit
{
// ... properties related to data you wish to retain about the visit
public virtual Page Page { get; set; } // navigation property
}

Related

Converting infinitely nested objects in .NET Core

EDIT: I originally worded this question very poorly, stating the problem was with JSON serialization. The problem actually happens when I'm converting from my base classes to my returned models using my custom mappings. I apologize for the confusion. :(
I'm using .NET Core 1.1.0, EF Core 1.1.0. I'm querying an interest and want to get its category from my DB. EF is querying the DB properly, no problems there. The issue is that the returned category has a collection with one interest, which has one parent category, which has a collection with one interest, etc. When I attempt to convert this from the base class to my return model, I'm getting a stack overflow because it's attempting to convert the infinite loop of objects. The only way I can get around this is to set that collection to null before I serialize the category.
Interest/category is an example, but this is happening with ALL of the entities I query. Some of them get very messy with the loops to set the relevant properties to null, such as posts/comments.
What is the best way to address this? Right now I'm using custom mappings that I wrote to convert between base classes and the returned models, but I'm open to using any other tools that may be helpful. (I know my custom mappings are the reason for the stack overflow, but surely there must be a more graceful way of handling this than setting everything to null before projecting from base class to model.)
Classes:
public class InterestCategory
{
public long Id { get; set; }
public string Name { get; set; }
public ICollection<Interest> Interests { get; set; }
}
public class Interest
{
public long Id { get; set; }
public string Name { get; set; }
public long InterestCategoryId { get; set; }
public InterestCategory InterestCategory { get; set; }
}
Models:
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public long? InterestCategoryId { get; set; }
}
Mapping functions:
public static InterestCategoryModel ToModel(this InterestCategory category)
{
var m = new InterestCategoryModel
{
Name = category.Name,
Description = category.Description
};
if (category.Interests != null)
m.Interests = category.Interests.Select(i => i.ToModel()).ToList();
return m;
}
public static InterestModel ToModel(this Interest interest)
{
var m = new InterestModel
{
Name = interest.Name,
Description = interest.Description
};
if (interest.InterestCategory != null)
m.InterestCategory = interest.InterestCategory.ToModel();
return m;
}
This is returned by the query. (Sorry, needed to censor some things.)
This is not .NET Core related! JSON.NET is doing the serialization.
To disable it globally, just add this during configuration in Startup
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
}));
edit:
Is it an option to remove the circular references form the model and have 2 distinct pair of models, depending on whether you want to show categories or interests?
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
public List<InterestModel> Interests { get; set; }
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
public class InterestModel
{
public long Id { get; set; }
public string Name { get; set; }
public InterestCategoryModel InterestCategory { get; set; }
public class InterestCategoryModel
{
public long Id { get; set; }
public string Name { get; set; }
}
}
Note that each of the models has a nested class for it's child objects, but they have their back references removed, so there would be no infinite reference during deserialization?

Entity Framework, eager loading entites

I have 3 classes which I would like to 'talk' to each other on a ASP.NET MVC C# WEBAPI app. They are, Item, which can have only one User but the User can make multiple Comments on multiple Items and a Comment can have multiple Users but only one Item
My classes are as follows:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Comment> Comments { get; set; }
public User User { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public ICollection<Item> Items { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string Message { get; set; }
public bool Important { get; set; }
public Item Item { get; set; }
public User User { get; set; }
}
I'm using angularJs front end, and so that I don't get a forever repeating loop I have configured the following:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
I'm using entity framework 6 and I want to Display all items including the comments and the users who have commented
I have read and feel? that Projection using Linq is probably best?
I have the following in my dbContext. (P.S, I've disabled LazyLoading, and including the System.Data.Entity namespace)
using(var _db = new dbContext)
{
var model = _db.Items.Include(i=>i.Comments.Select(p=>p.User).Select(vm=>new ViewModelItem(){
//here I think is where I would say....
ViewModelItem.Name = x.Name,
ViewModelItem.Description = x.Description,
ViewModelItem.Comments = ///
ViewModelItem.Comments.User.Name = ///
})).ToList();
return Ok(model);
}
I'm not sure where to go from here.
So I want to display All the comments and include the User who owns the Item but also include All the comments for that Item, and all the Users who have commented on that Item.
Without causing an infinite loop.
If I'm not being clear, please ask me to clarify. Any help as always is greatly appreciated.
Thank you
Assuming your comments data is good this should do it.
var model = db.Comment.Select(p=>
new ViewModelItem {
Name = p.User.Name,
Comments=p,
Description=p.Item.Description,
});

View Model implementation - less fields than in entities

I have two entities: Person and Quote (in one to many relationship)
Person:
public class Person
{
public int PersonID { get; set; }
[Required]
[StringLength(20]
public string Name { get; set; }
[StringLength(30]
public string Relation { get; set; }
public byte[] Image { get; set; }
[StringLength(50)]
public string ImageMimeType { get; set; }
public virtual ICollection<Quote> Quotes { get; set; }
}
Quote:
public class Quote
{
public int QuoteID { get; set; }
public int PersonID { get; set; }
[Required]
[StringLength(200)]
public string QuoteName { get; set; }
[StringLength(400)]
public string Context { get; set; }
public DateTime? Date { get; set; }
public virtual Person Person { get; set; }
}
I want to make a ViewModel for displaying quotes in short format - I need just a few properties - Person Name, QuoteName and Person Image. I could do something casual like they're showing in every ASP.NET MVC tutorial:
public class QuoteViewModel
{
public IEnumerable<Quote> Quotes { get; set; }
}
Is there a better way rather than creating IEnumerable with type of Quote and loading all properties?
How about creating QuoteShort model and making QuoteViewModel as IEnumerable<QuoteShort> QuotesShort.
In controller I would map every 3 fields from repository to QuoteShort and add it to QuotesShort IEnumerable (even though I don't know how to persist them to QuotesShort IEnumerable )
Some examples appreciated.
You can make a QuoteShort ViewModel with just the few properties you need, and then have your view expect IEnumerable<QuoteShort> as its model. You don't necessarily have to wrap that up in another container.
If you have this:
public class QuoteShort{
public Person Person {get;set;}
public string Name {get; set;}
// etc
}
You can do this in the controller:
var quotes = //however you get your list of quotes
var model = (from q in quotes select new QuoteShort
{ Person = q.Person, Name = q.Name /*etc*/ }).ToList();
return View(model);
What about something like
public class QuotesShortViewModel
{
public IEnumerable<QuoteShortViewModel> QuotesShort { get; set; }
}
public class QuoteShortViewModel
{
// ... the properties you need
}
Create a View that receives a QuotesShortViewModel and iterates through the list, rendering the short quotes as it pleases you.
AutoMapper is useful to map between Models and ViewModels in your controllers.

Seed() not fully updating the database

I'm giving a go through some tutorials (here and here) on ASP.NET MVC, and decided to try a few things on my own. Now, I've got three tables, Resume, Descriptions, SubDescriptions. Here's the code for the three:
public class Resume
{
public Resume()
{
Descriptions = new List<Description>();
}
[Key]
public int ResumeId { get; set; }
[Required]
public string Employer { get; set; }
[DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[DataType(DataType.Date)]
public DateTime EndDate { get; set; }
[Required]
public string Location { get; set; }
[Required]
public virtual ICollection<Description> Descriptions { get; set; }
}
public class Description
{
public Description()
{
SubDescriptions = new List<SubDescription>();
}
[Key]
public int DescriptionId { get; set; }
[ForeignKey("Resume")]
public int ResumeId { get; set; }
[Required]
public string Desc { get; set; }
public virtual Resume Resume { get; set; }
public virtual ICollection<SubDescription> SubDescriptions { get; set; }
}
public class SubDescription
{
[Key]
public int SubDescriptionId { get; set; }
[ForeignKey("Description")]
public int DescriptionId { get; set; }
[Required]
public string Sub { get; set; }
public virtual Description Description { get; set; }
}
And my Seed() is as follows:
protected override void Seed(ResumeDBContext context)
{
context.Resumes.AddOrUpdate(i => i.Employer,
new Resume
{
Employer = "Employer Test",
StartDate = DateTime.Parse("2012-3-26"),
EndDate = DateTime.Parse("2013-10-24"),
Location = "Houston, TX",
Descriptions = { new Description
{ Desc = "DescTest",
SubDescriptions = {new SubDescription {Sub = "SubTest"},
new SubDescription {Sub = "SubTest2"},
new SubDescription {Sub = "SubTest3"}}
},
new Description { Desc = "DescTest2" }}
}
);
}
Now, whenever I run update-database from my Package Manager Console, it says it's running Seed(). However, upon querying the database, my SubDescriptions table is still empty. Everything else populates as expected. I don't receive any errors, or anything of the sort. Am I missing something silly in my associations?
The Resume table is populated properly from the Seed(), and the Descriptions table is populated as well, with the appropriate association to the Resume table. Yet, following the same example to try to populate SubDescriptions, the table is just flat out empty. The associations and navigation properties appear to be set correctly, but as I'm new to this, I'm not 100% certain.
Okay, so I found the answer quite by accident. I dropped and recreated the database, and when it ran Seed() again, it populated all my tables as it should. Initially, I was making changes to the Seed() and updating, hoping that they'd be applied. But since the data already existed in the tables, it wasn't populating.

TryUpdateModel sets property to null on insert even though a value is specified, using MVC4 and Entity Framework

I hope this is an easy one.
Using MVC4 with Entity Framework 4.1.
I'am trying to update a model with some properties passed through from a View with a custom view model's properties.
I have this class.
public class SitePage
{
public System.Guid PageID { get; set; }
public int PageTypeID { get; set; }
public string PageTitle { get; set; }
public Nullable<int> RegionId { get; set; }
public Nullable<int> LockLevelId { get; set; }
public System.DateTime UpdatedDate { get; set; }
public System.Guid UpdatedById { get; set; }
public System.Guid CreatedById { get; set; }
public System.DateTime CreatedDate { get; set; }
public string Url { get; set; }
public int PageStateId { get; set; }
public string SummaryImage { get; set; }
}
My controller
public ActionResult Update(AddPageViewModel model)
{
if (ModelState.IsValid)
{
var g = new Guid(model.PageId);
SitePage UpdatePage = dbSite.SitePages.FirstOrDefault(m => m.PageID == g);
//This is a page update. Not updating all properties
UpdatePage.PageTitle = model.PageTitle;
UpdatePage.GetFirstSection.PageSectionText.PageText = model.PageText;
UpdatePage.PageTypeID = (int)SitePageType.PageTypes.CommunityPage;
UpdatePage.RegionId = model.Region
UpdatePage.CreatedById = userId;
UpdatePage.CreatedDate = DateTime.UtcNow;
UpdatePage.UpdatedById = userId;
UpdatePage.UpdatedDate = DateTime.UtcNow;
TryUpdateModel(UpdatePage);
dbSite.Save();
return RedirectToAction("Community_test", "Community", new { id = UpdatePage.Url });
}
else
{
return RedirectToAction("Community_test", "Community");
}
}
Passing this page view model to the controller
public class AddPageViewModel
{
public string PageTitle { get; set; }
public string PageText { get; set; }
public int Region { get; set; }
public string TagString { get; set; }
public string ParentPageId { get; set; }
public string PageId { get; set; }
}
If i debug I can see that the SitePage instance UpdatePage has all the correct values when TryUpdateModel is called. However all values except RegionId are updated correctly in the db. RegionId gets set as null. If i run a trace on the server the sql entity framework generates is indeed setting RegionId to null even though my model has a value. This problem is intermittent! Some times it works, other it doesnt. 1 in 4 will update correctly and i cant quite see where im going wrong. Im newish to this stuff really.
Our classes are generated from the database and we are using an edmx file. Im not sure if i can make any changes to these classes without them just being overridden the next time we update model from database.
Any ideas or more info needed, give me a shout. Cheers
TryUpdateModel takes the values from the form and populate the given model. This means that setting any values on UpdatePage isn't necessary.
if (ModelState.IsValid)
{
var updatePage = dbSite.SitePages.FirstOrDefault(m => m.PageID == model.PageId);
TryUpdateModel(updatePage);
dbSite.Save();
//...
}
It is good practice to also specify the properties that should be updated.
TryUpdateModel(updatePage, new string[] { "PageTitle", "RegionId", etc.. });
Regarding RegionId getting set to Null.
If i debug I can see that the SitePage instance UpdatePage has all the correct values when TryUpdateModel is called.
Before TryUpdateModel is called or right after?

Categories