EF4: How to Reload / Refresh an EntityCollection? - entity-framework-4

Using Entity Framework 4.0 in C# / .NET 4.0.
Within my Entity model, I have an object (MyObject) that is part of a one-to-many relationship that produces a Navigation Property of the type EntityCollection<OtherObject>. Snippet of the generate code:
public partial class MyObject : EntityObject
{
/* other code */
public EntityCollection<OtherObject> OtherObjects
{
get { /* RelationshipManager stuff */ }
set { /* RelationshipManager stuff */ }
}
/* other code */
}
I load the data fine, everything is good. Then another process adds lines to the underlying OtherObject table. I want to be able to reload or refresh my entity collection in order to gain access to these new objects.
Is there any possible way to do this? Neither of the following attempts accomplish the task:
Context.Refresh(RefreshMode.StoreWins, myObject);
Context.Refresh(RefreshMode.StoreWins, myObject.OtherObjects);
I'd like to avoid having to unload the entire context (as this would force a save of any currently modified information, which is undesirable), so is there any way to get the newly added data into my local entity model?
Thanks.

myObject.OtherObjects.Clear();
Context.AcceptAllChanges();
myObject.OtherObjects.Load();
Disclaimer: Not Tested.

Related

Collection is not set in release build

Platform: .NET 4.5, EF6
Original code:
model.ContentGroups = new List<ContentGroup>();
model.ContentGroups.Add(new ContentGroup());
Working code:
model.ContentGroups.Clear()
model.ContentGroups = new List<ContentGroup>();
model.ContentGroups.Add(new ContentGroup());
ContentGroups definition:
private ICollection<ContentGroup> _contentGroups;
public virtual ICollection<ContentGroup> ContentGroups
{
get { return _contentGroups ?? (_contentGroups = new List<ContentGroup>()); }
set { _contentGroups = value; }
}
If model.ContentGroups already contains one item, the original code resulted two items in the collection unless deliberately make call to collection Clear()
It only occurs when compiling code in release mode, but not in debug build.
Any feedback is appreciated.
Michael
Without seeing all the code related to your DbContext and how you are actually using this I can't be certain but I think it is because setting the property to a new list sort of conflicts with entity framework. I am guessing the issue is related to lazy loading and the way entity framework knows when to do something with the database. Even though you have set it to a new List, EF still goes out to the database and re-fills the list when you enumerate it. Setting to a new List doesn't tell EF to do anything database wise.
If you want to clear the list then all you need is the .Clear() call, there is no need to ever set it to a new list. The Clear() will instruct EF to update the database when you SaveChanges on the DbContext.

Some objects not materializing (but are in query response) - inheritance issue?

I've just added a fourth layer of expand to my query - ie:
.expand("..., ScanDates.Printouts.BMDSites, ...");
And I've discovered that although the data is being returned in the response, it is not populating the objects below "Printouts" (ie. patient.ScanDates.Printouts.BMDSites is an empty array, despite several elements being returned in the response).
I've altered the MaxExpansionDepth on the controller action, and there are no errors appearing on the console or server side. I've also successfully filled BMDSite objects by just querying for them individually, but that would mean ten or twenty return trips to the server... not ideal.
Edit: I've just tried several other queries, and it seems that even if I'm just doing a single expand (ie: .expand("BMDSites")), the same problem occurs - data is in response, but not materialized into entities. When I query just for the BMDSites, (say for a specific Printout) the array is filled and materialized properly.
Edit 2: It just occurred to me that the Printout class is the base class of a TPH inheritance hierarchy... Looking around a bit, I suspect that this is likely the source of the issue.
Thanks so much for any ideas!
-Brad
Looks like it had nothing to do with inheritance after all... In creating a simplified model for Jay, I of course found that it worked just fine. Adding back in the features that I thought were irrelevant, I eventually broke it, and replicated my issue by adding in some [NotMapped] properties that were providing some easy access to the list of BMDSites. For example, in a class derived from Printout:
[NotMapped]
public BMDSite _Ud = null;
[NotMapped]
public BMDSite Ud
{
get
{
if (_Ud == null)
{
_Ud = BMDSites.Find(b => b.Region == Region.Forearm_UD);
}
return _Ud;
}
}
Upon adding this back in, once again, my list of BMDSites were not populating (edit- more specifically, any BMDSite that was touched by an unmapped property was being excluded from the list of BMDSites). Turns out that the JSON.net classes that Breeze uses don't look at [NotMapped] (which makes sense, as it is serialization, not DB mapping)... By adding a reference to JSON.net in my EF model, and adding it's equivilent tag - ie: [NotMapped, JsonIgnore], it doesn't look at the properties, and everything works just fine.
Bottom line (for those that skim)... code above causes issues, code below works fine:
[NotMapped, JsonIgnore]
public BMDSite _Ud = null;
[NotMapped, JsonIgnore]
public BMDSite Ud
{
get
{
if (_Ud == null)
{
_Ud = BMDSites.Find(b => b.Region == Region.Forearm_UD);
}
return _Ud;
}
}
Cheers,
Brad
I would start by insuring that the 'expand' you are doing is in fact valid by trying the exact same query on the server using 'Include's. If this fails then the issue is likely with your model. Breeze 'expand's get turned into EF 'Include's.
If the query works in pure EF, then can you detail the relevant properties in your model and what the inheritance hierarchy looks like so that we can try to replicate your issue?

