asp.net mvc no longer getting values from subclass - c#

Disclaimer; I'm a mvc nub.
I'm kinda stuck, this worked like a couple of days ago, and I'm not sure why it doesn't now. The problem is I'm not getting the subclass data back, only the data from the "normal" properties. I'm populating 5 rows from within Global.asax, and the table ReDayModels is in the DB. The keys are okay also in the DB.
edit: Both tables ReWeekModel and ReDayModel has data. I'm using code first approach.
I have this DB:
public class ReChronoDB : DbContext
{
public DbSet<ReWeekRowModel> WeekRows { get; set; }
}
And this is my DbSet rows
public class ReWeekRowModel
{
public ReWeekRowModel()
{
CreatedDate = DateTime.Now;
Days = new List<ReDayModel>()
{
new ReDayModel {DayName = DayOfWeek.Monday},
new ReDayModel {DayName = DayOfWeek.Tuesday},
new ReDayModel {DayName = DayOfWeek.Wednesday},
new ReDayModel {DayName = DayOfWeek.Thursday},
new ReDayModel {DayName = DayOfWeek.Friday},
new ReDayModel {DayName = DayOfWeek.Saturday},
new ReDayModel {DayName = DayOfWeek.Sunday}
};
}
public DateTime CreatedDate { get; private set; }
[Key]
public int Id { get; set; }
public string Name { get; set; }
[DisplayName("Week #"), Range(1, 53)]
public int WeekOfYear { get; set; }
[Range(2017,2075)]
public int Year { get; set; }
public List<ReDayModel> Days { get; set; }
public Priority Priority { get; set; }
public Status Status { get; set; }
public Category Category { get; set; }
public Responsible Responsible { get; set; }
public ResponseGroup ResponseGroup { get; set; }
public string CaseRef { get; set; }
[StringLength(75), DisplayName("Short Description")]
public string Description { get; set; }
public string Details { get; set; }
}
And here the subclass:
public class ReDayModel
{
[Key]
public int Id { get; set; }
public DayOfWeek DayName { get; set; }
public double Hours { get; set; }
public string Comments { get; set; }
}
And finally, here is the controller with the Index Action.
public class ReChronoController : Controller
{
ReChronoDB _reChronoDb = new ReChronoDB();
// GET: ReChrono
public ActionResult Index(string responsegroup = "", string responsible = "", int week = 0, int year = 0)
{
CultureInfo ciCurr = CultureInfo.CurrentCulture;
ViewBag.CurrentWeek = ciCurr.Calendar.GetWeekOfYear(DateTime.Now, CalendarWeekRule.FirstFourDayWeek,
DayOfWeek.Monday);
ViewBag.CurrentYear = ciCurr.Calendar.GetYear(DateTime.Today);
var model = _reChronoDb.WeekRows.ToList();
// For dropdownlists
ViewBag.Years = _reChronoDb.WeekRows.Select(r => r.Year).Distinct();
ViewBag.Weeks = _reChronoDb.WeekRows.Select(r => r.WeekOfYear).Distinct();
var filteredmodel = ReChronoDomainLayer.FilteredWeekRows(model, responsegroup, responsible, week, year);
return View(filteredmodel);
}
}
When I hover over the "model" variable and drill down, the Days collection for the rows have no values.
edit: Here is the sql that is being executed:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[CreatedDate] AS [CreatedDate],
[Extent1].[Name] AS [Name],
[Extent1].[WeekOfYear] AS [WeekOfYear],
[Extent1].[Year] AS [Year],
[Extent1].[Priority] AS [Priority],
[Extent1].[Status] AS [Status],
[Extent1].[Category] AS [Category],
[Extent1].[Responsible] AS [Responsible],
[Extent1].[ResponseGroup] AS [ResponseGroup],
[Extent1].[CaseRef] AS [CaseRef],
[Extent1].[Description] AS [Description],
[Extent1].[Details] AS [Details]
FROM [dbo].[ReWeekRowModels] AS [Extent1]
edit: this is the context?
public class ReChronoDBInitializer :
DropCreateDatabaseIfModelChanges<ReChronoDB>
{
protected override void Seed(ReChronoDB context)
{
base.Seed(context);
var enumToLookup = new EnumToLookup();
enumToLookup.Apply(context);
ReChronoDomainLayer.CreateDummyEntries(context);
}
}
Seems like its completely ignoring the List Days collection.
I've also tried without the filteredmodel, but i left it in here, so you understand why I have parameters in the action.
Any ideas?

