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

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.

Related

Entity Framework : map duplicate tables to single entity at runtime?

I have a legacy database with a particular table -- I will call it ItemTable -- that can have billions of rows of data. To overcome database restrictions, we have decided to split the table into "silos" whenever the number of rows reaches 100,000,000. So, ItemTable will exist, then a procedure will run in the middle of the night to check the number of rows. If numberOfRows is > 100,000,000 then silo1_ItemTable will be created. Any Items added to the database from now on will be added to silo1_ItemTable (until it grows to big, then silo2_ItemTable will exist...)
ItemTable and silo1_ItemTable can be mapped to the same Item entity because the table structures are identical, but I am not sure how to set this mapping up at runtime, or how to specify the table name for my queries. All inserts should be added to the latest siloX_ItemTable, and all Reads should be from a specified siloX_ItemTable.
I have a separate siloTracker table that will give me the table name to insert/read the data from, but I am not sure how I can use this with entity framework...
Thoughts?
You could try to use the Entity Inheritance to get this. So you have a base class which has all the fields mapped to ItemTable and then you have descendant classes that inherit from ItemTable entity and is mapped to the silo tables in the db. Every time you create a new silo you create a new entity mapped to that silo table.
[Table("ItemTable")]
public class Item
{
//All the fields in the table goes here
}
[Table("silo1_ItemTable")]
public class Silo1Item : Item
{
}
[Table("silo2_ItemTable")]
public class Silo2Item : Item
{
}
You can find more information on this here
Other option is to create a view that creates a union of all those table and map your entity to that view.
As mentioned in my comment, to solve this problem I am using the SQLQuery method that is exposed by DBSet. Since all my item tables have the exact same schema, I can use the SQLQuery to define my own query and I can pass in the name of the table to the query. Tested on my system and it is working well.
See this link for an explanation of running raw queries with entity framework:
EF raw query documentation
If anyone has a better way to solve my question, please leave a comment.
[UPDATE]
I agree that stored procedures are also a great option, but for some reason my management is very resistant to make any changes to our database. It is easier for me (and our customers) to put the sql in code and acknowledge the fact that there is raw sql. At least I can hide it from the other layers rather easily.
[/UPDATE]
Possible solution for this problem may be using context initialization with DbCompiledModel param:
var builder = new DbModelBuilder(DbModelBuilderVersion.V6_0);
builder.Configurations.Add(new EntityTypeConfiguration<EntityName>());
builder.Entity<EntityName>().ToTable("TableNameDefinedInRuntime");
var dynamicContext = new MyDbContext(builder.Build(context.Database.Connection).Compile());
For some reason in EF6 it fails on second table request, but mapping inside context looks correct on the moment of execution.

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

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).

Model a 1-to-many relationship with a single table in Entity Framework

Lets say I have 2 tables on my physical model, Receipt(ID, Location) and LineItem(ID, ReceiptID, ItemName) where a Receipt has multiple LineItems and ReceiptID is a Foreign Key to Receipt's ID.
I want to model these as a single table in my conceptual model, where I only see a table of LineItems with the Location included on each LineItem.
Every time I try to model this in the Entity Modeler, I get an error about how the Primary Key must be the same for every table being combined into the single conceptual entity.
Is this even possible to model using the entity framework?
Thanks!
No there is no way to model this directly. You must either create database view and map that view or import both entities and create QueryView in the model. In both cases resulting entity combining your two tables will become readonly and the only way to support CUD operations will be mapping stored procedures.

Entity Framework many-to-many question

