Duplicate record in database when call SaveChanges() - c#

Hi i have a problem with EF. In my application i have to load from database some content to populate a DataGrid.
UserControl :
contenus = new List<Contenu>();
contenus = sacoche.Contenus.ToList(); // i get sacoche in the parameter of the contructor
ContenuViewSource.Source = contenus;
ContenuView = (ListCollectionView)ContenuViewSource.View;
ContenuView.Refresh();
everything work just fine, but when i try to add some others Contenus i get a duplicate record in the database. The only difference between the duplicated record is that the first record loose his foreign key.
Here i add my Contenuto my Sacoche:
editableSacoche = SacocheDal.dbContext.Sacoches.Include("Contenus").First(i => i.SacocheID == editableSacoche.SacocheID);
editableSacoche.Contenus = contenus;
SacocheDal.dbContext.SaveChanges();
all i do is get the Sacoche and add to it his Contenu and finally call SaveChanges().
Here is the result :
EDIT: I tried to get only the new items but failed.
List<Contenu> contenuAjoute = contenus.Except(editableSacoche.Contenus.ToList()).ToList();
in contenuAjoutei get all the records even if they are equal ...

Try this:
editableSacoche = SacocheDal.dbContext.Sacoches.Include("Contenus").First(i => i.SacocheID == editableSacoche.SacocheID);
editableSacoche.Contenus = null;
editableSacoche.ContenusID = contenus.ID;
SacocheDal.dbContext.SaveChanges();

I found a way to achieve what i wanted. I create an ItemComparer and use Exceptto only add the new items.
Here is the comparer :
class ContenuComparer : IEqualityComparer<Contenu>
{
public bool Equals(Contenu x, Contenu y)
{
if (x.ContenuID == y.ContenuID)
return true;
return false;
}
public int GetHashCode(Contenu obj)
{
return obj.ContenuID.GetHashCode();
}
}
And Here the code :
editableSacoche = SacocheDal.dbContext.Sacoches.Include("Contenus").First(i => i.SacocheID == editableSacoche.SacocheID);
List<Contenu> contenuAjoute = contenus.Except(editableSacoche.Contenus.ToList(), new ContenuComparer()).ToList();
foreach (Contenu c in contenuAjoute)
{
editableSacoche.Contenus.Add(c);
}
SacocheDal.dbContext.SaveChanges();
I don't now if it's the right way but it works fine.

Related

SaveChanges problem in N-Tier Architecture

Normally, with MVC I use db.savechanges() method after I do some processes. But check the below code when I use N-Tier Architecture in everyloop its gonna insert in this way but I dont want it. I have to check all the items first. If there is no problem then I have to insert it all together.
foreach (var item in mOrderList)
{
MOrder mOrder = new MOrder();
mOrder.StatusAdmin = false;
mOrder.Date = DateTime.Now;
mOrder.StatusMVendor = "Sipariş alındı.";
mOrder.HowMany = item.HowMany;
mOrder.MBasketId = item.MBasketId;
mOrder.MProductId = item.MProductId;
mOrder.MVendorId = item.MVendorId;
mOrder.WInvestorId = item.WInvestorId;
MProduct mprostock = _imProductService.GetMProductById(item.MProductId);
if (mprostock.Stock<=0)
{
return ReturnErrorAndSuccess(HttpStatusCode.NotFound, "MProduct", mprostock.Name + " ürününde stok kalmadığı için işlem tamamlanamadı.");
}
_imOrderService.InsertMOrder(mOrder);
}
all you have to do is:
first you should define a method that get list of mProductId and then return list of MProduct.
after that you should check if there is any record with Stock<=0 then return your error.
-also for your insert you should define a method that get list of MOrder and return appropriate datatype for example Boolean.
public List<MProduct> GetMProductByIds(List<MProductId> mProductId)
{
//getting record code
}
public bool AddMOrder(List<MOrder> mOrder)
{
//inserting record code
}

How to get the entire list instead of only the first result? Net Core 3.1

