I have the following two models:
public class ModelA {
public IList<ModelB> list { get; set; }
// rest properties
}
public class ModelB {
public ModelA navProp { get; set; }
// rest properties
}
When my application loads, I fetch all the data; that is, instances of ModelA include all the values of the collection navigation property list.
In other words, I don't apply the eager loading or loading on demand techniques to fetch the data for the navigation property.
The problem with that is that I get the following error:
A MergeStrategy of 'Disallowed' does not allow you to attach an entity when an entity with the same key is already attached:
Looking at the code, I noticed that the first instance of ModelA is attached twice. It seems that breeze tries to recursively attach the entities into the cache (starting from the navigation property).
I just wonder if I'm following the wrong path. I have the impression that breeze expects us to explicitly load the related entities.
A bug that caused this error message was fixed in breeze 1.5.2.
Related
I have a problem with an OData controller that is a little unusual compared to the others I have. It is the first one working completely from memory - no database involved.
The returned entity is:
public class TrdRun {
[Key]
public Guid Identity { get; set; }
public TrdTrade [] Trades { get; set; }
TrdTrade is also an entity set (which if queries goes against a database). But in this particular case I want to return all trades associated as active from a run, and I an do so WITHOUT going to the database.
My problem? The following code:
[ODataRoute]
public IEnumerable<Reflexo.Api.TrdRun> Get(ODataQueryOptions options) {
var instances = Repository.TrdInstance.AsEnumerable();
var runs = new List<Reflexo.Api.TrdRun>();
foreach (var instance in instances) {
runs.Add(Get(instance.Identifier));
}
return runs;
}
correctly configures runs to have the trades initialized - but WebApi decides to swallow them.
What is a way to configure it to return the data "as given" without further filtering? I know about the AutoExpandAttribute (Which I would love to avoid - I do not want the API classes marked with OData attributes), but I have not enabled Query, so I would expect the return data to be returned as I set it up.
The value of the Trades property is not being serialized because the default behavior of ODataMediaTypeFormatter is to not follow navigation properties, regardless of what is in memory. You could override this behavior by using $expand in the query string of the request, or AutoExpandAttribute on the Trades property in the class definition, but both approaches require decorating your controller method with EnableQueryAttribute.
If you don't want to do any of that, you can still programmatically specify auto-expansion of Trades in your service configuration as follows:
// Let builder be an instance of ODataModelBuilder or a derived class.
builder.EntityType<TrdRun>().CollectionProperty(r => r.Trades).AutoExpand = true;
Minor issue: With the programmatic approach, if the client requests full metadata (e.g., odata.metadata=full in the Accept header), the OData serializer will not include full metadata in the auto-expanded objects.
for ( int i = 0; i < libraryList.Count; i++)
{
if (ModelState.IsValid)
{
context.Library.Add(libraryList[i]);
context.SaveChanges();
}
}
A library contains an entity 'predefinedgoals' which is already set up in the DB. So when the above code runs it stores dublicates of 'predefinedgoals' and assigns new ID's to them.
I read that I should attach the existing entity to the context but I'm not sure how to to do it in my scenario. The classes look like this:
class library
int libraryID
list<book> bks
.
class book
int bookID
list<importantdates> impdts
.
class importantdate
int importantdateID
predefinedgoal predfg
int numberofresellers
.
class predefinedgoal
int predefinedgoalID
string description
int daysfrompublication
I tried something like this right after ModelState.IsValid but I sense I'm doing it wrong:
var prdfgs= context.predefinedgoals.ToList();
foreach(var pg in prdfgs)
context.predefinedgoals.Attach(pg);
This answer is going to be based on a couple of assumptions, but I've seen this exact problem so many times that this is automatically my go-to answer.
What I think you're doing is that you're creating Library, Book, and ImportantDate objects (and setting up all of the relationships between them as well). In the process of doing all of this, however, you are trying to set the PreDefinedGoal navigational property on those ImportantDate objects, all the while leaving the actual int FK property (that would be something like PreDefinedGoalID), still set to 0. When that happens, Entity Framework disregards the fact that the object contained in the navigational property has an ID on it, and assumes that you are trying to create this PreDefinedGoal object from scratch, just like you're creating the ImportantDate object (as well as the others). It will then create a PreDefinedGoal object with the exact same data as the one you're actually trying to use, but it will create it as a separate, duplicate record in the database.
The solution to your problem then is simple: Don't set the navigational property. Just simply set the FK (ImportantDate.PreDefinedGoalID) to the ID of the PreDefinedGoal object that you want to hook up to it. When you do that, and you save it, Entity Framework will then reach out to the database for the correct object based on that ID, and thus you will avoid having duplicate PreDefinedGoal objects in your database.
FYI: I learned this from one of Julie Lerman's MSDN posts. If you're going to be working with EF, I highly recommend reading her posts and columns.
I am in the same situation and found a workaround. The way this workaround works makes me think that in this case EF is to blame for handling the situation badly.
In order to simplify the example I will just post an example with one object and it's navigational property.
public class Topic
{
int Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
}
public class Course
{
int Id { get; set; }
public Topic Topic { get; set; }
// additional properties don't matter now
}
Note the absence of any foreign key or other data annotations. EF6 will correctly create the database schema from this and infer that Id is the primary key.
Without workaround adding a new course for an existing topic will create a new topic object with a new Id (overwriting the Id it was given!) :
db.Courses.Add(course);
await db.SaveChangesAsync();
The braindead workaround:
course.topic = db.Topics.Find(course.topic.Id);
db.Courses.Add(course);
await db.SaveChangesAsync();
In other words, if the topic has been loaded from the context directly, EF will recognize it as an existing topic and don't try to add it again.
Update: To just attach the entity without reloading it:
db.Topics.Attach(course.topic);
However you will run into more issues with this setup, it is probably best to use ForeignKey attribute(s) and include the TopicId in Course object. Following works OK but still looks ridiculous to me:
[ForeignKey("Topic")]
public int TopicId { get; set; }
[ForeignKey("TopicId")]
public virtual Topic Topic { get; set; }
Would love to hear about a less redundant solution though.
The answer to why it stored duplicates in my scenario was that I performed tasks in two different classes - using different database context variables in each of them.
So class #1 is the one in my question, that's where I'm saving to the DB using context #1. In class #2 I retrieved all the PredefinedGoals and added them to ImportantDates but to do this I created context #2. The ID's and objects were the same but retrieved from different context variables.
I solved it by retrieving the PredefinedGoals in class #1 with context variable #1 and sent them as an argument to class #2.
I have the following Entity:
public class Invoice
{
[Key]
public int Id { get; set; }
public DateTime? ArchiveDate { get; set; }
public DateTime? ClotureDate { get; set; }
...
}
I would like to know whether my invoice is archived or closed by using a kind of flag (boolean). For that purpose I added 2 unmapped properties in my breeze entity like this:
public class Invoice
{
[Key]
public int Id { get; set; }
public DateTime? ArchiveDate { get; set; }
public DateTime? ClotureDate { get; set; }
[NotMapped]
public bool Archived { get { return ArchiveDate.HasValue; } }
[NotMapped]
public bool Clotured { get { return ClotureDate.HasValue; } }
...
}
Now I can query my breeze entity like this:
var query = entityQuery.from("Invoices")
.where('id', '==', id)
.toType('Invoice');
The call above will return all properties of my invoice entity (including archived & clotured). It works well.
But I need only a few specific properties (for performance). Then I try:
var query = entityQuery.from("Invoices")
.where('id', '==', id)
.select("id, archived, clotured")
.toType('Invoice');
I got the error: The specified type member 'Archived' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Very frustrating. Any idea why do I cannot perform such query?
Or maybe does someone have another solution?
Many thanks.
Short version
What you are seeing is perfectly expected. The ArchivedDate is both a persisted data property and a serialized property. The Archived property is not persisted but it is serialized. That's why you see data values for both ArchivedDate and Archived. However, your remote query ... the LINQ query executed on the server ... may only refer to the persisted properties such as ArchivedDate. EF knows nothing about calculated properties such as Archived; they cannot participate in a LINQ query ... not in a where, select, orderBy or any other query. You can't mention something in a query that EF doesn't know about ... and you told EF (properly) to ignore these Archived and Clotured calculated properties.
Long version
The [Unmapped] attribute hides the properties from EF ... as it must because Archived and Clotured are calculated properties, not persistable data.
The [Unmapped] attribute also hides these properties from the metadata generated from EF. That too is both expected and good.
But this also means that you cannot construct a LINQ query that references these properties. They aren't data properties. They can't be queried by EF. Only data properties and navigation properties can appear in a LINQ query. It is really that simple.
Perhaps you're wondering why the unmapped calculated property values are actually communicated to the JavaScript client, why those values appear in the JSON payload and would populate the like-named Breeze entity properties if you add such properties to the client metadata for Invoice as "unmapped properties".
To understand why, you must understand the difference between properties that you query with EF and the properties that you serialize with Json.NET. After the EF query completes, the materialized entities have both the data properties (e.g., ArchivedDate) and the calculated properties (Archived). The [NotMapped] attribute doesn't hide a property from Json.NET. Json.NET serializes ALL properties of the materialized object - both data and calculated properties - unless you tell it not to. For example you could hide the Archived property from Json.NET serialization with the [Ignore] attribute.
The toType is a red herring and has no bearing on the matter.
Remove the ".toType('Invoice')' line from your query. Just go with:
var query = entityQuery.from("Invoices")
.where('id', '==', id)
.select("id, archived, clotured");
This forces breeze to coerce your projection into an Invoice entity type. If you leave it off you will get a true projection, i.e. a plain javascript object with just the properties you have specified, i.e. not an entity.
Loading related object in MVC can be pretty confusing.
There are lots of terms you need to be aware of and learn if you really want to know what you're doing when writing your entity model classes and controllers.
A couple of questions that I've had for a very long time are: How does the virtual keyword work and when should I use it? And how does the Include extension method work and when should I use it?
Here's what I'm talking about;
virtual keyword:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace LazyLoading.Models
{
public class Brand
{
public int BrandId { get; set; }
public string Name { get; set; }
public int ManufacturerId { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
}
}
And the Include extension method:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using LazyLoading.Models;
namespace LazyLoading.Controllers
{
public class LazyLoadingStoreController : Controller
{
private UsersContext db = new UsersContext();
//
// GET: /LazyLoadingStore/
public ActionResult Index()
{
var brands = db.Brands.Include(b => b.Manufacturer);
return View(brands.ToList());
}
... Other action methods ...
Notice that the Index() action method is autogenerated by Visual Studio. Yes, Visual Studio automatically added the .Include(b => b.Manufacturer). That's pretty nice.
Note: It took me too long to write this answer to just discard it when two other appeared ...
Virtual keyword works together with two properties DbContext.Configuration:
ProxyCreationEnabled - allows EF crating dynamic proxy when the object is created by the EF
LazyLoadingEnabled - allows dynamic proxy to load related entities when the navigation property is used for the first time
Lazy loading is transparently implemented through dynamic proxy. Dynamic proxy is class derived from your entity which is created and compiled by EF at runtime. It overrides your virtual navigation properties and implements logic checking if related entities were already loaded or not. If not it triggers a loading on the context (issues a new query to the database). Lazy loading can be executed only in the scope of the context to which the entity is attached - if you dispose the context you cannot use it.
Dynamic proxy creation is controlled by the first mentioned property. Once the entity instance is created as proxied you cannot "remove" the proxy. Also if you create entity without proxy (for example by calling constructor yourselves) you cannot add it later (but you can use DbSet<T>.Create instead of constructor to get the proxied instance).
The second property can be changed during live time of your entity instances so you can avoid unnecessary queries to the database when working with your entities by changing it to false (it is sometimes very useful).
Include represents eager loading. Eager loading loads related entities together with the main entity and it is executed as part of the main entity's query (it adds SQL joins to the query and builds a big result sets).
Benefit of eager loading is to get all data upfront with one roundtrip to the database. Especially if you know that you will need all of them it can be a way to go. Disadvantage of eager loading are very big result sets if you use too many includes as well as some limitations (you cannot add ordering or filtering to loaded entities - it always loads all related objects).
Benefit of lazy loading is to load data only when you really need them. It is helpful if you don't know upfront if you will really need them. Disadvantages are additional queries generated by EF in some scenarios when you don't expect them (any first access to the property will trigger the lazy loading - even Count on navigation collection will trigger loading of all data to be able to do count in your application instead of querying count from the database - this is called extra lazy loading and it is not natively supported by EF yet). Another big issue is N+1 problem. If you load several brands and you will access their manufacturer property by looping through all loaded brands without using eager loading, you will generate N+1 queries to database (where N is number of brands) - one for loading all brands and one for a manufacturer of each brand.
There is another option called explicit loading. It is like lazy loading but it is not transparently executed for you. You must execute it yourselves by using context class:
context.Entry(brand).Reference(b => b.Manufacturer).Load();
It is not very useful in this case but it would be useful if you have Brands navigation property on the Manufacturer class because you can do this:
var dataQuery = context.Entry(manufacturer).Collection(m => m.Brands).Query();
Now you have a IQueryable<Brand> instance and you can add any condition, ordering or even additional eager loading and execute it against the database.
I created a test MVC4 internet application.
Here's what I found:
First, create your entity model classes - notice the virtual keyword for the Manufacturer property:
public class Manufacturer
{
public int ManufacturerId { get; set; }
public string Name { get; set; }
public ICollection<Brand> Brands { get; set; }
}
public class Brand
{
public int BrandId { get; set; }
public string Name { get; set; }
public int ManufacturerId { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
}
Next, create your controller - I created (autogenerated using create new controller dialog) mine with CRUD action methods and views. Notice the Include extension method that was autogenerated automatically by Visual Studio thanks to the relationship in you Brand model class.
public class LazyLoadingStoreController : Controller
{
private UsersContext db = new UsersContext();
//
// GET: /LazyLoadingStore/
public ActionResult Index()
{
var brands = db.Brands.Include(b => b.Manufacturer);
return View(brands.ToList());
}
Let's remove the Include part for now so that our action method looks like this:
public ActionResult Index()
{
var brands = db.Brands;
return View(brands.ToList());
}
And this is how the Index view will look in Page Inspector after adding a couple of Brand objects - notice that Visual Studio automatically added the dropdown for Manufacturer and how it automatically scaffolded the Name column for Manufacturer - sweet!:
The Create action method:
//
// GET: /LazyLoadingStore/Create
public ActionResult Create()
{
ViewBag.ManufacturerId = new SelectList(db.Manufacturers, "ManufacturerId", "Name");
return View();
}
Awesome. Everything was autogenerated for us!
Now, what happens if we remove the virtual keyword from our Manufacturer property?
public class Brand
{
public int BrandId { get; set; }
public string Name { get; set; }
public int ManufacturerId { get; set; }
public Manufacturer Manufacturer { get; set; }
}
This is what will happen - our Manufacturer data is gone:
Okay, makes sense. What if I add back the Include extension method (with virtual still removed from the Manufacturer property)?
public ActionResult Index()
{
var brands = db.Brands.Include(b => b.Manufacturer);
return View(brands.ToList());
}
This is the result from adding back the Include extension method - The Manufacturer data is back!:
So that's how all that stuff work.
Next thing would be to explain what kind of T-SQL that gets generated behind the scenes in both cases (Lazy loading and Eager loading). That I'll leave to someone else. :)
Note: Visual Studio automatically generates Include(b => b.Manufacturer) wheather you add the virtual keyword or not.
Note2: Oh, yeah. Almost forgot. Here are some links to some good Microsoft resources.
http://msdn.microsoft.com/en-us/data/jj574232.aspx
http://msdn.microsoft.com/en-us/library/vstudio/bb896272(v=vs.100).aspx
The second link talks about performance considerations which the other link lacks if that is something that gets you going.
Lazy loading
Brand is a POCO (plain old CLR object). It is persistence ignorant. In other words: it does not know it is created by an Entity Framework data layer. It knows even less how to load its Manufacturer.
Still, when you do this
var brand = db.Brands.Find(1);
var manufacturer = brand.Manufacturer;
the Manufacturer is loaded on the fly ("lazily"). If you monitor the SQL that's sent to the database you'll see that a second query is emitted to fetch the Manufacturer.
That is because under the hood, EF does not create a Brand instance, but a derived type, a proxy, that's stuffed with wiring to execute lazy loading. That's why the virtual modifier is required for lazy loading to be enabled: the proxy must be able to override it.
Lazy loading is typically used in smart client applications where a context has a relatively long life span (e.g. context per form). Although I must say that even in smart client applications using short-lived contexts is beneficial and perfectly possible.
Eager loading
Eager loading means that you load an object with adhering objects (parents and/or children) in one take. That's what the Include method is for. In the example
db.Brands.Include(b => b.Manufacturer)
you'll see that EF creates a SQL query with a join and that accessing a Brand's Manufacturer does not spawn a separate query any more.
Eager loading is the way to go in most situations (esp. in disconnected scenarios), because the recommended way to deal with context instances is to use them and dispose them for each unit of work. Thus, lazy loading is not an option, because for a navigation property to be lazy loaded the context must be alive.
I am kind of confused about how Lazy loading working.
For example if I have supplier object, which have Address foreign object as a property, like this:
public class Supplier
{
public int ID { get; set; }
[Required]
public string FullName { get; set; }
public string TaxNumber { get; set; }
public virtual Address DeliveryAddress { get; set; }
}
when I put a break point at:
var suppliers = dbContext.Supplier.ToList();
I can see that Address information are available to the var suppliers, etc. when I expend the DeliveryAddress property, it is avaliable, is that means all the FK object has been loaded? But on the other hand, for the query on the right hand I can view that from Visual Studio at the break point, and it is like that:
{SELECT
[Extent1].[ID] AS [ID],
[Extent1].[FullName] AS [FullName],
[Extent1].[TaxNumber] AS [TaxNumber],
[Extent1].[DeliveryAddress_ID] AS [DeliveryAddress_ID]
FROM [dbo].[Suppliers] AS [Extent1]}
Which means the query itself wouldn't tying to load the Address object at all?
So who is loading the FK objects? ToList() or VS debugger?
Other suggestion about how to confirm that whether it is Lazy loading or not?
Note: So now I confirmed that Lazy loading is working by two ToList call, one set Lazy loading off, and another set lazy loading on. Could someone point me to a way to know at what point when lazy loading another query was sent for FK property?
The VS debugger loads the related object and it happens with a second SQL query, not the one you have shown in your question. When you drill into the address object in the debugger, the debugger accesses properties of the object to display their values. Accessing this object triggers lazy loading and the second SQL query.
Edit
This works because the parent object is not your Supplier type but a class which is derived from Supplier (a "proxy"). This derived class is created dynamically at runtime and has some cryptic autogenerated name (you should see this class name in the debugger). This dynamic class has the same properties as your base Supplier but it has overloaded the DeliveryAddress property. (That's the reason why those navigation properties must be virtual, otherwise overloading wouldn't be possible.) The query uses the FK column value which has already been fetched with your supplier query and retrieves the address by this value.
The new getter of the overloaded DeliveryAddress property which is called when you or the debugger accesses the property with supplier.DeliveryAddress contains at runtime generated code which runs a SQL query to load the related object from the database. The derived proxy class holds various additional internal members, especially a reference to the context/database connection (to be able to run a query at all) and a flag which indicates for the proxy object that it already has loaded the navigation property, so that it doesn't run a second redundant query when you access the DeliveryAddress a second time.
That's in short the way how lazy loading with POCOs works.