How to use DBContext.Add/Attach (using EF CodeFirst 4.1) with nested opbjects - entity-framework

Problem: When adding an object "Order" to my dbcontext, all nested objects of the order gets "readded" to the database, though the nested objects is static data and only a reference shoudl be added in the database.
Example:
The database holds 0 orders, and 3 items.
I add one order with 2 items.
Now the database hold 1 order, and 5 items. The two items in the order has been "readded" to the database, even though the items had the right primary keys before db.SaveChanges().
I realize that i may be able to attach the existing items to the dbcontext before saving changes, but is that really the only way to go? Can't EF figure out that to item already exists when the primary key matches an existing item?
Does anyone know if this is different in the new version of EF CodeFirst?

No EF cannot figure if entities are existing one or new one - both Add and Attach commands are graph oriented operations. You call them on one entity in the graph and they traverse all relations (and their relations and so on) and perform the operation for them as well.
You must figure correct state of each entity in the graph for example by using:
dbContext.Orders.Add(newOrder);
foreach(var item in newOrder.Items) {
dbContext.Entry(item).State = EntityState.Unchanged;
}
dbContext.SaveChanges();
You can use the reverse operation by calling Attach(newOrder) and set the order to Added state. The main difference will come with independent associations (for example many-to-many relations). The first approach will correctly add new relation between order and each item whereas second will not unless you manually set each relation to Added state (and changing state for relations is more complex).

Related

How to set up relationships between new and existing entities in EF

