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.
Related
I am struggling for updating several records at the same time using Linq-to-sql Orm.
The database i have made is as follows:
One Tendering has one or many details.
one TenderingDetails has one or many DetailsOptions.
One Option has One Or Many ConfigDetail.
I was able to create those normally using Linq To Sql.
but when i try to Update i end up having multiple records.
I am having a model class for each of the aforementioned entities, then i pass to the service that update the enitity of Tindering, TinderingDetails, DetailsOptions and OptionConfigDetails
then when i try to Update as follow:
the parameters are :
TenderingModel tenderingModel,
TenderingDetailsModel tenderingDetailModel,
List<TenderingOption> optionsList
try
{
var entity = ModelMapper.GetTenderingEntity(TenderingModel);
//The next statement is where i got form dataconetxt ->singleordefault
//then map the properties one by one
_unitOfWork.TenderingRepository.Update(entity);
var tenderingDetailEntity = ModelMapper.GetTenderingDetailEntity(tenderingDetailModel)
_unitOfWork.TenderingDetailsRepository.Update(tenderingDetailEntity);
//Update Options
if (optionsList != null)
{
var optionsEntities = new List<OptionItem>();
optionsList.ForEach(o =>
{
//Just get the Entity from the model
optionsEntities.Add(o.GetEntity());
});
foreach (var item in optionsEntities)
{
item.ID_Tendering_DETAILS = pricingDetailEntity.ID_Tendering_DETAILS;
//Delete the exisiting and config details
//delete all configuration details
_unitOfWork.OptionConfigurationsRepository.DeleteAllByOptionId(item.ID_OPTION);
//Add the new Config Details
item.OptionConfigurationDetails.ToList().ForEach(od =>
{
_unitOfWork.OptionConfigurationsRepository.Create(od);
});
_unitOfWork.OptionsRepository.Update(item);
}
}
//this mainly would submitChanges()
_unitOfWork.Commit();
}
catch (Exception e)
{
throw new Exception($"Error while Updating the Tendering, details are : {e.Message}");
}
what i was expecting is that i would be updating as follows:
TenderingDetail Entity would be updated by the values from the model,
the options which is a child to the Tendering Detail is to be updated by removing all its config details,
then add new ones along of updating the Options table itself
but what happens is that i would get is added records for the options and for the details instead of updating???
an Edit:
It seems that it is something i do wrong with the Update approach i am using, but i do not know what it is exactly :
for example the tendering table is having the following columns:
Id -> Primary Key
ClientId-> FK
Current Revision -> int value, nullable
some other column values that are having default values , like time stamps , and etc,
How i do the Update is, like :
public void Update(Tendering tendering)
{
var tenderingEntity = GetTendering ById(pricing.ID_Tendering);
tenderingEntity .CURRENT_REVISION = tendering.CURRENT_REVISION;
tenderingEntity .ID_CLIENT = tendering.ID_CLIENT;
}
From the code supplied, I'm not sure what _unitOfWork is here or how you're handling your db context. Typically when duplicate records are saved, it happens when different contexts are used to save the parent and child items or the item was fetched from a context other than the one you are trying to use to save. Double check the implementation of your repository and unit of work to make sure that they are issuing a single Save at the end of the operation and all values are being fetched and saved by the same context instance.
As an aside, your _UnitOfWork feels more like an active record pattern than a true unit of work.
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});
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 do I delete a record in a junction table within Entity Framework 5?
When reverse engineering my DataContext, Entity Framework seems to have recognized my junction table and automatically added Collections to my Models to represent the M:M relationship. This is great when adding items, as I can simply build my entire Entity and everything gets inserted properly. Perfect.
However, I'm stumped on removing a relationship. For example, an Activity can have multiple Contacts associated to it, and this is linked using a junction table (dbo.ActivityContacts) that consists of the columns:
ActivityID
ContactID
Both my Activity and Contact models have been updated by EF with Collections to represent the other. For example, my Activity model looks like this:
public class Activity
{
public int ActivityID { get; set; }
public string Subject { get; set; }
public virtual ICollection<Contacts> Contacts { get; set; }
}
In a non-EF environment, I would simply delete the record from the junction table and move on with my day. However, it seems I cannot access the junction table directly using EF, so I'm a tad confused on how to remove the record (relationship).
How can I properly remove a record from a junction table in Entity Framework?
Agree with #Chris.
Another solution is to do:
context.Entry(activity).State = EntityState.Deleted;
Entity Framework should remove the record for you, if you remove the associated object from either side of the relationship.
Assuming you've obtained this Activity instance from your context and want to remove a specific Contact with a known ID:
unwantedContact = context.Contacts.Find(contactID);
myActivity.Contacts.Remove(unwantedContact);
context.SaveChanges();
Should delete the record in your junction table, unless I'm being daft.
ali golshani did a good job providing a solution. Let me try to expand on it a little more. In my scenario I have two list boxes where you can move items left or right (selected or not selected)
The 'dto' object below is sent from the client. It's checking the selected state for each item in the list. If anyone knows of any way to improve this any more please leave feedback.
file_appender selectedAppender = context.file_appender.Find(dto.Id);
int[] ids = dto.Loggers.Where(x => !x.Selected).Select(x => x.Id).ToArray();
var loggers_to_delete = selectedAppender.logger.Where(x => ids.Contains(x.id));
loggers_to_delete.ToList().ForEach(x =>
{
selectedAppender.logger.Remove(x);
});
ids = dto.Loggers.Where(x => x.Selected).Select(x => x.Id).ToArray();
var loggers_to_add = context.logger.Where(x => ids.Contains(x.id));
loggers_to_add.ToList().ForEach(x =>
{
selectedAppender.logger.Add(x);
});
Lets look at another example....This one is for a list box with embedded check boxes (a little simpler). Honestly this could probably be applied to the solution above to make easier to read code.
protected void saveRelatedConnectors(test_engine testEngine, List<int> connectorTypes)
var stepConnectorsToDelete = testEngine.step_connector.Where(x => (connectorTypes.Count == 0) ||
(connectorTypes.Count != 0 && !connectorTypes.Contains(x.id)));
stepConnectorsToDelete.ToList().ForEach(x =>
{
testEngine.step_connector.Remove(x);
});
var stepConnectorsToAdd = entities.step_connector.Where(x => connectorTypes.Contains(x.id));
stepConnectorsToAdd.ToList().ForEach(x =>
{
testEngine.step_connector.Add(x);
});
entities.SaveChanges();
contact_to_delete = context.Contacts.Find(contactID);
selected_activity = context.Activity.Find(ActivityID);
context.Entry(selected_activity).Collection("Activity").Load();
selected_activity.Contacts.Remove(contact_to_delete);
db.SaveChanges();
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();