First of all please make sure that ReDayModel is part of ReChronoDB as well:
public class ReChronoDB : DbContext
{
public DbSet<ReWeekRowModel> WeekRows { get; set; }
public DbSet<ReDayModel> DayRows { get; set; }
}
Now your controller action attempts to use lazy loading but it is disabled due to incorrect property type for Days property. If you want to enabled lazy loading your class should look like this:
public class ReWeekRowModel
{
...
public virtual ICollection<ReDayModel> Days { get; set; }
...
}
As an alternative you can use eager loading which would require a change in your action to use .Include() method:
public class ReChronoController : Controller
{
...
public ActionResult Index(string responsegroup = "", string responsible = "", int week = 0, int year = 0)
{
...
var model = _reChronoDb.WeekRows.Include(w => w.Days).ToList();
...
}
}
If you always need the Days collection this might result in better performance. Please read more here.

Related

How to query Entity Framework database for records between 2 dates, then return that information for display on screen

I have an Entity MVC app with a code-first database. I need to produce a search box to search between 2 dates and return the records between those dates.
I will call the method with jQuery/ajax and render the results in a table.
I've tried writing an API, with no success. I am not even sure if this is the correct way to go about it?
namespace Areometrex_Leaflet.Models
{
[Table ("Flight_Lines")]
public class Flight_line
{
[Key]
public string Swath_name { get; set; }
public string Flight_name { get; set; }
public string Swath_record { get; set; }
public string Flight_date { get; set; }
public decimal Start_lat { get; set; }
public decimal Start_long { get; set; }
public decimal End_lat { get; set; }
public decimal End_long { get; set; }
public decimal Altitude { get; set; }
public DateTime Time_start { get; set; }
public DateTime Time_end { get; set; }
public string Sensor { get; set; }
}
public class FlightLineContext : DbContext
{
public DbSet<Flight_line> Flight_Lines { get; set; }
}
}
This is my model that holds the objects in the database. I need to search the "Flight_date" property, that is held in my DB in this following format as an "nvarchar" :
17/11/2018 11:09:18 PM
My current API looks something like this:
[HttpPost]
public IEnumerable<Flight_line> SearchFlight_Line()
{
string start, end;
var rc = RequestContext;
var data = rc.Url.Request.GetQueryNameValuePairs();
{
start = data.FirstOrDefault().Value ?? string.Empty;
end = data.LastOrDefault().Value ?? string.Empty;
}
//db format: 17/11/2018 11:22:56 PM
var s = DateTime.Parse(start);
var flightSearch = new List<Flight_line>();
using (_context)
{
var sql = $"SELECT * FROM Flight_Lines WHERE Flight_Date BETWEEN '{start}' AND '{end}'";
flightSearch = _context.Flight_Lines.SqlQuery(sql).ToList<Flight_line>();
}
return flightSearch;
}
Ideally, I want to call this API with jquery/Ajax and return results to be displayed in an MVC view. My guess is that this is dead easy, but I am only learning and I'm running out of ideas. I would have thought this was really simple, but I am struggling to find the answers I am looking for online, which leads me to believe perhaps I am doing it wrong?
First of all, don't save dates as string in your database, you will just have problems later on.
Instead of:
public string Flight_date { get; set; }
Set it up as DateTime:
public DateTime Flight_date { get; set; }
As far as the query for searching flights go, you can try this. This will return a list of "Flight_line" objects which you can then return wherever you need.
DateTime start = DateTime.Now;
DateTime end = DateTime.Now.AddDays(7);
var flights = _context.Flight_line.Where(f => f.Flight_date >= start && f.Flight_date <= end).ToList();

502 error while converting entity framework data to json. Possible recursion. How to prevent it?