mvc in asp.net doesnt satisfy programmers

I am learning MVC4 in Visual Studio and I have many questions about it. My first statement about MVC is that MVC's Model doesnt do what I expected. I expect Model to select and return the data rows according to the needs.
But I read many tutorial and they suggest me to let Model return ALL the data from the table and then eliminate the ones I dont need in the controller, then send it to the View.
here is the code from tutorials
MODEL
public class ApartmentContext : DbContext
{
public ApartmentContext() : base("name=ApartmentContext") { }
public DbSet<Apartment> Apartments { get; set; }
}
CONTROLLER
public ActionResult Index()
{
ApartmentContext db = new ApartmentContext();
var apartments = db.Apartments.Where(a => a.no_of_rooms == 5);
return View(apartments);
}
Is this the correct way to apply "where clause" to a select statement? I dont want to select all the data and then eliminate the unwanted rows. This seems weird to me but everybody suggest this, at least the tutorials I read suggest this.
Well which ever tutorial you read that from is wrong (in my opinion). You shouldn't be returning actual entities to your view, you should be returning view models. Here's how I would re-write your example:
public class ApartmentViewModel
{
public int RoomCount { get; set; }
...
}
public ActionResult Index()
{
using (var db = new ApartmentContext())
{
var apartments = from a in db.Apartments
where a.no_of_rooms == 5
select new ApartmentViewModel()
{
RoomCount = a.no_of_rooms
...
};
return View(apartments.ToList());
}
}
Is this the correct way to apply "where clause" to a select statement?
Yes, this way is fine. However, you need to understand what's actually happening when you call Where (and various other LINQ commands) on IQueryable<T>. I assume you are using EF and as such the Where query would not execute immediately (as EF uses delayed execution). So basically you are passing your view a query which has yet to be run and only at the point of where the view attempts to render the data is when the query will run - by which time your ApartmentContext will have been disposed and as a result throw an exception.
db.Apartments.Where(...).ToList();
This causes the query to execute immediately and means your query no longer relys on the context. However, it's still not the correct thing to do in MVC, the example I have provided is considered the recommended approach.
In our project, we will add a Data Access Layer instead of accessing Domain in controller. And return view model instead of Domain.
But your code, you only select the data you need not all the data.
If you open SQL Profiler you'll see that's a select statement with a where condition.
So if it's not a big project I think it's OK.
I can't see these tutorials but are you sure it's loading all the data? It looks like your using entity framework and entity framework uses Lazy laoding. And Lazy loading states:
With lazy loading enabled, related objects are loaded when they are
accessed through a navigation property.
So it might appear that your loading all the data but the data itself is only retrieved from SQL when you access the object itself.

Entity Framework 5's Lazy Loading and Fiddler

I am having problems understanding lazy loading in Entity Framework 5. I understand when using lazy loading related entities are not loaded until requested:
"When using Lazy Loading, your initial query only brings in the target
entity set. But whenever you access a navigation property, another
query is issued against the store to load the related entity.
(reference)"
I have a ASP.NET Web API project with two classes:
public class Farm
{
public int FarmId { get; set; }
public virtual ICollection<LandUnit> LandUnits { get; set; }
...
}
public class LandUnit
{
public int LandUnitId { get; set; }
...
}
I set LazyLoadingEnabled = true, and have my POCO classes conforming to the guidelines (reference), but when I use scaffolding to create a FarmController and call it through fiddler, it shows:
JSON
{}
...
LandUnits
{}
...
If I set LazyLoadingEnabled = false, then I get:
JSON
{}
...
LandUnits=(null)
Am I misunderstanding the basics of lazy loading? It appears to me that what is occurring is the opposite of what the definition states. When lazy loading is off, related entities are not loaded. When lazy loading is on, related entities are loaded.
This is expected behaviour. When the JSON serializer comes to serialize the type, it will enumerate the LandUnits navigation property which will of course invoke the lazy load of that collection from the database.
When you switch lazy loading off, your navigation property will still be set to its default value as demonstrated, as no proxy type will be generated by Entity Framework.
I would recommend keeping lazy loading switched off, and eagerly (using the Include method) or explicly loading (using the Load method) the related data to ensure you aren't inadvertantly loading data that you don't require.
You should also be aware of circular dependency problems when using the default JSON serializer.
This just shows that you havent yet requested any of the LandUnits yet (if you loop through them using something like a foreach they will be loaded). So the JSON object is empty when using lazy loading.
When lazy loading is turned off, the LandUnits object is null as the LandUnits have not been loaded and cannot be loaded at a later time (attempting to do so would throw an error)