My application allows the user to create a hierarchy of new entities via a UI - let's say it's a "Customer" plus one or more child "Order" entities. The user also assigns each Order entity to an existing "OrderDiscount" entity (think of these as "reference"/"lookup" items retrieved from the database). Some time later, the user will choose to save the whole hierarchy to the database, accomplished like this:-
using (var context = new MyContext())
{
context.Customers.Add(customer);
foreach (var entity in context.OrderDiscounts.Local)
{
objectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
}
context.SaveChanges();
}
The foreach loop changes the state of the OrderDiscount entities to Unchanged, and prevents EF from attempting to insert them into the database, resulting in duplicates.
Great so far, but I've now hit another issue. For reasons I won't go into, the OrderDiscount entities can come from different BLL calls, resulting in a situation where two Orders in the graph may appear to reference the same OrderDiscount (i.e. both have the same PK ID, and other properties), but the entities are different object references.
When I save, the above foreach loop fails with the message "AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges". I can see the two OrderDiscount objects in the context.OrderDiscounts.Local collection, both with the same PK ID.
I'm not sure how I can avoid this situation. Any suggestions?
This article (http://msdn.microsoft.com/en-us/magazine/dn166926.aspx) describes the scenario and provides one possible solution, which is to set just the FK ID (order.OrderDiscountId), and leave the order.OrderDiscount relationship null. Unfortunately it's not feasible in my case, as further down the line I rely on being able to traverse such relationships, e.g. ApplyDiscount(order.OrderDiscount);.

Create One-to-One relationship based on PK of both tables

I'm really new to Entity Framework (currently using EF5) and vs2012 and am having difficulty trying to figure something out.
I have an .edmx that was generated from my database. It has two tables in it: Item and 3rdPartyItem. In short, the Item table is the main table for all the company items while the 3rdPartyItem table is a table that is used to hold additional attributes for items. This 3rdPartyItem table was created by an outside company and is used with their software for the company, so I can't mess with either table. What I'm trying to do is show a list of Items in a grid but I need to show a combination of all the fields for both tables. In vs2012, if I create a relationship and make it 'zero-to-one' (because for each record in the Item table, there doesn't necessarily have to be one in the 3rdPartyItem table), vs complains about not being mapped correctly. When I set the mapping, it then complains that there's multiple relationships. I did some research and found that you can't create a relationship with 2 primary keys, so I was thinking that was the problem. But how can I change the .edmx so that in code, I can access Items and 3rdPartyItem like so:
var items = dbContext.Items;
items.3rdPartyItem.SomeField <--field from 3rdPartyItem table.
not too sure if it's even possible, but it would be very, very helpful if so. Any ideas?
What you're looking for is table-per-type (TPT) mapping inheritance. You can view an MSDN walkthrough here (although you'd want your base type to be instantiable):
http://msdn.microsoft.com/en-us/data/jj618293.aspx

Select Specific Columns from Database using EF Code First

We have a customer very large table with over 500 columns (i know someone does that!)
Many of these columns are in fact foreign keys to other tables.
We also have the requirement to eager load some of the related tables.
Is there any way in Linq to SQL or Dynamic Linq to specify what columns to be retrieved from the database?
I am looking for a linq statement that actually HAS this effect on the generated SQL Statement:
SELECT Id, Name FROM Book
When we run the reguar query generated by EF, SQL Server throws an error that you have reached the maximum number of columns that can be selected within a query!!!
Any help is much appreciated!
Yes exactly this is the case, the table has 500 columns and is self referencing our tool automatically eager loads the first level relations and this hits the SQL limit on number of columns that can be queried.
I was hoping that I can set to only load limited columns of the related Entities such as Id and Name (which is used in the UI to view the record to user)
I guess the other option is to control what FK columns should be eager loaded. However this still remains problem for tables that has a binary or ntext column which you may not want to load all the times.
Is there a way to hook multiple models (Entities) to the same table in Code First? We tried doing this I think the effort failed miserably.
Yes you can return only subset of columns by using projection:
var result = from x in context.LargeTable
select new { x.Id, x.Name };
The problem: projection and eager loading doesn't work together. Once you start using projections or custom joins you are changing shape of the query and you cannot use Include (EF will ignore it). The only way in such scenario is to manually include relations in the projected result set:
var result = from x in context.LargeTable
select new {
Id = x.Id,
Name = x.Name,
// You can filter or project relations as well
RelatedEnitites = x.SomeRelation.Where(...)
};
You can also project to specific type BUT that specific type must not be mapped (so you cannot for example project to LargeTable entity from my sample). Projection to the mapped entity can be done only on materialized data in Linq-to-objects.
Edit:
There is probably some misunderstanding how EF works. EF works on top of entities - entity is what you have mapped. If you map 500 columns to the entity, EF simply use that entity as you defined it. It means that querying loads entity and persisting saves entity.
Why it works this way? Entity is considered as atomic data structure and its data can be loaded and tracked only once - that is a key feature for ability to correctly persist changes back to the database. It doesn't mean that you should not load only subset of columns if you need it but you should understand that loading subset of columns doesn't define your original entity - it is considered as arbitrary view on data in your entity. This view is not tracked and cannot be persisted back to database without some additional effort (simply because EF doesn't hold any information about the origin of the projection).
EF also place some additional constraints on the ability to map the entity
Each table can be normally mapped only once. Why? Again because mapping table multiple times to different entities can break ability to correctly persist those entities - for example if any non-key column is mapped twice and you load instance of both entities mapped to the same record, which of mapped values will you use during saving changes?
There are two exceptions which allow you mapping table multiple times
Table per hierarchy inheritance - this is a mapping where table can contains records from multiple entity types defined in inheritance hierarchy. Columns mapped to the base entity in the hierarchy must be shared by all entities. Every derived entity type can have its own columns mapped to its specific properties (other entity types have these columns always empty). It is not possible to share column for derived properties among multiple entities. There must also be one additional column called discriminator telling EF which entity type is stored in the record - this columns cannot be mapped as property because it is already mapped as type discriminator.
Table splitting - this is direct solution for the single table mapping limitation. It allows you to split table into multiple entities with some constraints:
There must be one-to-one relation between entities. You have one central entity used to load the core data and all other entities are accessible through navigation properties from this entity. Eager loading, lazy loading and explicit loading works normally.
The relation is real 1-1 so both parts or relation must always exists.
Entities must not share any property except the key - this constraint will solve the initial problem because each modifiable property is mapped only once
Every entity from the split table must have a mapped key property
Insertion requires whole object graph to be populated because other entities can contain mapped required columns
Linq-to-Sql also contains ability to mark a column as lazy loaded but this feature is currently not available in EF - you can vote for that feature.
It leads to your options for optimization
Use projections to get read-only "view" for entity
You can do that in Linq query as I showed in the previous part of this answer
You can create database view and map it as a new "entity"
In EDMX you can also use Defining query or Query view to encapsulate either SQL or ESQL projection in your mapping
Use table splitting
EDMX allows you splitting table to many entities without any problem
Code first allows you splitting table as well but there are some problems when you split table to more than two entities (I think it requires each entity type to have navigation property to all other entity types from split table - that makes it really hard to use).
Create stored procedures that query the number of columns needed and then call the stored procs from code.

adding entries to the "Relational" table in entity model? how do i do that?

so the story is very simple.
I have one table called Products and another Called categories. In addition, i have another table called ProductCategories that hold the relationship of catetories to their corresponding products (i.e, the table has two columns, ProductId, ColumnId).
For some reason, after adding all those table to my entity model, i don't have "Access" to it, hence i can do myentityModel.ProductCategories, so i could relational items between those two tables.
And yes, the ProductCategores table is added as "Association" to the entity model. i don't really understand that.
EDIT:
I do see that as part of creating new "Product" i can pass EntityCollection of "Category". So i do query from my entity model for a list of the matching categories that the user selected (on the webpage). so for example, i get (after query the model), an Objectset of "Category". However, i encountered two issues:
the 'AddObject' accept only EntityCollection, hence i need to re-create a set and then add all the objects from the ObjectSet to the entityCollection, in this process i need to detach it from the previous model and add it to the new collection. if not, i get an exception.
when i do the SaveChanges, i see that i get an exception that it was actually trying to Create new Category rather than adding new ProductCategory. again, am i missing something here?
Thanks.
This sounds like a Many-to-Many relationship. In your entity model, you don't need to declare the join table as a separate entity. Instead, you configure the relationship between the Products and the Categories as a Many-to-Many and add metadata about the join table. In Hibernate, you would have:
#ManyToMany(targetEntity=Categories.class, cascade={CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinTable(name="tb_products_categories",
joinColumns=#JoinColumn(name="category_id"),
inverseJoinColumns=#JoinColumn(name="product_id")
)
#IndexColumn(name="join_id")
public List<Categories> getCategories() {
return categories;
}
When you query, the ORM layer takes care of determining SQL and traversing table joins.

Entity Framework: Exclude columns from the selection in Entity Framework?

I want to have an ObjectQuery that returns tracked entities (not static data), but I don't want it to load all the columns, I want some columns to load as null, I don't want to use select, since this will return an IEnumerable of the values, not tracked objects.
Is there a way to do it?
If yes, how do I then complete reloading those columns on demand?
Have you tried creating a view and then mapping the view?
By creating a view you can select the columns that you really want and only those will show up on the Entity Model.
I think the only way is to create new entity type which will not contain columns you don't need. You will map this entity type to the same table. On demand (lazy) loading works only for navigation properties.
Edit:
My previous idea doesn't work but in some special cases you can use idea from this article. Instead of modeling single entity from single table you will model multiple entities related with 1:1 relations. Entities will not overlap in properties (except the primary key) as my previous idea assumed because it doesn't work. You will than have main entity with fields you want to load immediately and related entities which will be lazy loaded when needed.