How to delete all many-to-many relations without loading related entities in Entity Framework? - entity-framework-4

I have db scheme where Product table have many to many relation to Color table. I'm using EF and create POCO objects:
public class Product
{
public Guid Id {get;set;}
public ICollection<Color> Colors {get;set;}
}
public class Color
{
public Guid Id {get;set;}
public ICollection<Product> Products {get;set;}
}
In many situations it is necessary to delete all colors related to product and set new colors. So i want to delete all many to many relations whitout exactly knowing id of related colors. Is it possible to delete them without additional queries to db? I know i can just write stored procedure which will delete all relation with colors for specified product, but it will be better to find general approach through entity framework.

If you don't know keys of colors you cannot delete them without loading them first - EF deletes records one by one so it needs to know which record to delete.
The straight forward option is executing SQL DELETE directly:
dbContext.Database
.ExecuteSqlCommand("DELETE FROM dbo.ProductColors WHERE ProductId = #Id", product.Id);

Related

Entity Framework Mapping. Multiple Foreign keys

I have two tables
People Relation
------------- -----------------
Id (int) Id (int)
Name (string) ParentPeopleId (int)
ChildPeopleId (int)
I need to get all people by Relation table with union all.
The relation table has two foreign keys. And there is one problem with mapping them. The mapping is one to many. People has many Relation, Relation has one People.
I mapped them like this:
HasRequired(r=> r.People).WithMany(p=>p.Relation).HasForeignKey(r=>r.ChildPeopleId);
So, how can I map the second foreign key?
Per each FK column in your Relations table you should have a navigation property in your Relation entity (this is not mandatory, but what is mandatory is have at least one navigation property between the entities involve in the relationship). In this case you have two relationships between People and Relations, and a navigation property represents one end in an relationship. Your model could be this way:
public class Relation
{
public int Id {get;set;}
public int ParentPeopleId {get;set;}
public int ChildPeopleId {get;set;}
public virtual People ParentPeople {get;set;}
public virtual People ChildPeople {get;set;}
}
public class People
{
public int Id {get;set;}
public string Name {get;set;}
public virtual ICollection<Relation> ParentRelations {get;set;}
public virtual ICollection<Relation> ChildRelations {get;set;}
}
And the Fluent Api configurations like this:
HasRequired(r=> r.ParentPeople ).WithMany(p=>p.ParentRelations ).HasForeignKey(r=>r.ParentPeopleId);
HasRequired(r=> r.ChildPeople).WithMany(p=>p.ChildRelations ).HasForeignKey(r=>r.ChildPeopleId );
Now if you don't want to work with one of the collection navigation properties in your People entity, you can create an unidirectional relationship. For example if you don't want ParenRelations navigation property, you can configure that relationship as follow:
HasRequired(r=> r.ParentPeople).WithMany().HasForeignKey(r=>r.ParentPeopleId);
Update
Let me start first with a suggestion. I thing your table Relation is not playing any role is you have only those columns. If a person con only have a parent I would change your model to the following:
public class People
{
public int Id {get;set;}
public string Name {get;set;}
public int ParentId {get;set;}
public virtual People Parent {get;set;}
public virtual ICollection<People> Children {get;set;}
}
And you relationship configuration would be:
HasOptional(r=> r.Parent).WithMany(p=>p.Children).HasForeignKey(r=>r.ParentId);
Now going back to your current model, EF sees your ChildPeopleId property as an simple scalar column, it doesn't know it's a FK column, that's way I suggested above map two relationship instead one.
Another thing, with the below line
var Peoplelist = MyDbContext.People.Include(p=>p.Relations.Select(r=>r.People)).ToList();
You are telling to EF that you want to load the Relation entities related to a People, but also you want to load the People related with each Relation, which is at the same time the same People where the Relation came from, so, you don't need to do the last select, if your data is properly related, that People navigation property is going to be loaded when you execute your query,so, that query should be this way:
var Peoplelist = MyDbContext.People.Include(p=>p.Relations).ToList();

Can I delete a single child entity without loading the entire collection?