How to bypass the System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method) validation of Entity framework?

I'm using a customized method for tracking individual modified properties of an n-tier disconnected entity class. I extracted it from
Programming Entity Framework: DbContext by Julia Lerman and Rowan
Miller (O’Reilly). Copyright 2012 Julia Lerman and Rowan Miller,
978-1-449-31296-1.
The code is:
public void ApplyChanges<TEntity>(TEntity root) where TEntity : class, IObjectWithState {
// bind the entity back into the context
dbContext.Set<TEntity>().Add(root);
// throw exception if entity does not implement IObjectWithState
CheckForEntitiesWithoutStateInterface(dbContext);
foreach (var entry in dbContext.ChangeTracker.Entries<IObjectWithState>()) {
IObjectWithState stateInfo = entry.Entity;
if (stateInfo.State == RecordState.Modified) {
// revert the Modified state of the entity
entry.State = EntityState.Unchanged;
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
entry.Property(property).IsModified = true;
}
} else {
entry.State = ConvertState(stateInfo.State);
}
}
dbContext.SaveChanges();
}
The purpose of this method is to let the EF know only a predefined set of entity fields are ready for update in the next call of SaveChanges(). This is needed in order to workaround the entity works in ASP.NET MVC 3 as follows:
on initial page load: the Get action of the controller is loading the
entity object and passing it as a parameter to the view.
The View generate controls for editing 2 of the fields of the entity,
and holds the ID of the record in a hidden field.
When hitting [save] and posting the entity back to the controller all
of the fields excepting the 3 preserved in the view comes with a null
value. This is the default behavior of the MVC binding manager.
If i save the changes back to the database the update query will of course overwrite the non mapped fields with a sentence as follows:
UPDATE non_mapped_field_1 = NULL, ..., mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2', ... non_mapped_field_n = NULL WHERE ID = mapped_field_3
This is the reason i'm trying to track the fields individually and update only those fields i'm interested in. before calling the custom method with ApplyChanges() i'm adding the list of fields i want to be included in the update to the IObjectWithState.ModifiedProperties list, in order to get a SQL statement as follows:
UPDATE mapped_field_1 = 'mapped_value_1', mapped_field_2 = 'mapped_value_2' WHERE id = mapped_value_3
The problem is, when marking one of the fields as modified in ApplyChanges, i.e.:
entry.Property(property).IsModified = true;
the system is throwing the following exception:
{System.InvalidOperationException: Member 'IsModified' cannot be called for property 'NotifyCEDeadline' on entity of type 'User' because the property is not part of the Entity Data Model.
at System.Data.Entity.Internal.InternalPropertyEntry.ValidateNotDetachedAndInModel(String method)
at System.Data.Entity.Internal.InternalPropertyEntry.set_IsModified(Boolean value)
at System.Data.Entity.Infrastructure.DbPropertyEntry.set_IsModified(Boolean value)
...
So the question is. There's a way to bypass this EF validation or let the context know of the existance of this system property (IsModified) that i'm trying to change?
Summary of the architeture:
EF Code first (annotation + Fluent API)
Oracle .NET EF Data provider (ODAC)
Context is injected to a cutom business context with nInject.MVC => this is the reason i customized the ApplyChanges() method from
using (var context = new BreakAwayContext()){
context.Set().Add(root);
to a simple call to the already initialized dbcontext
dbContext.Set().Add(root);
Oracle Database is created manually i.e. without the help of EF, so no EF metadata tables are used.
Thanks,
Ivan.
Very good description, however I can't find any information on why you need a transient property called "IsModified" in the object and/or why you need to tell EF about it being modified (EF won't be able to persist it anyway).
The value of the IsModified property should be set by the model binder if the property was incldued in the view anyway.
You could just add code in your ApplyChanges method to skip a property named "IsModified", or even better, filter only known properties using entry.CurrentValues.PropertyNames, e.g.:
foreach (var property in stateInfo.ModifiedProperties) {
// mark only the desired fields as modified
if (entry.CurrentValues.PropertyNames.Contains(property)) {
entry.Property(property).IsModified = true;
}
}
Update: Ivan, very sorry I did not understand the problem better when you posted it several months ago and that I did not follow up after your added these clarifying comments. I think I understand better now. That said, I think the code snippet that I offered can be part of the solution. From looking at the exception you are getting again, I understand now that the problem that EF is detecting is that NotifyCEDDealine is not a persistent property (i.e. it is not mapped in the Code First model to a column in the database). IsModified can only be used against mapped properties, therefore you have two options: you change the code of the implementation of IObjectWithState in your entities so that non-mapped properties are not recorded in ModifiedProperties, or you use my code snippet to prevent calling IsModified with those.
By the way, an alternative to doing all this is to use the Controller.TryUpdateModel API to set only the modified properties in your entities.
Hope this helps (although I understand it is very late).

Resources