[HttpGet("/api/notes/suggested")]
public JsonResult GetSuggestedNotes(string searchText)
{
//TODO: Podpowiedzi przy wpisywaniu tytułu
JsonResult result = null;
try {
List<Note> n = db.Notes.Include(x => x.NoteTags).ToList();
result = Json(n);
}
catch(Exception e)
{
Console.WriteLine(e);
}
return result;
}
public class Note
{
public Note()
{
CreationDate = DateTime.Now;
NoteTags = new HashSet<NoteTag>();
Parts = new HashSet<Part>();
}
public int ID { get; set; }
public virtual ICollection<NoteTag> NoteTags { get; set; }
public virtual ICollection<Part> Parts { get; set; }
public DateTime? CreationDate { get; set; }
[NotMapped]
public string TagsToAdd { get; set; }
[NotMapped]
public string TagsAsSingleString {
get
{
string result = "";
foreach(var nt in NoteTags)
{
result += nt.Tag.Name + " ";
}
return result;
}
}
}
public class NoteTag
{
public int NoteId { get; set; }
public virtual Note Note { get; set; }
public int TagId { get; set; }
public virtual Tag Tag { get; set; }
}
When I try to get data using this WebAPI controller, I get 502 bad gateway. No errors, everything's fine while debugging server. Data get from database correctly.
I suspect that it could be something similar to "infinite loop" but how to prevent it? (Note class is connected to collection of NoteTag objects that are connected back to Note which probably makes this loop).
And why there are no errors if something went wrong? :/
I don't know if it still relevant but i had the same problem and what worked for me it to Configure Newtonsoft.Json
SerializerSettings.ReferenceLoopHandling = ewtonsoft.Json.ReferenceLoopHandling.Ignore.
If you are using VS2015 MVC you can add the following code:
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
in the ConfigureServices method in the Startup class.
I think the problem its recursion, can you try with an Anonymous type
NoteTags has Note , imagine if the Note->NoteTags->Note->NoteTags->Note->NoteTags ...
`List n = db.Notes.Include(x => x.NoteTags).ToList();
var e = n.select(x=> new {property=value});
result = Json(e);`

Find(System.Object[]) cannot be called with instance of type .ObjectQuery [duplicate]

I want to Find Username by userId
this code snippet working
Discussion_CreateBy = db.AspNetUsers.Find(discussion.CreatedBy).UserName,
and this once not working in following controller class
Comment_CreateBy = db.AspNetUsers.Find(c.CreatedBy).UserName,
this is my model classes
public class DiscussionVM
{
public int Disussion_ID { get; set; }
public string Discussion_Title { get; set; }
public string Discussion_Description { get; set; }
public Nullable<System.DateTime> Discussion_CreateDate { get; set; }
public string Discussion_CreateBy { get; set; }
public string Comment_User { get; set; }
public IEnumerable<CommentVM> Comments { get; set; }
}
public class CommentVM
{
public int Comment_ID { get; set; }
public Nullable<System.DateTime> Comment_CreateDate { get; set; }
public string Comment_CreateBy { get; set; }
public string Comment_Description { get; set; }
}
this is whole controller class
public ActionResult Discussion_Preview()
{
int Discussion_ID = 1;
var discussion = db.AB_Discussion.Where(d => d.Discussion_ID == Discussion_ID).FirstOrDefault();
var comments = db.AB_DiscussionComments.Where(c => c.Discussion_ID == Discussion_ID);
DiscussionVM model = new DiscussionVM()
{
Disussion_ID = discussion.Discussion_ID,
Discussion_Title = discussion.Discussion_Name,
Discussion_Description = discussion.Discussion_Name,
Discussion_CreateBy = db.AspNetUsers.Find(discussion.CreatedBy).UserName,
Discussion_CreateDate = discussion.CreatedDate,
Comments = comments.Select(c => new CommentVM()
{
Comment_ID = c.Comment_ID,
Comment_Description = c.Comment_Discription,
Comment_CreateBy = db.AspNetUsers.Find(c.CreatedBy).UserName,
Comment_CreateDate = c.CreatedDate
})
};
return View(model);
}
Getting following error
Method 'Project.Models.AspNetUser Find(System.Object[])' declared on type 'System.Data.Entity.DbSet1[Project.Models.AspNetUser]' cannot be called with instance of type 'System.Data.Entity.Core.Objects.ObjectQuery1[Project.Models.AspNetUser]'
Discussion_CreateBy = db.AspNetUsers.Find(discussion.CreatedBy).UserName
Works because discussion is an in-memory object because you are executing a query by calling FirstOrDefault on it:
var discussion = db.AB_Discussion.Where(d => d.Discussion_ID == Discussion_ID).FirstOrDefault();
On the other hand in the following statement:
db.AspNetUsers.Find(c.CreatedBy).UserName
c is not queried yet because
db.AB_DiscussionComments.Where(c => c.Discussion_ID == Discussion_ID)
returns an IQueriable and not the actual collection of comments
The easiest way to fix it is to bring all your comments into memory (since you are anyway need them all) :
var comments = db.AB_DiscussionComments.Where(c => c.Discussion_ID == Discussion_ID).ToList();

