Entity Framework Code First Many-to-Many tries to create duplicates - c#

In my application I have a many-to-many join between Appointments and AppointmentTypes. When the database is created a Table called AppointmentTypesAppointments is created as the joining table.
When I create and save a new appointment which has multiple appointment types the joining table is populated correctly.
However, when I later edit and re-save the appointment, without making any changes to the appointment types, the application tries to create duplicate entries in the joining table (AppointmentTypesAppointments).
How can I stop the application from trying to create these duplicate rows?
// appointments are fetched as a group...
using (var context = new SchedulerContext())
{
//return GetAll();
IQueryable<Appointment> queryable = context.Appointments;
// check if any records exist
if (queryable.Count() == 0)
{
MessageBox.Show(#"No Appointment details found within the database");
}
return queryable.ToList();
}
//-----------------------------------------
// user then edits a single appointment and it is saved as below....
using (var context = new SchedulerContext())
{
try
{
var originalEntity = context.Appointments.Where(x => x.Id == id).FirstOrDefault();
context.Entry(originalEntity).CurrentValues.SetValues(appointment);
context.SaveChanges();
return SuccessResult();
}
catch
{
return OtherFailResult(id);
}
}

How do you do edit and resave? It's obvious that EF doesn't know that you are doing an update on an existing entity. You need check for existence to decide whether it's a add or save.

Related

How I can update inventory stock after purchase - asp.net webapplication

Can someone help me? I can't update inventory records after purchase, it shows me null error. I can't retrieve the original data from the database.
Here is my code:
DBContext db = new DBContext();
var inTB = db.InventoryTBs.Where(s => s.id == customerTB.CustomerID).FirstOrDefault();
inTB.Quantity += customerTB.CQuantity;
db.InventoryTBs.Add(inTB);
db.SaveChanges();
Thank you
To modify existing record obtained by the context just call SaveChanges():
var inTB = db.InventoryTBs.Where(s => s.id == customerTB.CustomerID).FirstOrDefault();
if (inTB != null)
{
// Modifying existing record
inTB.Quantity += customerTB.CQuantity;
}
else
{
// Adding a new record
db.InventoryTBs.Add(new InventoryTB() { Quantity = customerTB.CQuantity; /* set another properties */});
}
db.SaveChanges();
In your case a record with customerTB.CustomerID does not exist. Therefore, perhaps it's necessary to create new one and then add it to the InventoryTBs.
For additional information see: Modifying data via the DbContext

Linq-To-Sql ORM Update One To many

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.

How to delete 1000 records based upon created date ascending using Entity Framework?

I have a requirement to delete last 1000 records from a table based upon created date and then insert new 1000 records from a different table.
What would be the best possible way to do it?
public void mymethod()
{
// TestBasketRequests (delete thousands records from here)
// Call Delete();
}
public void Delete(IList<TestBasketRequest> TestBasketRequests)
{
using (var dbContext = new myContext())
{
dbContext.Entry(TestBasketRequests).State = EntityState.Deleted;
dbContext.SaveChanges();
}
}
The
RemoveRange()
method is used to delete multiple items from the database. And in the method you have to use to get the last 1000 items using
TakeLast()
will only return last items based on the order they were added to the database.
Inside your mymethod() u have to write something like this
var lastN = MyDbContext.MyDbSet
.OrderByDescending(g => g.CreateDate)
.Take(1000);
MyDbContext.MyDbSet.RemoveRange(lastN);
I didnt test this code.you can make changes as per your requirements.

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.

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