Entity Framework, Seeding on Foreign Keys - c#

My question is, can you easily seed a third tier of data using entity framework. I know two tiers of foreign key work as I use them all the time, though will three?
My data structure is the following.
Top Level Table
Table One - Company (One to many of Areas)
- "list" Areas
Table Two - Areas (One to many Items)
- "list" Items
Table Three - Items (Many to One)
- ItemID
Currently the syntax for two levels (Between Company and Areas) works fine:
var NewCompany = new Company() {
Areas = new List<Area>(){
Areaid = 0
}
}
_context.Company.Add(NewCompany);
_context.Areas.AddRange(NewCompany.Areas);
The question is how to achieve the following, adding the third table as a list to Areas.
var NewCompany = new Company() {
Areas = new List<Area>(){
Items = new List<Items>(){
itemId= 1
}
}
}
_context.Company.Add(NewCompany);
_context.Areas.AddRange(NewCompany.Areas);
_context.Areas.AddRange(NewCompany.Areas.Items);

Ideally if you specified the constraint well in database and configured the model correctly then you would not require to add the related entities (Area & Items) to the relavant entity collection.
var NewCompany = new Company() {
Areas = new List<Area>(){
Areaid = 0
Items = new List<Item>(){
new Item(1),
new Item(2)
...
...
}
}
}
//just add newCompany instance to the entity collection rest should be automatically taken care.
_context.Company.Add(NewCompany);
//no need to write below statement.
//_context.Areas.AddRange(NewCompany.Areas);

Related

EF friendly way to update child collections whose relationship is a mapping table

For some reason I can't update child collections using Entity Framework. I have the following schema which has been generated in an .edmx.
MainRecord RecordType RecordTypeMap
MainRecordID RecordTypeID RecordTypeMap
[Other fields] RecordTypeName RecordTypeID
The RecordType table holds a small list of RecordTypes, the idea is to use the mapping table.
A MainRecord can have more than one RecordType, so the RecordTypeMap table might look like the following:
MainRecordID RecordTypeID
1002 1
1002 2
1003 1
1004 2
1004 3
1004 4
The way that EF generates this is that it knows that this is a mapping table, and my MainRecord objects have a collection of RecordTypes. The problems are that the wrong information seems to go into the database when I attempt to add, edit, or delete these records. If I am adding a new MainRecord with a call like:
context.MainRecords.Add(record);
The RecordTypes for this particular MainRecord end up in the RecordType table with new RecordTypeIDs, instead of in the RecordTypeMap table with an existing RecordTypeID. I have tried a number of permutations for editing and deleting these RecordTypes from their association with the MainRecord. While I can hack a solution, isn't there an EF-friendly way to perform the mapping function above? I'm using EF6.
EDIT: How can I made the below code edit the RecordType records associated with the MainRecord? The 'Add' section of the code works fine, although why it works when the entity state is considered 'modified' for the RecordType is beyond me...
Here is the code that saves the record. If there is an obvious syntax problem it is a transcription error.
public bool SaveRecord(MainRecord record)
{
using (var context = new DBEntities())
{
var existingRecord = context.MainRecords.SingleOrDefault(x => x.MainRecordID == record.MainRecordID);
if (existingContact != null)
{
context.Entry(existingRecord).CurrentValues.SetValues(record);
}
else
{
// Add new record.
context.MainRecords.Add(record);
// The below properly adds entries with the existing keys into the Map table.
// Not sure why this does that, it doesn't look like it would on the surface.
foreach (var type in contact.tblContactTypes)
{
context.Entry(type).State = EntityState.Modified;
}
}
}
context.SaveChanges();
return true;
}

EntityFramework update existing records with new foreign key

I have a database and a set of tables that already have data in.
I am using code first. Now I have been asked to add a Category table to the database and with that, to change some of the other tables to reference that table via a foreign key.
I know through previous experience, that if I update the database after changing the models, it will moan because an FK can't have 0.
So I want to use the Seed method to actually update the Foreign Keys as the database is updated.
I want to do something like this:
protected override void Seed(DatabaseContext context)
{
// Create our categories
context.Categories.AddOrUpdate(m => m.Id,
new Category { Id = 1, Name = "Cameras" },
new Category { Id = 2, Name = "Televisions" }
);
// Get all our current feeds
var feeds = context.Feeds.ToList();
context.Feeds.AddOrUpdate(m => m.Id, feeds.Select(m => m.CategoryId = 1).ToArray());
}
Can someone let me know how I might actually do this? Or if there is another way of doing it?
Inside a loop:
foreach(var f in context.Feeds)
{
f.CategoryId=1;
}
context.SaveChanges();
Or using Entity Framework Extended library to do it in one batch:
context.Feeds.Update(f=>new Feed{CategoryId=1});

Removing many to many entity Framework

