I encountered a very strange (and annoying) issue:
in MVC 4 web application while loading data from the database and using a foreach on the Model within the view:
#foreach (var meeting in Model)
i have a breakpoint in the method's beginning and examining the object meeting i see that it misses some data (reference to other tables). if I open (+) other object within meeting the missing data appears.
Why?
Thanks!
here is my controller method:
public ActionResult GetMeetingMR(int id = 0)
{
var meetingPurpose = db.MeetingPurposes.ToList();
ViewBag.MeetingPurpose = new SelectList(meetingPurpose, "MeetingPurposeID", "MeetingPurposeName");
ViewBag.MRID = id;
List<Meeting> meetings = (db.MortgageRequests.Find(id)).Meetings.ToList();
return View(meetings);
}
Including your title in your question, my guess is you do not experience the problem (yet) without the debugger attached.
As others have commented it is due to LazyLoading.
The problem may occur sometimes because while rendering the View the entities container is being disposed, most probably because it is collected by the GarbageCollector which calls the destructor of the container.
If you wish to access the entities on your View you can use Include on the ObjectSet.
Let's say MeetingPurposes has an associated EntitySet of Dates:
If you wish to include them in the loadoperation you can do the following:
db.MeetingPurposes.Include("Dates").ToList();
public ActionResult GetMeetingMR(int id = 0)
{
//Load the MeetingPurposes including their dates
var meetingPurpose = db.MeetingPurposes.Include("Dates").ToList();
ViewBag.MeetingPurpose = new SelectList(meetingPurpose,
"MeetingPurposeID",
"MeetingPurposeName");
ViewBag.MRID = id;
List<Meeting> meetings = (db.MortgageRequests.Find(id)).Meetings.ToList();
return View(meetings);
}
If you wish to do this for Meetings and Meetings has an EntitySet of Dates you could do:
public ActionResult GetMeetingMR(int id = 0)
{
var meetingPurpose = db.MeetingPurposes.ToList();
ViewBag.MeetingPurpose = new SelectList(meetingPurpose,
"MeetingPurposeID",
"MeetingPurposeName");
ViewBag.MRID = id;
var meetings = (from mee in db.Meetings.Include("Dates")
join mga in dbo.MortgageRequests.Where(m => m.Id == id)
on mee.MortgageRequestID equals mga.ID
select mee).ToList();
return View(meetings);
}
Not really sure what your Find extension method is so I used a Where on MortgageRequests.
Related
We have a design paradigm we are considering standardizing to but it has some problems and none of us have a good solution for it.
1) We create a core model that contains all the general fields and index values of any foreign keys. This is tied to a table and in our design will be edited in a popup from our grids.
2) a second class inherits the first one and has all foreign key references. This class would populate grids. This would allow us to show specific info from foreign tables.
Now for the Problem: The popup model must match the model for the grid (we are using telerik grids) and when we save, EF tries to validate anything even remotely connected to the table through the foreign keys and anything they reference through foreign keys etc. We once had a record throw an error because we tried to edit an order and the truck assigned to the order's driver was missing mileage info.
I tried (successfully) to replace foreign key references with notmapped fields and load them in the index file but it was slow compared to EF. I looped through each record in the grid (for each) and looked up each matching model and added it.
Another programmer got around the problem by creating two completely different models and having the 2nd model with the foreign keys point to an alias of the table. Personally this feels illegal to me.
Our third developer did something similar but pulled data from a view. He actually had a good reason though as the view had a calculated "locked" column based on financial settlement.
So there you have it. Three different methods to solve the same issue. I've also tried "exclude" from the post but i was evidently doing it wrong.
We considered shutting off the validation in EF but nobody is familiar enough with it to know if it kills the validation on that page as well. MVC is a recent step for us. Our site was originally in asp. The driving force in fact was our orders grid which could take a minute to load.
**So after i gave you all that info, here's the question: ** What is the best way to set our models up? I am the junior programmer and feel like we are doing it wrong in all three scenarios. More importantly i feel like it's completely unnecessary. I would like to bind a model to a grid with full data that inherits a core model but that's all we validate. How is that done?
Example controller:
namespace DispatchCrude.Controllers
{
[Authorize(Roles = "viewShipperTimeCardWorkTypeRates")]
public class ShipperTimeCardWorkTypeRatesController : _DBController
{
[HttpGet]
public ActionResult Index(int? orderid, int? typeid)
{
// Send through any filters
if (orderid != null)
ViewBag.OrderID = orderid;
if (typeid != null)
ViewBag.TypeID = typeid;
return View();
}
// Read
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
virtual public ContentResult Read([DataSourceRequest] DataSourceRequest request = null, int id = 0, int? orderid = null)
{
return Read(request, null, id, orderid);
}
protected ContentResult Read([DataSourceRequest] DataSourceRequest request, ModelStateDictionary modelState, int id = 0, int? orderid = null)
{
var data = db.ShipperTimeCardWorkTypeRates
.Where(m => m.ID == id || id == 0).ToList();
if (orderid != null)
{
var validRates = db.Database.SqlQuery<int>(#"SELECT R.ID
FROM dbo.viewOrder O
CROSS APPLY dbo.fnShipperTimeCardRates(isnull(O.OrderDate, O.DueDate), null, null,
O.TicketTypeID, O.DestTicketTypeID, O.CustomerID, CarrierTypeID, O.CarrierID, DriverGroupID, O.DriverID, O.TruckTypeID, O.ProductGroupID,
O.DestinationID, O.OriginID, O.DestStateID, O.OriginStateID, O.DestRegionID, O.OriginRegionID, O.ProducerID, 0) R
WHERE O.ID = " + orderid);
data = data.Where(m => validRates.Contains(m.ID)).ToList();
}
return ToJsonResult(data, request, modelState);
}
// Create
[HttpPost]
[Authorize(Roles = "createShipperTimeCardWorkTypeRates")]
public ActionResult Create([DataSourceRequest] DataSourceRequest request, ShipperTimeCardWorkTypeRateBase timeCardWorkTypeRates)
{
// Create functionality is now taken care of in the update function as of Kevin's 2017 update to how MVC controllers are written
return Update(request, timeCardWorkTypeRates);
}
// Update
[HttpPost]
[Authorize(Roles = "editShipperTimeCardWorkTypeRates")]
public ActionResult Update([DataSourceRequest] DataSourceRequest request, ShipperTimeCardWorkTypeRateBase timeCardWorkTypeRates)
{
try
{
DateTime checkDate1;
DateTime checkDate2;
bool success1 = DateTime.TryParse(timeCardWorkTypeRates.EffectiveDate.ToString(), out checkDate1);
bool success2 = DateTime.TryParse(timeCardWorkTypeRates.EndDate.ToString(), out checkDate2);
if (success1 && success2 && checkDate2.Subtract(checkDate1).TotalHours < 0)
{
ModelState.AddModelError("Update", "End Date must be equal to or later than Start Date.");
}
if (ModelState.IsValid)
{
// Create new record or update existing record
db.AddOrUpdateSave(User.Identity.Name, timeCardWorkTypeRates);
}
}
catch (Exception ex)
{
ModelState.AddModelError("Update", ex.Message); // TODO: use a common routine to "cleanup" db generated errors
return App_Code.JsonStringResult.Create(new[] { timeCardWorkTypeRates }.ToDataSourceResult(request, ModelState));
}
return Read(request, ModelState, timeCardWorkTypeRates.ID);
}
// Delete (Deactivate)
[HttpPost]
[Authorize(Roles = "deleteShipperTimeCardWorkTypeRates")]
public ActionResult Delete([DataSourceRequest] DataSourceRequest request, ShipperTimeCardWorkTypeRateBase shipperTimeCardWorkTypeRate)
{
// Delete the record
db.ShipperTimeCardWorkTypeBases.Attach(shipperTimeCardWorkTypeRate);
db.ShipperTimeCardWorkTypeBases.Remove(shipperTimeCardWorkTypeRate);
db.SaveChanges(User.Identity.Name);
return null;
}
public ContentResult getBestMatch(int orderid, int? typeid = null)
{
string sql = "SELECT DISTINCT ID FROM fnOrderShipperTimeCardRates(" + orderid + ")";
if (typeid != null)
sql += " WHERE WorkTypeID = " + typeid;
var bestmatchids = db.Database.SqlQuery<int>(sql);
return ToJsonResult(bestmatchids.ToList());
}
}
}
`
the popup editor uses #model DispatchCrude.Models.ShipperTimeCardWorkTypeRate
the grid uses the same. This split works but EF won't allow us to set two models to the same table, and the inheriting didn't work either.
What do we do?
I am trying to add records from table position for positionName(s) to let user select a position for employee when editing.My last attempts is to add a navigation property like field in company model
public virtual ICollection<Position> Mpositions { get; set; }
But all I got so far is null ref exception or no element in viewModel with property "PositionName" ass per viewbag didn't bother using everybody keeps recommending to avoid it so not going to do so either.
public ActionResult Edit([Bind(Include = "CompanyID,CompanyName,EntityForm,Address,Dissolute,CreationDate,FiscaleYear,Description")] Company company)
{
var GlbUpdate = db.Companies.Include(c => c.Members).Include(p => p.Mpositions);
List<Company> mdlCompanies = new List<Company>();
foreach (var item in GlbUpdate)
{
if ((item.Mpositions==null) || (item.Mpositions.Count() == 0))
{
item.Mpositions = (ICollection<Position>)new SelectList(db.Positions.Except((IQueryable<Position>)db.Positions.Select(xk => xk.Members)), "PositionID", "PositionName");
}
mdlCompanies.Add(item);
//I tried first to edit the Mpositions property directly in gblUpdate
//item.Mpositions = (IEnumerable<Position>)db.Positions.Select(p => new SelectListItem { Value = p.PositionID.ToString(), Text = p.PositionName}) ;
//(ICollection<Position>)db.Positions.ToListAsync();
}
In the view I have this
List<SelectListItem> mPositionNames = new List<SelectListItem>();
#*#this yields no results if I try gettign it from the compani record itself it gives a logic error where all id match all positionNames impossible to select an item and only positions already registered are available on the dropDownlist*#
#{foreach (var item in Model.Mpositions)
{
mPositionNames.Add(new SelectListItem() { Text = item.PositionName, Value = item.PositionID.ToString(), Selected = (false) ? true : false });
#*#selected attribute set to false not an issue, no data to select from :p so far*#
}
}
#*#null exception(if i try to midify Mpositions directly in controler) here or empty list if modify it then put it with original query in a new list*#
<div class="SectionContainer R-sectionContainerData" id="MwrapperDataRight">
#Html.DropDownListFor(mpos => item.PositionID, (SelectList)Model.Mpositions)
</div>
All I want to do is pull the positions table to create a drop downList so users can change the position of an employee but since position has a 1>many relation with employee not companies it is not bound automatically by EF nor I seem to be able to use Include() to add it.
Your query for editing positions are complex. This query must edit person's info only. Using Edit action for formalizing position's edit are not correct.It's againts to Single Responsibility Principle. Use ViewComponents for this situation. Load positions separately from person info.
I found a suitable solution using a model that encapsulate the other entities then using Partialviews/RenderAction so each part handles one entity/operation.
I have tried multiple solution found in stack and the internet i have lost a full day of work behind this please have a look to my code
public async Task<ActionResult> Edit(JobeezUserInfoViewModel jobeezUserInfo, HttpPostedFileBase UploadedImage)
{
var user = UserManager.FindById(User.Identity.GetUserId());
//list of languages
var lgs = jobeezUserInfo.Languages.Select(l => l.LanguageId);
//Languages to be deleted
var lngTodel = db.Languages.AsNoTracking()
.Where(ut => ut.JobeezUserInfoId == jobeezUserInfo.ApplicationUserId)
.Where(ut => !lgs.Contains(ut.LanguageId));
//language ids as Ilist for better performace
var ids = lgs as IList<int> ?? lgs.ToList();
//Languages to be added
var lngToAdd = ids
.Where(
lid =>
user.JobeezUserInfo.Languages
.Select(ut => ut.LanguageId) //for each userlanguages create a list if languageids _
.Contains(lid) == false //_check if it does not contain the posted languageids and return the language ids if it is the case(tid is the posted languageid)
)
.Select(tid =>
new Language()
{
JobeezUserInfoId = user.Id,
LanguageId = tid,
Name = Enum.GetName(typeof(Enums.LanguageEnum), tid)
});
//languages to be updated
var lngToUpdate = user.JobeezUserInfo.Languages.Where(l=>ids.Contains(l.LanguageId));
Mapper.CreateMap<JobeezUserInfoViewModel, JobeezUserInfo>(MemberList.Destination);
JobeezUserInfo info = Mapper.Map<JobeezUserInfo>(jobeezUserInfo) as JobeezUserInfo;
user.FirstName = jobeezUserInfo.FirstName;
user.LastName = jobeezUserInfo.LastName;
user.PostCode = jobeezUserInfo.PostCode;
user.PhoneNumber = jobeezUserInfo.Telephone;
if (ModelState.IsValid)
{
//mark modified for the userinfo
db.JobeezUserInfo.Attach(info); // Entity is in Unchanged state
db.Entry(info).State = EntityState.Modified;
And the next line is this - My question is why I cant attach the language object 'l'
lngToUpdate.ForEach(l =>
{
db.Languages.Attach(l);
db.Entry(l).State = EntityState.Modified;
});
I have the error
"An entity object cannot be referenced by multiple instances of
IEntityChangeTracker."
I have opened the the quick view After the following line (one part of the image shows also the input parameters of my controler action (viewmodel object):
//mark modified for the userinfo
db.JobeezUserInfo.Attach(info); // Entity is in Unchanged state
Precision : the languageobject in the db.changetracket.Entities() is the same object that is posted to my server (in my viewModel) why the enitity framework cannot understand that the new object has to be tracked or attached in place of the new language object (My code is partial for the sake of clarity i can post full code if needed)
My question is : I really dont know what is the best method to update the child entities correctly. What am I doing wrong here and how to get this work ?
thanks in advance
I solved my parent child problem by using AsNoTracking().
Here BOND is my Parent table and GOVBONDINTERESTSCHEDULE is its Child. Also FI is another child table of BOND. A simple update Threw an error because of EF tracking. So I simply updated GOVBONDINTERESTSCHEDULE using AsNoTracking().
Below is my working code.
GOVBONDINTERESTSCHEDULE editmodel = new GOVBONDINTERESTSCHEDULE();
editmodel = new Entities(Session["Connection"] as EntityConnection).
GOVBONDINTERESTSCHEDULEs.Include("BOND").AsNoTracking().Where(t => t.REFERENCE == Ref).
SingleOrDefault();
editmodel.STATUS = ConstantVariable.STATUS_APPROVED;
editmodel.LASTUPDATED = DateTime.Now;
editmodel.LASTUPDATEDBY = Session["UserId"].ToString();
using (Entities db = new Entities(Session["Connection"] as EntityConnection)) {
db.Entry(editmodel).State = EntityState.Modified;
db.SaveChanges();
}
I understood that I have two different errors actually
1.
I had this error because lngToUpdate was a wrongly populated one - speaking about the following line (my bad! ;) ).
var lngToUpdate = user.JobeezUserInfo.Languages.Where(l=>ids.Contains(l.LanguageId));
It is the same collection as in the database which was obviously already been tracked.
so I created a new collection with the modified property values for each languages who's properties were to be updated.
2.
Now For the other two lists lngToAdd and lngToUpdate , with the ObjectStateManager gotten buy the following code
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
I used the manager to change the objectState of the peviously tracked languages as EntityState.unchanged, attched the new ones to the context and marked EntityState.Added EntityState.Deleted respectively (as per my lists).
and it worked! hope this helps someone!
Hello I have something like this:
public ActionResult Edit(int id)
{
var movie = (from m in _db.Movies where m.Id == id select m).First();
return View(movie);
}
[HttpPost]
public ActionResult Edit(Movie movie)
{
try
{
var originalMovie = (from m in _db.Movies where m.Id == movie.Id select m).First();
_db.Movies.ApplyCurrentValues(movie);
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
This example was taken from Proper way to Edit an entity in MVC 3 with the Entity Framework using Data Model First approach?
I want to pass to the DB SQL-query (UPDATE Movie ....) only modified columns because I'm doing a column audit.
The code works ok, but the problem is that in my "Movie" Entity I have a "FlagOldMovie" property and others 10 properties witch I'm not using its in this view because they will stay the same, but the entityframework put to that properties defaults values so the "ApplyCurrentValues" find changes and that properties are updated too.
A workaround is to pass my not changed properties to html hidden inputs, but its privated data.
Any idea?
[HttpPost]
public ActionResult Edit([Bind(Exclude ="column_name")] Movie movie)
{
//code here
}
This would ignore the column you specify, I usually do that to exclude fields like Id.
But if you are ignoring to many columns then you should consider ViewModel concept where you just have only properties you need for a view.
EDIT: Still some issues?
Here is how you add multiple ones
[HttpPost]
public ActionResult Edit([Bind(Exclude ="c_name, c_name2, c_name3")] Movie movie)
{
//code here
}
You can tell EF which fields you want to update. Try something like this:
_db.Movies.Attach(movie);
ObjectStateEntry entryToUpdate = db.ObjectStateManager.GetObjectStateEntry(movie);
entryToUpdate.SetModifiedProperty("field1"); // Replace "field1" with the name of the 1st field to update
entryToUpdate.SetModifiedProperty("field2"); // Replace "field2" with the name of the 2nd field to update
entryToUpdate.SetModifiedProperty("field3"); // Replace "field3" with the name of the 3rd field to update
_db.SaveChanges();
Best practice is to use a ViewModel and not a domain/data model to pass to/from your views. :)
This scenario illustrates one of the dangers of not doing so.
I finally got it, first at all, the solution only works on .NET 4.5+
[HttpPost]
public ActionResult Edit(Movie movie)
{
try
{
//Get DB version
var originalMovie = (from m in _db.Movies where m.Id == movie.Id select m).First();
//Mark changes with data received
_db.Movies.ApplyCurrentValues(movie);
//CODE ADDED - Ignoring field/properties you dont want to update to DB
ObjectStateEntry entryToUpdate = db.ObjectStateManager.GetObjectStateEntry(originalMovil);
entryToUpdate.RejectPropertyChanges("field1");
entryToUpdate.RejectPropertyChanges("field2");
entryToUpdate.RejectPropertyChanges("field3");
//-----------------
_db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
With this code, the only data modified is witch you want, next what I did is to audit columns changed extending _db.SaveChanges() to _db.SaveChangesAudito(id);
try like that
var originalMovie = (from m in _db.Movies where m.Id == movie.Id select m).First();
originalMovie.updateme = updating;
_db.SaveChanges();
I have got two different tables. User and ProjectDetails. There are two different controllers as well to do CRUD operations on these tables. Now, I have a case where, in the User CREATE operation, I have to select the Project from the List of Projects in ProjectDetails. I tried the following:
In the user model, I created this line:
public IEnumerable<ProjectDetail> ProjectDetail { get; set; }
And in the controller, in the create Action, I have added the following code:
public ActionResult Create()
{
var model = new UserDetail
{
ProjectDetail = db1.ProjectDetails
};
return View(model);
}
And in the create view, I am trying to get the list of Project IDs as follows:
#Html.DropDownListFor( x => x.ProjectDetail, new SelectList(Model.ProjectDetail, "Project ID"))
However, wen i run, i get the number of lines (as equal to the number of projects) as
System.Data.Entity.DynamicProxies.ProjectDetails_F########### (Some numbers)..
Please can someone help me?
Regards,
Hari
[EDIT] - I checked in the debug mode and found the following.. Tried attaching the image..
I drilled down that Proxy things and found ProjectID there. How can I get that?
You are using a wrong overload, use this instead:
#Html.DropDownListFor( x => x.ProjectDetail,
new SelectList(Model.ProjectDetail, "ProjectId","ProjectName"))
// where ProjectId is the unique identifier field of `ProjectDetail`
// and `ProjectName` is the text you want to show in the dropdown
In your code you are not telling the html helper what properties to use for the datavalue and the datatext. The overload you use is the one where you tell the htmlhelper which value is selected.
You can do something like
var projection = db1.ProjectDetails.Select(t => new ProjectDetailsViewModel
{
Prop1 = t.Prop1,
Prop2 = t.Prop2
});
Can you try
public ActionResult Create()
{
var model = new UserDetail
{
ProjectDetail = db1.ProjectDetails.ToList()
};
return View(model);
}