I have 2 classes, like the below.
They can have very large collections - a Website may have 2,000+ WebsitePages and vice-versa.
class WebsitePage
{
public int ID {get;set;}
public string Title {get;set;}
public List<Website> Websites {get;set;}
}
class Website
{
public int ID {get;set;}
public string Title {get;set;}
public List<WebsitePage> WebsitePages {get;set;}
}
I am having trouble removing a WebsitePage from a Website. Particularly when removing a WebsitePage from mutliple Websites.
For example, I might have code like this:
var pageToRemove = db.WebsitePages.FirstOrDefault();
var websites = db.Websites.Include(i => i.WebsitePages).ToList();
foreach(var website in websites)
{
website.WebsitePages.Remove(pageToRemove)
}
If each website Include() 2k pages, you can imagine it takes ages to load that second line.
But if I don't Include() the WebsitePages when fetching the Websites, there is no child collection loaded for me to delete from.
I have tried to just Include() the pages that I need to delete, but of course when saving that gives me an empty collection.
Is there a recommended or better way to approach this?
I am working with an existing MVC site and I would rather not have to create an entity class for the join table unless absolutely necessary.
No, you can't... normally.
A many-to-many relationship (with a hidden junction table) can only be affected by adding/removing items in the nested collections. And for this the collections must be loaded.
But there are some options.
Option 1.
Delete data from the junction table by raw SQL. Basically this looks like
context.Database.ExecuteSqlCommand(
"DELETE FROM WebsiteWebsitePage WHERE WebsiteID = x AND WebsitePageID = y"));
(not using parameters).
Option 2.
Include the junction into the class model, i.e. map the junction table to a class WebsiteWebsitePage. Both Website and WebsitePage will now have
public ICollection<WebsiteWebsitePage> WebsiteWebsitePages { get; set; }
and WebsiteWebsitePage will have reference properties to both Website and WebsitePage. Now you can manipulate the junctions directly through the class model.
I consider this the best option, because everything happens the standard way of working with entities with validations and tracking and all. Also, chances are that sooner or later you will need an explicit junction class because you're going to want to add more data to it.
Option 3.
The box of tricks.
I tried to do this by removing a stub entity from the collection. In your case: create a WebsitePage object with a valid primary key value and remove it from Website.WebsitePages without loading the collection. But EF doesn't notice the change because it isn't tracking Website.WebsitePages, and the item is not in the collection to begin with.
But this made me realize I had to make EF track a Website.WebsitePages collection with 1 item in it and then remove that item. I got this working by first building the Website item and then attaching it to a new context. I'll show the code I used (a standard Product - Category model) to prevent typos.
Product prd;
// Step 1: build an object with 1 item in its collection
Category cat = new Category { Id = 3 }; // Stub entity
using(var db = new ProdCatContext())
{
db.Configuration.LazyLoadingEnabled = false;
prd = db.Products.First();
prd.Categories.Add(cat);
}
// Step 2: attach to a new context and remove the category.
using(var db = new ProdCatContext())
{
db.Configuration.LazyLoadingEnabled = false;
db.Products.Attach(prd);
prd.Categories.Remove(cat);
db.SaveChanges(); // Deletes the junction record.
}
Lazy loading is disabled, otherwise the Categories would still be loaded when prd.Categories is addressed.
My interpretation of what happens here is: In the second step, EF not only starts tracking the product when you attach it, but also its associations, because it 'knows' you can't load these associations yourself in a many to many relationship. It doesn't do this, however, when you add the category in the first step.

Changing relationships between tables to simplify data access in MVC3

Asp.net MVC3 app with Entity framework. Lets say I have 3 tables; Article, Category and Author.
I create relations between
Category.CategoryId -> Article.CategoryId and Author.AuthorId -> Article.AuthorId
Using code first navigation properties
public virtual Category Category { get; set; }
public virtual Author Author { get; set; }
That means that when I view a list of the articles I have to :
return View(db.Article
.Include(c=>c.Category)
.Include(a=>a.Author)
.ToList());
In order to have access to the names of categories and authors and not just their id’s
How much would it hurt to break this classic schema and not create relationships between these tables? Then I could just return SelectLists from Author and Category Tables in a ViewModel and populate the Category and Author fields in my Article table directly with the corresponding names not the id’s and also preserve data integrity.
My query would be simplified to just:
return View(db.Article.ToList());
I suppose I will have to create indexes for those fields to speed up searches.
Is this being done somewhere or is it completely wrong?
Does it have better or worse performance?
#Panos, your original approach is correct, deleting foreign keys would be a mistake. With the includes you avoid the lazy loading in this scenario and you have a good performance.
public virtual Category Category { get; set; }
public virtual Author Author { get; set; }
you defined category and Author as virtual and it means these object wont load without an Include command in your query. you man use a select list in your grid without removing these relations, because these relations doesn't have any real load without Include command in your query.
but be aware of using .ToList() this will load all records of your query and later it may become a large amount of data.