The following functions return only the first row of the database, would you please guide me get the entire rows please?
My Function :
private List<Phrases_Clp> PhrasesStringToListPhrases_Clp(string phrases_codes)
{
List<Phrases_Clp> phrases_Clps = new List<Phrases_Clp>();
if (!String.IsNullOrEmpty(phrases_codes))
{
if (phrases_codes.Contains(","))
{
string[] listprhases = phrases_codes.Split(",");
foreach (var item in listprhases)
{
var mentiontoinsert = _context.Phrases_Clps.FirstOrDefault(m =>
m.Phrase_Numero == item);
phrases_Clps.Add(mentiontoinsert);
}
}
else
{
var mentiontoinsert = _context.Phrases_Clps.FirstOrDefault(m =>
m.Phrase_Numero == phrases_codes);
phrases_Clps.Add(mentiontoinsert);
}
}
return phrases_Clps;
}
UPDATE :
It still gives me the first result. I wonder if I should do changes in my view as I display information from another table which correspond the cell values in the current table.
Table Identification_Produit
Table Phrases Clp
Current Output:
Desired Output is to display 'Mentions Dangers' of each Product.
GET:
//GET: EvaluationRisque/Create
[Authorize(Roles = "Administrateur, Modificateur")]
public async Task<IActionResult> Create()
{
List<Models.dbo.equipements_protection.Equipement_Protection> listsequipement = _context.Equipement_Protections.ToList();
Create_Viewbags();
var model = new EvaluationRisquesViewModel();
var produitUtiliseByProduitId = await _context.Identification_Produit.FirstOrDefaultAsync();
model.MentionsDanger = produitUtiliseByProduitId;
model.List_Mentions_Danger = PhrasesStringToListPhrases_Clp(model.MentionsDanger.Mentions_Danger);
return View(model);
}
Bootstrap Model in the View Page:
<td>#foreach (var mention_danger in Model.List_Mentions_Danger)
{
<p> #mention_danger.Phrase_Numero : #mention_danger.Phrase_Libelle
</p>}
</td>
Change FirstOrDefault(...) to .Where(...).ToList() (or use phrases_Clps.AddRange( _context.Phrases_Clps.Where(...))).
But actually there is no need to make multiple requests to the database and you can skip conditional splitting, so something like following should work:
List<Phrases_Clp> phrases_Clps = null;
if (!string.IsNullOrEmpty(phrases_codes))
{
string[] listprhases = phrases_codes.Split(","); // will be array with one item if there are no commas in string
phrases_Clps = _context.Phrases_Clps
.Where(m => listprhases.Contains(m.Phrase_Numero))
.ToList();
}
return phrases_Clps ?? new List<Phrases_Clp>();
Because you are using FirstOrDefault function in this line:
var mentiontoinsert = _context.Phrases_Clps.FirstOrDefault(m => m.Phrase_Numero == item);
replace it with Where function.
Thinking that to utilize the query as IQueryable.
Updated:
Thanks for #GuruStron (Guru/Sensei)'s opinion and for pointing out the issue from my previous answer.
In short,
Exit the function with an empty List if phrases_codes is an empty string or null.
Working with IQueryable to prepare the different search criteria and defer the execution.
Once the query is prepared, execute the query via .ToList().
if (String.IsNullOrEmpty(phrases_codes))
return new List<Phrases_Clp>();
IQueryable<Phrases_Clp> query = _context.Phrases_Clps;
if (phrases_codes.Contains(","))
{
string[] listprhases = phrases_codes.Split(",");
query = query
.Where(x => listprhases.Contains(x.Phrase_Numero));
}
else
{
query = query
.Where(x => x.Phrase_Numero == phrases_codes);
}
return query.ToList();
This is my minimalist proposal
private List<Phrases_Clp> PhrasesStringToListPhrases_Clp(string phrases_codes)
{
var listprhases = phrases_codes != null ? phrases_codes.Split(",") : new string[0];
return _context.Phrases_Clps.Where(x => listprhases.Contains(x.Phrase_Numero)).ToList();
}

Duplicate record cause no record insert