There is a many to many relationship between Artist and ArtistType. I can easily add artist ArtistType like below
foreach (var artistType in this._db.ArtistTypes
.Where(artistType => vm.SelectedIds.Contains(artistType.ArtistTypeID)))
{
artist.ArtistTypes.Add(artistType);
}
_db.ArtistDetails.Add(artist);
_db.SaveChanges();
This goes and updates the many to many association table with correct mapping. But when I try to remove any item from table I do not get any error but it does not remove it from the table?
foreach (var artistType in this._db.ArtistTypes
.Where(at => vm.SelectedIds.Contains(at.ArtistTypeID)))
{
artistDetail.ArtistTypes.Remove(artistType);
}
this._db.Entry(artistDetail).State = EntityState.Modified;
this._db.SaveChanges();
What am I missing?
Standard way is to load the artist including the current related types from the database and then remove the types with the selected Ids from the loaded types collection. Change tracking will recognize which types have been removed and write the correct DELETE statements to the join table:
var artist = this._db.Artists.Include(a => a.ArtistTypes)
.SingleOrDefault(a => a.ArtistID == someArtistID);
if (artist != null)
{
foreach (var artistType in artist.ArtistTypes
.Where(at => vm.SelectedIds.Contains(at.ArtistTypeID)).ToList())
{
artist.ArtistTypes.Remove(artistType);
}
this._db.SaveChanges();
}
For removing only one field, I came up with this solution. It seems odd but in EF, most of the things are odd anyway because we try to tell EF the database ops in terms of OOP.
using (var db = new Context())
{
//Create existing entities without fetch:
var artist = new Artist() { ArtistID = _artistID };
var type = new Type() { TypeID = _typeID };
//Add one entity to other's list
//This is in memory, not connected.
//So we do this because we try to tell EF that we want to remove this item
//Without fetch, we should add it first in order to remove :)
artist.ArtistTypes.Add(type);
//Attach that entity which you add an item to its list:
db.Artists.Attach(artist);
//It's now connected and recognized by EF as database operation
//After attaching, remove that item from list and save db
artist.ArtistTypes.Remove(type);
db.SaveChanges();
}
That's it! With this solution, you are no longer fetching all entries of joined table ArtistTypes.

How to add or remove a many-to-many relationship in Entity Framework?

There's a many-to-many UserFeed table that stands between User and Feed and denotes a twitter-like follow relationship.
It only has two fields, which form a composite key: UserID and FeedID.
I need to write a method that will subscribe or unsubscribe a user from a feed based on a boolean flag.
public void SetSubscriptionFlag (int userId, int storeId, bool subscribe)
{
}
I'm new to Entity Framework so I'm trying to find and follow an "EF-ish" way to accomplish this.
My initial thoughts are:
Instead of working with the middle UserFeed class, I should create a many-to-many Subscriptions property (EDIT: hit limitations here);
After I've done so, I'll need to fetch a User instance by ID, check whether it has given Feed in its Subscriptions and add/delete it depending on the flag and current existence;
Figure out how to avoid racing conflicts when there is a time interval before the check and adding/deleting and user manages to submit two adding or deletion requests;
Optimize my code as to avoid unneccessary SELECTs, if any occur, because all I really want to do is a single SELECT and single INSERT/DELETE.
A relevant code snippet and comment on my points is highly appreciated.
Thanks!
You can use dummy objects - it definitely works for insert and I hope it can be used for delete as well:
Create new relation:
var user = new User() { Id = userId };
context.Users.Attach(user);
var store = new Store() { Id = storeId };
context.Stores.Attach(store);
// Now context trackes both entities as "existing"
// and you can build a new relation
user.Subscriptions.Add(store);
context.SaveChanges();
Remove existing relation:
var user = new User() { Id = userId };
var store = new Store() { Id = storeId };
user.Subscriptions.Add(store);
context.Users.Attach(user);
// Now context trackes both entities as "existing"
// with "existing" relation so you can try to remove it
user.Subscriptions.Remove(store);
context.SaveChanges();

Save a relation with between two entities an N-N association

I've a Entity Framework 4.0, with poco object. the edmx model file is generated from the database.
This datacontext is accessed through WCF service, it's only mean that I receive some objects and I need to attach them to the current datacontext(or reload them with the key correspondance).
Everything seems to work fine, except for one case:
I've a N-N relationship between two table, so I've an association table, without any field other than ID of two tables:
LINQ transform this into the following schema, this seems to be right.
When I retrieve data, there is no problem, data I've inserted myself in the Right_group are correctly transformed into "new object in my collection of Rights/Groups".
But if I try to modify something and save, it doesn't work
public void SaveRights(Group group, List<Rights> rights){
//here, group and rights are objects attached to the database
group.Rights.Clear();
group.Rights.AddRange(rights);
_dataContext.SaveChanges();
}
So my question is: How to save this "relationship" of two objects ?
Thank you!
If you want to avoid loading the objects from the database first you can do it like this(Code taken from one of my aplications so you will have to adapt it):
public void AddAndRemovePersons(int id, int[] toAdd, int[] toDelete)
{
var mailList = new MailList { ID = id, ContactInformations = new List<ContactInformation>() };
this.db.MailLists.Attach(mailList);
foreach (var item in toAdd)
{
var ci = new ContactInformation { ID = item };
this.db.ContactInformations.Attach(ci);
this.db.ObjectStateManager.ChangeRelationshipState(mailList, ci, ml => ml.ContactInformations, System.Data.EntityState.Added);
}
foreach (var item in toDelete)
{
var ci = new ContactInformation { ID = item };
this.db.ContactInformations.Attach(ci);
this.db.ObjectStateManager.ChangeRelationshipState(mailList, ci, ml => ml.ContactInformations, System.Data.EntityState.Deleted);
}
}
I found deleting the relationship as hard as creating it so I left that code in there. One thing about this solution is that both the maillist and the contacts exist prior to this function being run. I attach them to make the state manager track them.
If you are adding new objects that you also want to save you would use the
this.db.MailLists.AddObject(you new item here)
I hope that helps!
Just a thought... how are the keys setup in the Right_Group table? If you use both IDRight and IDGroup together as primary key - this problem might occur. One suggetion is to add a new column (ID) into the Right_Group table, and having this ID as the primary key. Then use foreign keys on the other columns (IDRight, IDGroup) respectivly.

Categories