I have a simple razor view:
<script>var fieldList = #Html.Raw(Json.Encode(Model));</script>
This line throws The ObjectContext instance has been disposed exception. If I remove it all works fine even when I use Model later in View.
foreach (SomeCustomObject pField in Model)
{
<div>
#pField.SomeProperty
</div>
}
Controller action
ActionResult SomeAction()
{
List<SomeCustomObject> tList = new List<SomeCustomObject>();
using(EFEntities db = new EFEntities())
{
tList = db.SomeCustomObject.ToList();
}
return View(tList);
}
I presume, that it could be because object has navigation properties that no longer work. Is it possible to tell Json.Encode to use only NON navigation preperties of object?
You can't operate on model obejct outside of using block, where it is loaded. Possibly, JSON.encode does deep reflection analysis of model object and so touches some context-dependent attributes.
So, in common case, you should convert model to JSON in controller/action method, inside using block, put it to string variable and then use this variable in template.
Simplest way that I came up with was:
Create view ViewSomeCustomObjects in database, what returns all fields of SomeCustomObjects table.
Update model from database and include new view.
Use entity what is maped to view instead of table, as this is only for data display.
I REALY hope that if I change SomeCustomObjects table in database, changes will cascade trough entire solution...
Disable lazy loading on the context first with context.Configuration.LazyLoadingEnabled = false; before you start pulling stuff out of your database.
using (var context = new SomeEntityContext())
{
context.Configuration.LazyLoadingEnabled = false; // This is the fixer.
return context.SomeEntitiesWithRelations.ToList();
}
Related
My Database is set up with an Entity table, which has a Ref_Type navigation property (and a FK which references TypeID). The Type table, has a Ref_Department, same FK setup. Finally, the Department table has a Ref_Locale and same FK setup.
I save the Entities in a List, which is a property of a singleton. It is created as follows;
private Singleton()
{
using (Database db = new Database())
{
List<Entities> EntityList = db.Entities.ToList<Entities>();
}
}
This is fine, the navigation properties are ALL loaded and I can access any one of them.
The problem comes when I update an Entity entry as follows;
public void UpdateEntity(Entities oldEnt, Entities newEnt)
{
using (Database db = new Database())
{
Entities ent = db.Entities.Where(e => e.EntityName == oldEnt.EntityName).FirstOrDefault();
ent.EntityName = newEnt.EntityName;
ent.EntityEmail = newEnt.EntityEmail;
...
ent.EntityType_ID = newEnt.EntityType_ID;
db.SaveChanges();
}
RefreshEntities();
}
public void RefreshEntities()
{
using (Database db = new Database())
{
db.Configuration.LazyLoadingEnabled = false;
db.SaveChanges();
EntityList = db.Entities.Include("Ref_EntityType").Include("Ref_EntityPosition").ToList<Entities>();
}
}
Ref_Entity gets loaded properly, but then within Ref_Entity, Ref_Department is just null. I've tried just using db.Entities.ToList<Entities>(); like in my constructor, no dice. As you can see, I've also tried turning LazyLoading off (I thought I might need to call SaveChanges() for it to actually apply the flag). I've also tried .Include("Ref_Department") but it just complains that it doesn't exist for Entities, which makes sense.
The newEnt that I pass to the UpdateEntity method does not have Ref_Type initialised, I'm working under the assumption that anything not changed in the UpdateEntity method would just stay the same...
So now I'm at a bit of a loss as to what's going on and how to fix it. If anyone could help explain where I'm going wrong or give me some pointers about how to fix my code to make it work, that would be great.
On a whim, I modified RefreshEntities() to;
EntityList = db.Entities.Include("Ref_EntityPosition").Include("Ref_EntityType").
Include("Ref_EntityType.Ref_Department").
Include("Ref_EntityType.Ref_Department.Ref_Locale").ToList<Entities>();
And now I'm getting all the references.
I'm still not sure why it would load all the references in the constructor but not in the RefreshEntities() method, even if the calls are identical, but this solves the problem so I'm happy enough to leave it like that.
I'm wanting to capture the old values within a model so I can compare with the new values after submission, and create audit logs of changes a user makes.
My guess is doing it with hidden input boxes with duplicated old value properties would be one way. But wondering if there are any other good alternatives?
Thanks
In the save method, just go and get the original object from the database before saving the changes, then you have your old and new values to compare against? :)
This sounds like standard auditing. You should not worry about what has changed just capture EVERYTHING and who made the change. Unless there is some sort of real time reporting that needs to be done.
Possible auditing implementations:
CQRS, in a nutshell it tracks every change to a given object. The downside is it's an architecture that is more involved to implement.
The Rolling ledger. Each insert is a new row in the database. The most current row is used for display purposes, but with each update, a new row is inserted into the database.
Yet another approach is to save it off into an audit table.
All get the job done.
You could also store the original model in the view bag and do something like this...
// In the controller
public ActionResult DoStuff()
{
// get your model
ViewBag.OriginalModel = YourModel;
return View(YourModel);
}
// In the View
<input type="hidden" name="originalModel" value="#Html.Raw(Json.Encode(ViewBag.OriginalModel));" />
// In the controller's post...
[HttpPost]
public ActionResult DoStuff(YourModel yourModel, string originalModel)
{
// yourModel will be the posted data.
JavaScriptSerializer JSS = new JavaScriptSerializer();
YourModel origModel = JSS.Deserialize<YourModel>(originalModel);
}
I didn't get a chance to test this, just a theory :)
Exactly what mattytommo says is the preferred method all around
Instantiate new view model for creating a new entity
public ActionResult Edit(int id) {
var entity = new Entity(id); // have a constructor in your entity that will populate itself and return the instance of what is in the db
// map entity to ViewModel using whatever means you use
var model = new YourViewModel();
return View(model);
}
Post changes back
[HttpPost]
public ActionResult Edit(YourViewModel model) {
if (ModelState.IsValid) {
var entity = new YourEntity(model.ID); // re-get from db
// make your comparison here
if(model.LastUserID != entity.LastUserID // do whatever
... etc...
}
return View(model);
}
I have a view that I pass a viewmodel object to that contains an IQueryable<> object.
This object is used to populate an mvccontrib grid. The view also contains other partial views that allow the user to filter the data within the grid.
Once the grid is filtered I would like the user to be able to export the Iqueryable object to another controller actionresult method which then calls another viewmodel that exports the data to Excel.
Here is the snippet of the view that calls the Export actionresult() method:
#using (Html.BeginForm("Export", "Home", FormMethod.Post, new { Model }))
{
<p>
<input class="button" value="Export to Excel" type="submit" />
</p>
}
Model does contain the IQueryable object.
When I debug the code I can view the viewmodel object, and of course in order to populate the IQueryable I must enumerate the object.
I have also created another viewmodel object that, once the Model object is passed back to the actionresult method attempts to enumerate the IQueryable object by either using the .ToList() method or the AsEnumerable() method.
But in all cases the IQueryable object is pass to the controller as a null object.
Here is the action result method that is being called from the view:
[HttpPost]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Export(PagedViewModel<NPSProcessed> NPSData)
{
string message = "";
NPSData Query = new Models.NPSData(NPSData);
Query.IData = NPSData.Query.ToList();
// Opening the Excel template...
FileStream fs =
new FileStream(Server.MapPath(#"\Content\NPSProcessedTemplate.xls"), FileMode.Open, FileAccess.Read);
MemoryStream ms = new MemoryStream();
ee.ExportData(fs, ms, Query.IData, message);
// Sending the server processed data back to the user computer...
return File(ms.ToArray(), "application/vnd.ms-excel", "NPSProcessedNewFile.xls");
}
Any assistance would be greatly appreciated.
Thanks
Joe
You cannot pass complex objects around like this: new { Model }. It would have been to easy :-). You will have to send them one by one:
new { prop1 = Model.Prop1, prop2 = Model.Prop2, ... }
Obviously this could get quite painful. So what I would recommend you is to send only an id:
new { id = Model.id }
and then inside your controller action that is supposed to export to Excel use this id to fetch the object from wherever you fetched it initially in the GET action (presumably a database or something). If you want to preserve the paging, and stuff that the user could have performed on the grid, you could send them as well to the server:
new { id = Model.id, page = Model.CurrentPage, sortColumn = Model.SortBy }
Another possibility (which I don't recommend) consists into saving this object into the session so that you can fetch it back later.
Yet another possibility (which I still don't recommend) is to use the MVCContrib's Html.Serialize helper which allows you to serialize an entire object graph into a hidden field and it will be sent to the server when the form is submitted and you will be able to fetch it as action argument.
The simple answer is: don't put IQueryable properties in your model. The model should be purely simple objects and validation attributes. Keep the queryability in your controller.
I am building in a Change History / Audit Log to my MVC app which is using the Entity Framework.
So specifically in the edit method public ActionResult Edit(ViewModel vm), we find the object we are trying to update, and then use TryUpdateModel(object) to transpose the values from the form on to the object that we are trying to update.
I want to log a change when any field of that object changes. So basically what I need is a copy of the object before it is edited and then compare it after the TryUpdateModel(object) has done its work. i.e.
[HttpPost]
public ActionResult Edit(ViewModel vm)
{
//Need to take the copy here
var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);
if (ModelState.IsValid)
{
//Form the un edited view model
var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
//Compare with old view model
WriteChanges(uneditedVM, vm);
...
TryUpdateModel(object);
}
...
}
But the problem is when the code retrieves the "unedited vm", this is causing some unexpected changes in the EntityFramework - so that TryUpdateModel(object); throws an UpdateException.
So the question is - in this situation - how do I create a copy of the object outside of EntityFramework to compare for change/audit history, so that it does not affect or change the
EntityFramework at all
edit: Do not want to use triggers. Need to log the username who did it.
edit1: Using EFv4, not too sure how to go about overriding SaveChanges() but it may be an option
This route seems to be going nowhere, for such a simple requirement! I finally got it to override properly, but now I get an exception with that code:
public partial class Entities
{
public override int SaveChanges(SaveOptions options)
{
DetectChanges();
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
//return base.SaveChanges();
return base.SaveChanges(options);
}
}
IF you are using EF 4 you can subscribe to the SavingChanges event.
Since Entities is a partial class you can add additional functionality in a separate file. So create a new file named Entities and there implement the partial method OnContextCreated to hook up the event
public partial class Entities
{
partial void OnContextCreated()
{
SavingChanges += OnSavingChanges;
}
void OnSavingChanges(object sender, EventArgs e)
{
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
}
}
If you are using EF 4.1 you can go through this article to extract changes
See FrameLog, an Entity Framework logging library that I wrote for this purpose. It is open-source, including for commercial use.
I know that you would rather just see a code snippet showing how to do this, but to properly handle all the cases for logging, including relationship changes and many-to-many changes, the code gets quite large. Hopefully the library will be a good fit for your needs, but if not you can freely adapt the code.
FrameLog can log changes to all scalar and navigation properties, and also allows you to specify a subset that you are interested in logging.
There is an article with high rating here at the codeproject: Implementing Audit Trail using Entity Framework . It seems to do what you want. I have started to use this solution in a project. I first wrote triggers in T-SQL in the database but it was too hard to maintain them with changes in the object model happening all the time.
I need your help very much. I'd like to update object created in another datacontext;
Here is my code. Insert statement works well but I can't write code for update
var dataContext = new ReconNewDataContext();
if (Id == 0)
{
var item = this;
dataContext.RequestIO.InsertOnSubmit(item);
dataContext.SubmitChanges();
Id = item.Id;
}
else
{
var item = this;
//update object
}
I've read
Linq2SQL: Update object not created in datacontext
I've tried to use .Attach(this) .Attach(this,true) .Attach(this, oldObjectFromBase) but always I've an errors.
I know I can get object from database and manually transfer data from modified object, but there will be new fields. It means that I must always append these new fields in Save() method.
Is there any "beautiful" method to update object created in another datacontext?
Check out this article: http://omaralzabir.com/linq_to_sql__how_to_attach_object_to_a_different_data_context/
Also, in your example, for update, try doing:
var item = new ObjectBeingUpdated();
//copy over properties from old object to new object, make sure pk's match
//Attach this new object
Something similar worked for my coworker I believe.
UPDATE: Check this out for more info about attach: http://blogs.msdn.com/b/dinesh.kulkarni/archive/2007/10/08/attach-if-you-have-something-detached.aspx
You could use the following pattern:
Fetch existing object from repository
Use AutoMapper to copy properties
Save your object back.