Image database organization

I am curious as to how I should organize image storage in my application. They will be stored on disk somewhere, with the path / url stored in the database, but my application has become complex and image organization is leaving me baffled. The technology I am using is ASP.NET MVC 3 + EF 4.1 Code First (which is proving to be difficult with complex relationships).
This is a family based application, so there is a Family table, Member table, and a FamilyMember table to link to two. There is also a FamilyEvent, and MemberEvent table to track events in the family or member's life.
Use cases
A Member can upload multiple profile pictures (possibly to be put in a "profile" album?)
A Member can upload "family" photos, to be attached to the Family object, and put into some type of album for the family.
A Member can add a member event, and attach multiple images to the members event.
A Member can add a family event, and attach multiple images to the family's event.
These are the base use cases.
My thoughts
Base "Picture" table with id, description, and maybe the user id of the uploader.
MemberProfilePicture table with id, pictureId, memberId, etc...
MemberEventPicture table with id, pictureId, [member id?], membereventid, etc...
FamilyEventPicture table with id, pictureId, [familyId ?], familyeventid, etc...
This would allow a family or member to essentially "link" a Picture later to another profile pic / member event / or family event.
My question
Is this correct, or am I over-complicating this? I don't see any other way other than multiple tables to represent images for different things, which all refer to the base Picture table. Can anyone give any suggestions as to how I can improve this model? While I understand there may be many definitions to correct, I'm looking for solutions as far as how to organize the imaging aspect of my application, or improvements on what I have already.
Your using EF Code First so let's talk code not tables. You can realize your use cases by having only one Picture class and associate many Pictures with Member, Family and so on.
Taking this code
public class Family
{
public int Id { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
public class Member
{
public int Id { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
}
public class Picture
{
public int Id { get; set; }
public String Location { get; set; }
}
Will generate 3 Tables, Families, Members, Pictures with the Pictures table having two foreign keys Family_Id and Member_Id. This model allows to associated a single picture with Member, Family or both.
From a scalability point of view I would avoid to store the image binary in the database and using an ORM to access the binary. Better ideas are to store the picture in the file system or a "specialized" server like MongoDBs GridFS.
What I would do is have three tables.
The 'picture' table which stores a unique id for each picture and the location.
The 'profile' table which has your dtails about the profile.
A third 'linking' table composed of pictureId and profileId
If I want to link picture A to profile X I just add in "A,X" to the linking table. Same thing for events or whatever. If there's going to be a many-to-many relationship this is the cleanest way to go (I'm assuming that the same picture can be in multiple profiles).
Hope I understood your problem correctly.

single entity for different tables

I have same type of tables
ProductCodeTable, CountrycodeTable etc
All have key, value as their fields
When I use entity frame work,
Can I have a single entity for all these tables such that I can formulate different queries to get data from different tables?
You can create a base class for all of them and create sub class for each entity
public abstract class LookUpEntity
{
[Key]
public int Key { get; set; }
[Required]
public string Value { get; set; }
}
[Table("ProductCodeTable")]
public class ProductCode : LookUpEntity
{
}
This way you can model the relationships also and later if you wanted to add specific properties to those look up entities with out affecting other entities.
You can create a view with a Union of all tables like this:
create view AllKeyTables as
SELECT 'Product' as table, Productkey as Key, nameProduct as name
FROM ProductCodeTable
UNION
SELECT 'Country' as table, CountryISO as key, CountryName as name
FROM CountrycodeTable
UNION
...
Then update EF model and check 'table' and 'key' fields as Entity Primary Key.
Next question you will do is: 'How can I make a relation between this Entity and existing Entities?' The answer is 'you can't because EF is not able to join Entities between other fields than primary key'. Then, before implement this solution, be sure that this is you are looking for.
EF supports this only if you model it as inheritance (#Eranga showed it in code-first approach). You will have single base entity and derived entity for each table so you will not avoid having different type for each table. Otherwise the answer is no.
Inheritance will put additional requirements on data in your tables. For example Ids will have to be unique among all tables. So if ProductTableCode will have record with Id 1, CountryCodeTable (and any other code table) mustn't have record with Id 1.
Also inheritance in EF can produce nasty and very poorly performing queries.

Resources