Error inserting record with entity framework

I am sorry if it has already been answered but I can't find any solution. Here is my (little) problem. Also all my apologies if the terms I use are approximate, I am far from being a skilled C# developer
Note that I think my problem is similar to this one Entity Framework validation error for missing field, but it's not missing?
I have a table "Tweets" with a tweet_id field (bigint) which is my primary key.
I use the following class to load the table :
class TwitterDbContext : DbContext
{
public TwitterDbContext() : base("Twitter")
{
}
public DbSet<Stream> Streams { get; set; }
public DbSet<StreamParameter> StreamParameters { get; set; }
public DbSet<Tweet> Tweets { get; set; }
}
public class Tweet
{
public Tweet()
{
}
[Key]
public long tweet_id { get; set; }
public string tweet { get; set; }
public long creator { get; set; }
public double latitude { get; set; }
public double longitude { get; set; }
public string language { get; set; }
public DateTime created_at { get; set; }
public DateTime registered_at { get; set; }
public long? in_reply_to { get; set; }
public bool retweeted { get; set; }
}
I have an other class to store within the code execution all the fields used by the Tweet table. For the need here, let's imagine I manually create it that way
private void Test_Button_Click(object sender, RoutedEventArgs e)
{
Twts twtReceived = new Twts();
twtReceived.tweet_id = 1;
twtReceived.tweet = "test";
twtReceived.creator = 1;
twtReceived.latitude = -1;
twtReceived.longitude = -1;
twtReceived.language = "a";
twtReceived.created_at = DateTime.Now;
twtReceived.registered_at = DateTime.Now;
twtReceived.in_reply_to = 1;
twtReceived.retweeted = true;
AddTweet(twtReceived);
}
Now here is the AddTweet method
static public void AddTweet(Twts twtReceived)
{
try
{
// update the tweet data in the database
using (var TwitterDb = new TwitterDbContext())
{
Tweet twt = new Tweet()
{
tweet_id = twtReceived.tweet_id,
tweet = twtReceived.tweet,
creator = twtReceived.creator,
longitude = twtReceived.longitude,
latitude = twtReceived.latitude,
language = twtReceived.language,
created_at = twtReceived.created_at,
registered_at = twtReceived.registered_at,
in_reply_to = twtReceived.in_reply_to,
retweeted = twtReceived.retweeted
};
TwitterDb.Tweets.Add(twt);
TwitterDb.SaveChanges();
}
}
catch(Exception ex)
{
MessageBox.Show(ex.InnerException.ToString());
}
}
I constantly have the same error message:
Cannot insert the value NULL into column 'tweet_id', table
'Twitter.dbo.Tweets'; column does not allow nulls. INSERT fails.
The thing is that when I spy on "TwitterDb.Tweets.Local" after TwitterDb.Tweets.Add(twt); I correctly have tweet_id set to 1.
Any idea where is the issue?
Try marking your tweet_id field with following (instead of just [Key]), if this is a primary key column where you want to provide values yourself
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
If it is an auto-increment, then remove explicit assignments to this field and mark it as 'Identity' instead:
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]

Update multiple tables in MVC Edit Action using repository