Please consider this scenario:
I have two tables: Temp and Convert. The structure of these 2 tables are exactly same. I want to do some operation on each record of Temp and add it to Convert table. By doing my work, there may be duplicate records in Convert table and so I don't want to insert that record in Convert table. I wrote this code:
foreach (var item in allRecords)
{
var converted = new Convert()
{
F1 = item.F1,
F2 = item.F2,
F3 = DoWork(F3),
};
try
{
context.Convert.AddObject(converted);
context.SaveChanges();
}
catch (Exception ex)
{
var msg = "Violation of PRIMARY KEY constraint 'PK_Convert'."
+ " Cannot insert duplicate key":
if (ex.InnerException.Message.Contains(msg))
{
continue;
}
else
{
throw ex;
}
}
}
the problem is when I get exception for first duplicate and continue command executed, it seems that duplicate record not discard and still wait for save. After first exceptionو No record is stored in the database because of my first duplicate error. How I can solve this issue without checking existence of duplicate error in Convert table?
Thanks
You can try to check if the entity exists and if not - add it? Like this :
using System.Linq.Expressions;
public class ContextWithExtensionExample
{
public void DoSomeContextWork(DbContext context)
{
var uni = new Unicorn();
context.Set<Unicorn>().AddIfNotExists(uni , x => x.Name == "James");
}
}
public static class DbSetExtensions
{
public static T AddIfNotExists<T>(this DbSet<T> dbSet, T entity, Expression<Func<T, bool>> predicate = null) where T : class, new()
{
var exists = predicate != null ? dbSet.Any(predicate) : dbSet.Any();
return !exists ? dbSet.Add(entity) : null;
}
}
You can use this method directly and remember to call DbContext.SaveChanges() after the call.
var converted = new Convert()
{
F1 = item.F1,
F2 = item.F2,
F3 = DoWork(F3),
};
As F3 is your primarykey, then just check if it exists before inserting:
if(context.Convert.Any(x => x.F3 == converted.F3)
{
//deal with the error
}
else
{
context.Convert.AddObject(converted);
context.SaveChanges();
}

Why failed to add item on a session IEnumerable list

Work on entity framework vs2010,
I want to store somewhere some set of objects obtained from the database. Because I dont want to call DB after which user request. And I do it this way:
public IEnumerable<Message> Messages {
get { return HttpContext.Session[SESSION_MESSAGES_NAME] as IEnumerable<Message>; }
set { HttpContext.Session[SESSION_MESSAGES_NAME] = value; }
}
objEntity = new CmnItemSpecificationDetail();
objEntity.ItemSpecificationDetailRecordID = hidItemSpecificationDetailRecordID.Value.ToString() == "" ? _ItemSpecificationDetail.Count + 1 : int.Parse(hidItemSpecificationDetailRecordID.Value.ToString());
objEntity.SpecificationID = drpSpecification.SelectedIndex == 0 ? -1 : int.Parse(drpSpecification.SelectedValue);
objEntity.SpecValue = Convert.ToDecimal(txtSpecValue.Text);
objEntity.UOMID = drpUOM.SelectedIndex == 0 ? -1 : int.Parse(drpUOM.SelectedValue);
objEntity.UOMID = 1;
objEntity.Action = Entity.ActionMode.Add;
objEntity.Action = Entity.ActionMode.Add;
objEntity.CreateBy = LogInUser;
objEntity.CreateOn = DateTime.Now;
objEntity.CreatePc = PCName;
Messages.Append(objEntity);//failed to add item
public static class exten
{
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, params T[] items)
{
return source.Concat(items);
}
}
This list failed to fill up item,want to know why this can not add item on list,what is the problem,How to solve this problem.
If have any query please ask ,thanks in advanced.
You may want to modify your Getter a bit.
The way it currently works it doesn't handle situations where the Messages is null (hasn't been initialized).
Change this
get { return HttpContext.Session[SESSION_MESSAGES_NAME] as IEnumerable<Message>; }
To this.
get { var msg = HttpContext.Session[SESSION_MESSAGES_NAME] as IEnumerable<Message>;
if(msg == null) {
msg = new List<Message>();
HttpContext.Session[SESSION_MESSAGES_NAME] = msg;
}
return msg;
}
Your Append() extension method does NOT change the original sequence. It returns a new sequence with the specified items appended to it.
When you call Messages.Append(objEntity), you don't do anything with the return value, so you are throwing away the results of the Append().
To fix it, you will need to do something with the return value. Perhaps just do:
Messages = Messages.Append(objEntity);

LINQ to SQL - Updating Data Context Objects in Partial Classes

I have created an extensibility method for deleting one of my Linq To Sql objects called Reservation.
Well, in this partial method I created, I want to update some other objects. I can't seem to get the update to be persisted in the database. Here is my partial method for deleting the reservation.
public partial class LawEnforcementDataContext
{
partial void DeleteReservation(Reservation instance)
{
// Get ID's of those seated in course
var roster = new Roster(this, instance.CourseID);
var seated = from r in roster.All
where r.WaitingList == false
select r.ID;
// delete the reservation
this.ExecuteDynamicDelete(instance);
// get seated id's not in original seated ids
var newlySeated = from r in roster.All
where r.WaitingList == false && !seated.Contains(r.ID)
select r.ID;
var reservations = this.Reservations.Where(r => newlySeated.Contains(r.ID));
foreach (var r in reservations)
{
r.Confirmed = false;
// Have tried doing nothing, thinking calling code's db.SubmitChanges() would do the trick
//this.ExecuteDynamicUpdate(r); HAVE TRIED THIS
}
//this.SubmitChanges(); HAVE TRIED THIS
}
}
The delete is taking place but the update is not. Commented in the last few lines are some of the things I have tried.
Any ideas? Thanks!
EDIT
Here is what I have done to solve this:
public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
{
ChangeSet delta = GetChangeSet();
foreach (var res in delta.Deletes.OfType<Reservation>())
{
// Get ID's of those seated in course
var roster = new Roster(this, res.CourseID);
var seated = from r in roster.All
where r.WaitingList == false
select r.ID;
base.SubmitChanges(failureMode);
// get seated id's not in original seated ids
var newlySeated = from r in roster.All
where r.WaitingList == false && !seated.Contains(r.ID)
select r.ID;
var reservations = this.Reservations.Where(r => newlySeated.Contains(r.ID));
foreach (var r in reservations)
{
r.Confirmed = false;
}
}
base.SubmitChanges(failureMode);
}
I expect the problem here is that it has already called GetChangeSet().
I suggest you override SubmitChanges() at the data-context, and apply this logic there instead...
partial class LawEnforcementDataContext
{
public override void SubmitChanges(
System.Data.Linq.ConflictMode failureMode)
{
ChangeSet delta = GetChangeSet();
foreach (var reservation in delta.Deletes.OfType<Reservation>())
{
// etc
}
base.SubmitChanges(failureMode);
}
}
Here is an explanation and nice way to update objects in partial classes:
Implementing linqtosql partial DataContext class - how to inspect before/after values
I hope it helps.

Categories

Resources