Please help an EF n00b design his database.
I have several companies that produce several products, so there's a many-to-many relationship between companies and products. I have an intermediate table, Company_Product, that relates them.
Each company/product combination has a unique SKU. For example Acme widgets have SKU 123, but Omega widgets have SKU 456. I added the SKU as a field in the Company_Product intermediate table.
EF generated a model with a 1:* relationship between the company and Company_Product tables, and a 1:* relationship between the product and Company_Product tables. I really want a : relationship between company and product. But, most importantly, there's no way to access the SKU directly from the model.
Do I need to put the SKU in its own table and write a join, or is there a better way?
I just tested this in a new VS2010 project (EFv4) to be sure, and here's what I found:
When your associative table in the middle (Company_Product) has ONLY the 2 foreign keys to the other tables (CompanyID and ProductID), then adding all 3 tables to the designer ends up modeling the many to many relationship. It doesn't even generate a class for the Company_Product table. Each Company has a Products collection, and each Product has a Companies collection.
However, if your associative table (Company_Product) has other fields (such as SKU, it's own Primary Key, or other descriptive fields like dates, descriptions, etc), then the EF modeler will create a separate class, and it does what you've already seen.
Having the class in the middle with 1:* relationships out to Company and Product is not a bad thing, and you can still get the data you want with some easy queries.
// Get all products for Company with ID = 1
var q =
from compProd in context.Company_Product
where compProd.CompanyID == 1
select compProd.Product;
True, it's not as easy to just navigate the relationships of the model, when you already have your entity objects loaded, for instance, but that's what a data layer is for. Encapsulate the queries that get the data you want. If you really want to get rid of that middle Company_Product class, and have the many-to-many directly represented in the class model, then you'll have to strip down the Company_Product table to contain only the 2 foreign keys, and get rid of the SKU.
Actually, I shouldn't say you HAVE to do that...you might be able to do some edits in the designer and set it up this way anyway. I'll give it a try and report back.
UPDATE
Keeping the SKU in the Company_Product table (meaning my EF model had 3 classes, not 2; it created the Company_Payload class, with a 1:* to the other 2 tables), I tried to add an association directly between Company and Product. The steps I followed were:
Right click on the Company class in the designer
Add > Association
Set "End" on the left to be Company (it should be already)
Set "End" on the right to Product
Change both multiplicities to "* (Many)"
The navigation properties should be named "Products" and "Companies"
Hit OK.
Right Click on the association in the model > click "Table Mapping"
Under "Add a table or view" select "Company_Product"
Map Company -> ID (on left) to CompanyID (on right)
Map Product -> ID (on left) to ProductID (on right)
But, it doesn't work. It gives this error:
Error 3025: Problem in mapping fragments starting at line 175:Must specify mapping for all key properties (Company_Product.SKU) of table Company_Product.
So that particular association is invalid, because it uses Company_Product as the table, but doesn't map the SKU field to anything.
Also, while I was researching this, I came across this "Best Practice" tidbit from the book Entity Framework 4.0 Recipies (note that for an association table with extra fields, besides to 2 FKs, they refer to the extra fields as the "payload". In your case, SKU is the payload in Company_Product).
Best Practice
Unfortunately, a project
that starts out with several,
payload-free, many-to-many
relationships often ends up with
several, payload-rich, many-to-many
relationships. Refactoring a model,
especially late in the development
cycle, to accommodate payloads in the
many-to-many relationships can be
tedious. Not only are additional
entities introduced, but the queries
and navigation patterns through the
relationships change as well. Some
developers argue that every
many-to-many relationship should start
off with some payload, typically a
synthetic key, so the inevitable
addition of more payload has
significantly less impact on the
project.
So here's the best practice.
If you have a payload-free,
many-to-many relationship and you
think there is some chance that it may
change over time to include a payload,
start with an extra identity column in
the link table. When you import the
tables into your model, you will get
two one-to-many relationships, which
means the code you write and the model
you have will be ready for any number
of additional payload columns that
come along as the project matures. The
cost of an additional integer identity
column is usually a pretty small price
to pay to keep the model more
flexible.
(From Chapter 2. Entity Data Modeling Fundamentals, 2.4. Modeling a Many-to-Many Relationship with a Payload)
Sounds like good advice. Especially since you already have a payload (SKU).
I would just like to add the following to Samuel's answer:
If you want to directly query from one side of a many-to-many relationship (with payload) to the other, you can use the following code (using the same example):
Company c = context.Companies.First();
IQueryable<Product> products = c.Company_Products.Select(cp => cp.Product);
The products variable would then be all Product records associated with the Company c record. If you would like to include the SKU for each of the products, you could use an anonymous class like so:
var productsWithSKU = c.Company_Products.Select(cp => new {
ProductID = cp.Product.ID,
Name = cp.Product.Name,
Price = cp.Product.Price,
SKU = cp.SKU
});
foreach (var
You can encapsulate the first query in a read-only property for simplicity like so:
public partial class Company
{
public property IQueryable<Product> Products
{
get { return Company_Products.Select(cp => cp.Product); }
}
}
You can't do that with the query that includes the SKU because you can't return anonymous types. You would have to have a definite class, which would typically be done by either adding a non-mapped property to the Product class or creating another class that inherits from Product that would add an SKU property. If you use an inherited class though, you will not be able to make changes to it and have it managed by EF - it would only be useful for display purposes.
Cheers. :)

How Do I Create And Update A Many To Many Relationship With EF

I am using the Entity Framework with SQL Server. I have a many to many relationship between 2 tables. I have created a join table with just the primary key fields of the 2 tables. In the designer, the 2 tables now have a navigation property to the other with return types of Collection of X where X is the other entity. So far, everything just as it should be. The setup looks correct.
Task TaskProducts Product
========== ============ =======
TaskID TaskID ProductID
Description ProductID Name
Not every task will have a product or products associated with it. What do I assign to the Products navigation property of the Task table when there is no associated Product for that Task?
Do I build up a EntityCollection collection with the Product entities and assign that to the Products navigation property on the Task entity when I do have Product entities?
When doing updates(adding, removing and changing) to the Products navigation property on the Task entity, do I work with it like any other collection? Are there any special things to look out for?
I would be interested in any help working with many to many relationships in the Entity Framework.
Edit(11/17/2009)
One thing I learned is that to a many to many relationship work with a join table, BOTH fields in the join table need to be marked as primary keys;
MSDN has good documentation on managing many-to-many relationships in the Entity Framework:
http://msdn.microsoft.com/en-us/library/bb738695.aspx
The prescriptive guidance for inserts is to call the "Add" method on the entity collection and specify the related object (versus setting the Value property on the entity reference for a one-to-many relationship.)
Updates are handled just like any other EF update... load the desired object, set the changed properties and call SaveChanges on the context.
Deletes are handled the same as well, call DeleteObject on the context and then SaveChanges.