I have a pair of ViewModels that references data from a number of tables. One for displaying and one for editing.
When I return data from the display ViewModel I can map all the relevant fields using ValueInjecter InjectFrom functionality.
What do I do next to get the database to update?
If I send the models to my Update method in the repository I can see the changes in the model but the context doesn't pick them up. Am I missing a step or is there a better way of doing this?
If I try to modify one table at a time I can get the context to pick up the changes but then get an error as follows:
Store update, insert, or delete statement affected an unexpected
number of rows (0).
---EDIT---
I've updated the code and moved the mapping into the repository but I'm still getting the same error even though the debugger shows the entities with the new values.
ViewModels
public partial class HouseholdEditViewModel //for displaying in browser
{
public int entityID { get; set; }
public int familyID { get; set; }
public string UPRN { get; set; }
public string address { get; set; }
public HousingTypeDropDownViewModel housingTypeID { get; set; }
public KeyworkerDropDownViewModel keyworkerID { get; set; }
public string startDate { get; set; }
public bool loneParent { get; set; }
public string familyPhoneCode { get; set; }
public string familyPhone { get; set; }
}
public partial class HouseholdAddViewModel //for mapping to database
{
public int familyID { get; set; }
public string UPRN { get; set; }
public string address { get; set; }
public int entityTypeID { get; set; }
public int housingTypeID { get; set; }
public int keyworkerID { get; set; }
public DateTime startDate { get; set; }
public bool loneParent { get; set; }
public string familyPhoneCode { get; set; }
public string familyPhone { get; set; }
}
Repository (Current version - I've attempted a few different things without success)
public interface IHouseholdRepository : IDisposable
{
//other methods here...
void Update(HouseholdAddViewModel model, int id);
}
public void Update(HouseholdAddViewModel model, int id)
{
//check address exists
var address = (from u in context.tAddress
where model.UPRN.Contains(u.UPRN)
select u.UPRN);
var ae = new tAddressEntity();
ae.InjectFrom(model);
ae.entityID = id;
ae.UPRN = model.UPRN;
context.tAddressEntity.Attach(ae);
context.Entry(ae).State = EntityState.Modified;
var e = new tEntity();
e.InjectFrom(model);
e.entityID = id;
e.entityName = model.address;
e.tAddressEntity.Add(ae);
context.tEntity.Attach(e);
context.Entry(e).State = EntityState.Modified;
var a = new tAddress();
a.InjectFrom(model);
context.tAddress.Attach(a);
context.Entry(a).State = address.ToString() == string.Empty ?
EntityState.Added :
EntityState.Modified;
var hs = new tHousingStatus();
hs.InjectFrom(model);
hs.entityID = id;
context.tHousingStatus.Attach(hs);
context.Entry(hs).State = EntityState.Modified;
var k = new tKeyWorker();
k.InjectFrom(model);
k.entityID = id;
context.tKeyWorker.Attach(k);
context.Entry(k).State = EntityState.Modified;
var l = new tLoneParent();
l.InjectFrom(model);
l.entityID = id;
context.tLoneParent.Attach(l);
context.Entry(l).State = EntityState.Modified;
var h = new tHousehold();
h.InjectFrom(model);
h.entityID = id;
h.tHousingStatus.Add(hs);
h.tKeyWorker.Add(k);
h.tLoneParent.Add(l);
context.Entry(h).State = EntityState.Modified;
context.SaveChanges();
}
Controller
[HttpPost]
public ActionResult Edit(HouseholdAddViewModel model, int id)
{
model.entityTypeID = _repo.GetEntityType();
if (ModelState.IsValid)
{
_repo.Update(model, id);
return RedirectToAction("Index");
}
return View("Edit", id);
}
The easiest way to update an entity using EF is to retrieve the entity (using
it's key) and then apply the updates to that object instance. EF will automatically detect the updates to the entity and apply them when you call SaveChanges().
It seems as if you're creating new entities and you're not adding them to context so they
aren't being picked up.
I would change your Edit controller to do this
[HttpPost]
public ActionResult Edit(HouseholdAddViewModel model, int id)
{
model.entityTypeID = _repo.GetEntityType();
if (ModelState.IsValid)
{
var h = _repo.GetHousehold(id);
h.InjectFrom(model);
h.entityID = id;
//...
}
}

Categories

Resources