Multiplicity constraint violated - Entity Framework - c#

I'm having an issue when trying to save some data. What I'm trying to do is, I have Orders and I have an Order that has already been fulfilled and Paid. However, the Order Type is wrong and needs changed but in doing this it may mean VAT doesn't get added to the Order so an adjustment needs to be calculated to either give money to the Customer or have the Customer pay. So my idea was, when an Order Type is changed on an existing Paid Order, I create a copy of the Order, so another row in the SQL database (Orders table) with the same details, create copies of the Items (Items table) that were in the Order. However, the Order Type in the Orders table will be different. However, when I try to do this I'm getting the error
multiplicity constraint violated the role '_ target of the relationship has multiplicity 1 or 0..1
What happens is, the user opens the Order on screen, the Order details are retrieved from the Session and stored...
public void Save()
{
App_Order form;
List<App_Items> itemsInSession;
...
try
{
form = (App_Order)Session["Form" + id];
itemsInSession = (List<App_Items>)Session["FormDrugItems" + id];
}
// some validation is done
if(orderTypeChanged == true)
{
App_Order duplicateOrder = new App_Order();
duplicateOrder = CreateDuplicateOrderForm(form);
_orderService.SaveForm(duplicateOrder);
}
}
..
public App_Order CreateDuplicateOrderForm(App_Order form)
{
App_Order newForm = new App_Order()
{
OrderType = form.orderType,
OrderDate = form.orderDate,
CustomerId = form.customerId,
}
newForm.App_Items = new List<App_Items>();
foreach(App_Items orderItem in form.App_Item)
{
App_Items item = new App_Items()
{
ItemCode = orderItem.itemCode,
Quantity = orderItem.quantity,
Cost = orderItem.cost
};
newForm.App_Items.Add(item);
}
}
The Multiplicity Constraint Violation mentions App_Customer has a multiplicity. It would be a CustomerId from App_Customer that is stored in App_Order table in column CustomerId.
Any suggestions on what I'm doing wrong or what I can do to fix it?
Thanks in advance :)
App_Order:
public partial class App_Order
{
[ExcludeFromCodeCoverage]
public App_Order()
{
this.App_PharmacyFormDrugItem = new HashSet<App_PharmacyFormDrugItem>();
}
public int OrderId { get; set; }
public short OrderTypeId { get; set; }
public short OrderDate { get; set; }
public int CustomerId { get; set; }
public Nullable<int> CustomerId_Initial { get; set; }
public virtual Ref_OrderType Ref_OrderType { get; set; }
public virtual Ref_PaymentMonth Ref_PaymentMonth { get; set; }
public virtual App_Customer App_Customer { get; set; }
public virtual App_Customer App_Customer_Initial { get; set; }
public virtual ICollection<App_Item> App_Item { get; set; }
}
App_Customer:
public partial class App_Contract
{
public App_Contract()
{
this.App_CustomerAddress = new HashSet<App_CustomerAddress>();
this.App_CustomerContact = new HashSet<App_CustomerContact>();
}
public int CustomerId { get; set; }
public short CustomerTypeId { get; set; }
public short CustomerSubTypeId { get; set; }
public string Name { get; set; }
public virtual ICollection<App_CustomerAddress> App_CustomerAddress { get; set; }
public virtual ICollection<App_CustomerContact> App_CustomerContact{ get; set; }
public virtual Ref_CustomertSubType Ref_CustomerSubType { get; set; }
public virtual Ref_CustomerType Ref_CustomerType { get; set; }
public virtual ICollection<App_Order> App_Order{ get; set; }
public virtual ICollection<App_Order> App_Order1 { get; set; }
public virtual ICollection<App_Order> App_Order2 { get; set; }
}

Related

EF core won't let me access the same property twice

I'm trying to store older versions of entities in my database. To do that I am copying the existing values before I update them. For some reason EF Core won't let me use the same batch.Values property twice.
public async Task<Batch> UpdateBatch(Batch batch, Batch updatedBatch)
{
foreach (var valueParameter in batch.Values)
{
batch.ValuesHistory.Add(new ParameterValueHistory
{
Parameter = valueParameter.Parameter,
ParameterBatchNumber = valueParameter.ParameterBatchNumber,
Value = valueParameter.Value
});
}
batch.Values = updatedBatch.Values;
batch.Version++;
await this.context.SaveChangesAsync();
return batch;
}
The foreach loop and batch.Values = updatedBatch.Values; work exactly like they should when only one of them exists. But whenever they're both active I get the following error:
The instance of entity type 'ParameterValue' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
These are the relevant models:
ParameterValue:
public class ParameterValue
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public virtual RecipeParameter Parameter { get; set; }
public string Value { get; set; }
public string? ParameterBatchNumber { get; set; }
}
ParameterValueHistory:
public class ParameterValueHistory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public virtual RecipeParameter Parameter { get; set; }
public string Value { get; set; }
public string? ParameterBatchNumber { get; set; }
}
RecipeParameter for context:
public class RecipeParameter
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Type { get; set; }
public string Unit { get; set; }
public string Value { get; set; }
public bool BatchRequired { get; set; }
}
Batch:
public class Batch
{
[Key]
[MaxLength(12)]
public string BatchNumber { get; set; }
public virtual List<ParameterValue> Values { get; set; }
public virtual List<ParameterValueHistory> ValuesHistory { get; set; }
public int Version { get; set; }
[Required]
public bool IsResearch { get; set; }
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedOn { get; set; } = DateTime.UtcNow;
}
This is my DbContext class:
public class ApplicationDataContext : DbContext
{
public ApplicationDataContext(DbContextOptions<ApplicationDataContext> options)
: base(options)
{
}
public DbSet<Product> Product { get; set; }
public DbSet<Batch> Batch { get; set; }
public DbSet<ParameterValue> ParameterValue { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLazyLoadingProxies();
base.OnConfiguring(optionsBuilder);
}
}
Why does this error keep showing up? Even when I am just accessing the propety as batch.Values more than once, it gives me this error.
UPDATE:
This is the controller method that calls the UpdateBatch method.
[HttpPut("{productId}/batches/{batchNumber}")]
public async Task<ActionResult<Batch>> PutBatch(string batchNumber, Batch updatedBatch)
{
Batch batch = await this.repository.GetBatchByBatchNumber(batchNumber);
if (batch == null)
{
return NotFound()
}
return await this.repository.UpdateBatch(batch, updatedBatch);
}
When you use batch.Values = updatedBatch.Values;, because batch.Values contains the foreign key of Batch, and if the value in updatedBatch.Values also contains the key value,if the equal operation is performed directly, due to the foreign key constraint, the foreign key cannot be modified directly, which will cause your error.
Therefore, you cannot include the key value in the Values in your updateBatch.
Regarding your question. I did a simple test. You can see the following code(updateBatch.Values have no Id).
var batch = _context.Batches.Include(c => c.Values)
.ThenInclude(c => c.Parameter)
.Include(b => b.ValuesHistory)
.ThenInclude(c => c.Parameter)
.Where(c => c.BatchNumber == "1")
.FirstOrDefault();
var updateBatch = new Batch
{
Version = 3,
CreatedOn = new DateTime(),
IsResearch = true,
Values = new List<ParameterValue>
{
new ParameterValue
{
Value = "hello",
Parameter = new RecipeParameter
{
BatchRequired = true,
Name = "h",
Type = "e",
Unit = "l",
Value = "o"
}
},
},
ValuesHistory = new List<ParameterValueHistory>()
};
foreach (var valueParameter in batch.Values)
{
batch.ValuesHistory.Add(new ParameterValueHistory
{
Parameter = valueParameter.Parameter,
ParameterBatchNumber = valueParameter.ParameterBatchNumber,
Value = valueParameter.Value
});
}
batch.Values = updateBatch.Values;
batch.Version++;
_context.SaveChanges();
Test result:
start by making these changes..
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
should not be on
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedOn { get; set; } = DateTime.UtcNow;
instead model like
public class Batch
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
//you can add index on this
[MaxLength(12)]
public string BatchNumber { get; set; }
public int Version { get; set; }
[Required]
public bool IsResearch { get; set; }
[Required]
public DateTime CreatedOn { get; set; };// set this in the repo or create do another way
//you add this but don't see the linkage aka ParameterValue does not have a BatchId
public virtual List<ParameterValue> Values { get; set; }
public virtual List<ParameterValueHistory> ValuesHistory { get; set; }
}

Cannot insert explicit value for identity column in 'DentalProcedures' when IDENTITY_INSERT is set to OFF. EF code first

I will try to keep this one short. Any help is welcome and appreciated!
I have 2 classes that have a many-to-many relationship and their composite key class. When I'm creating a new "appointment" I want to pick "dental procedures" that are in the system.
All works fine until I reach the AppointmentRepository where I try to save the newly created appointment. The error is as stated.
I tried to add the
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
or
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
annotations above the DentalProcedureId property in the DentalProcedure class, but nothing works. Saved the changes and dropped tables, deleted all of the migrations etc.
DentalProcedure class:
public class DentalProcedure
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int DentalProcedureId { get; set; }
[Required(ErrorMessage = "The name of the procedure must be specified")]
public string ProcedureName { get; set; }
[Required(ErrorMessage = "The price of the procedure must be specified")]
public decimal ProcedurePrice { get; set; }
public bool isEnabled { get; set; }
public List<CustomerProcedure> CustomerProcedures { get; set; }
public List<AppointmentProcedure> AppointmentProcedures { get; set; }
}
Appointment class:
public class Appointment
{
[Key]
public int AppointmentId { get; set; }
[Required]
public DateTime AppointmentStart { get; set; }
[Required]
public DateTime AppointmentEnd { get; set; }
[Required]
public string Title { get; set; }
public string ProcedureDescription { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public int WorkDaysId { get; set; }
public WorkDays WorkDays { get; set; }
public List<AppointmentProcedure> AppointmentProcedures { get; set; }
}
AppointmentProcedure class:
public class AppointmentProcedure
{
public int AppointmentId { get; set; }
public Appointment Appointment { get; set; }
public int DentalProcedureId { get; set; }
public DentalProcedure DentalProcedure { get; set; }
public bool ProcedureAppointmentCanceled { get; set; }
}
Home controller:
Appointment appointment = new Appointment
{
AppointmentStart = model.AppointmentStart,
AppointmentEnd = model.AppointmentEnd,
Title = model.Title,
ProcedureDescription = model.ProcedureDescription,
CustomerId = Id,
WorkDaysId = workkWeek.WorkDaysId,
};
foreach (var proc in model.DentalProcedures)
{
if (proc.isEnabled)
{
appointment.AppointmentProcedures = new List<AppointmentProcedure>
{
new AppointmentProcedure
{
Appointment = appointment,
DentalProcedure = proc,
ProcedureAppointmentCanceled = false
}
};
}
}
_appointment.CreateAppointment(appointment);
And the error :
Thanks once more in advance.
PS: I'm still learning so if I forgot to mention something, I apologize in advance!
Entity Framework needs to track the object in the database so you need to either attach or (what I usually prefer to do) load it from the database. Here's the changed code:
foreach (var proc in model.DentalProcedures)
{
if (proc.isEnabled)
{
//assuming the DBSet is called Procedures
var dbProc = await db.Procedures.FirstOrDefaultAsync(p => p.DentalProcedureId == id));
appointment.AppointmentProcedures = new List<AppointmentProcedure>
{
new AppointmentProcedure
{
Appointment = appointment,
DentalProcedure = dbProc, //now set the loaded entity
ProcedureAppointmentCanceled = false
}
};
}
}

How do I create a new Log for this team whilst creating the team?

This is my team model
public partial class TeamModel
{
public TeamModel()
{
this.Players = new HashSet<PlayerModel>();
}
public int TeamModelId { get; set; }
public string TeamName { get; set; }
public int SchoolId { get; set; }
public int DivisionId { get; set; }
public LogModel TeamLog { get; set; }
[ForeignKey("Under")]
public int UnderId { get; set; }
public virtual LogModel Log { get; set; }
public virtual SchoolModel School { get; set; }
public virtual DivisionModel Division { get; set; }
public virtual UnderModel Under { get; set; }
public virtual ICollection<PlayerModel> Players { get; set; }
This is my log model
public class LogModel
{
[Key]
[ForeignKey("Team")]
public int TeamId { get; set; }
public int Played { get; set; }
public int Win { get; set; }
public int Draw { get; set; }
public int Lost { get; set; }
public int GoalsFor { get; set; }
public int GoalsAgainst { get; set; }
public int GoalDifference { get; set; }
public int Points { get; set; }
[ForeignKey("Season")]
public int SeasonId { get; set; }
public virtual SeasonModel Season { get; set; }
public virtual TeamModel Team { get; set; }
}
and this is where I create a new team and also where I try to create new log for that specific team in the process.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "TeamModelId,TeamName,SchoolId,DivisionId,UnderId")] TeamModel teamModel)
{
teamModel.TeamLog = new LogModel();
teamModel.TeamLog.Draw = 0;
teamModel.TeamLog.GoalDifference = 0;
teamModel.TeamLog.GoalsAgainst = 0;
teamModel.TeamLog.GoalsFor = 0;
teamModel.TeamLog.Lost = 0;
teamModel.TeamLog.Played = 0;
teamModel.TeamLog.Points = 0;
teamModel.TeamLog.Win = 0;
teamModel.TeamLog.SeasonId = db.Seasons.ToList().Last().SeasonId;
db.Teams.Add(teamModel);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.DivisionId = new SelectList(db.Divisions, "DivisionId", "DivisionName", teamModel.DivisionId);
ViewBag.SchoolId = new SelectList(db.Schools, "SchoolId", "SchoolName", teamModel.SchoolId);
ViewBag.UnderId = new SelectList(db.Under, "UnderID", "UnderName", teamModel.UnderId);
return View(teamModel);
}
The error that i get when i try adding a new log to the team like this is
System.Data.Entity.Infrastructure.DbUpdateException: 'An error
occurred while saving entities that do not expose foreign key
properties for their relationships. The EntityEntries property will
return null because a single entity cannot be identified as the source
of the exception. Handling of exceptions while saving can be made
easier by exposing foreign key properties in your entity types. See
the InnerException for details.
and the inner exception is
UpdateException: Unable to determine a valid ordering for dependent
operations. Dependencies may exist due to foreign key constraints,
model requirements, or store-generated values.
I looked for a solution on the internet and I failed to find anything that worked. I think my problem is how I was trying to find the solution. I searched for the error but it didn't seem like the solutions solved the problem I was having here. I have spent a few days trying to create a Log when I create a team.

Accessing a single field in a foreach using Linq Dapper

I am using dapper and I want to use Linq to be able to update a single field called status in one table I am trying to use.
public async Task<Int32> ProcessUnprocessedTransactions(IEnumerable<BomComponentData> items)
{
IEnumerable<BomComponentData> _itemstoBeProcesed = items.Where(w => w.Status == (int)BomStatus.Scanned);
foreach (BomComponentData item in _itemstoBeProcesed)
{
item.Status = (int)BomStatus.Completed;
}
return await database.UpdateAsync(_itemstoBeProcesed);
}
My class is as follows:
public class BomComponentData
{
public int Sequence { get; set; }
public string BOMShortName { get; set; }
public string OperationName { get; set; }
public long BomId { get; set; }
public long BOMLineID { get; set; }
public long StockItemID { get; set; }
public string BomLineType { get; set; }
public int Quantity { get; set; }
public long UnitID { get; set; }
public decimal? MultipleOfBaseUnit { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
public long ProductGroupID { get; set; }
public string ProductGroupCode { get; set; }
public int Status { get; set; }
public int BinLocation { get; set; }
public string BinName { get; set; }
public string UOM { get; set; }
public int CalculatedValue { get; set; }
public int BomPickedCount { get; set; }
public int TotalLeftTopick
{
get { return Quantity - BomPickedCount; }
}
public enum BomStatus
{
Listed=1,
Scanned=2,
Confirmed=3,
Processed=4,
Completed=4,
InVisible=5
}
public override string ToString()
{
return Code;
}
}
But it does not work if I use a foreach like above. I am sure it should update the items properly but I think that because I'm going through singular items in my foreach and my list in the update it's not updating correct.
All I want to do is mark the items as completed and ready for transfer, I am doing so by the status column and an int enum.
Maybe I am missing a declaration of what is my primary key?
Edit 2
When I use a key declaration of the primary key I get the following:
Unhandled Exception: System.AggregateException: One or more errors occurred. (Constraint
Edit 3
I have set key of my class but as you see I have auotincrement on my db and it still crashes. How should insert be handled?
Edit 4
For example I am inserting into the database as follows. Shouldn't this work?
List<BomComponentData> _bomList = new List<BomComponentData>();
_bomList.Add(new BomComponentData { Sequence = 1, Barcode = "0000000001498", BinLocation = 23, BinName = "A", BOMShortName = "GYNE-TXX", OperationName = "Example Product", Code = "TB9175CEA", Name = "Tiburon Peri/Gynae Pack-10", Quantity = 1, UOM = "Each" });
await database.InsertAllAsync(_bomList,true);
I have placed the tag key for the update that works ok but when I attempt to do an insert with the key it doesn't it says constraint error but the update works. Does anybody no how i can solve both the insert and update in Dapper contrib.
You are using Dapper.Contrib and this extension requires that you decorate your class with some attributes to help in the automatic handling of your data.
In particular for an Update method you need to define the Table attribute and the Key attribute to identify the primary key
[Table ("BomComps")]
public class BomComponentData
{
// [ExplictKey]
[Key]
public int Sequence { get; set; }
....
Here, for example, I have added the attribute Table to set a possible table name on the database, but if the name of the physical table matches the name of the class then you can omit the attribute. Also I have added the Key attribute to set the property that contains the primary key of your table (so the statement that updates your records could be formed with the proper WHERE condition).
Notice that the Key attribute should be used if the column has an Identity property set to yes while, if not, you use the ExplicitKey attribute to signal that the primary key is defined by your code.
This was actually the issue I had to decoate my class with the following Leaving this here so that anyone else has issue I was using the pcl libary but for some reason dapper contribe did not detect the key element it had to be declared as follows.
[SQLite.PrimaryKey, SQLite.AutoIncrement]
public class StockTransferArgs
{
[SQLite.PrimaryKey, SQLite.AutoIncrement]
public int StockTransferArgsId { get; set; }
public long StockItemID { get; set; }
public string Code { get; set; }
public string OperationName { get; set; }
public string Description { get; set; }
public decimal Quantity { get; set; }
public long SourceWarehouseID { get; set; }
public string SourceBinName { get; set; }
public long TargetWarehouseID { get; set; }
public string TargetBinName { get; set; }
public string Reference { get; set; }
public string SecondReference { get; set; }
public string BarCode { get; set; }
public int Status { get; set; }
}

How to seed this entity model framework for complex relationship

I have the following scenario. We need to be able to fill forms for some tables, examples Companies (Empresa in Spanish), however we want the administrator to be able to extend the entity itself with additional fields.
I designed the following classes, and I need to seed at least one row, however its unclear to me how to seed one row of type CampoAdicional
Entity class:
public abstract class Entidad
{
[Key]
public int Id { get; set; }
}
Company Class (Empresas)
public class Empresa : Entidad
{
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public virtual ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
And the Additional Fields (Campo Adicional)
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
public virtual Entidad Entidad { get; set; }
}
However I dont know how to seed this class or table, because entity should be of subtype Company
Obviously the typeof doesnt compile
context.CampoAdicionals.Add(new CampoAdicional() { Entidad = typeof(Empresa), Id = 1, NombreCampo = "TwitterHandle", TipoCampo = Tiposcampo.TextoUnaLinea });
Update 1: Please note that the additional fields are for the entire entity company not for each company.
Unfortunately, I don't think you'll be able to use EF to automatically create that kind of relationship. You might be able to do something similar with special getters and such:
public class Entidad
{
// stuff...
public IEnumerable<CampoAdicional> CamposAdicionales
{
get { return CampoAdicional.GetAll(this); }
}
}
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public virtual Tiposcampo TipoCampo { get; set; }
protected string EntidadType { get; set; }
// You will need some mapping between Type and the EntidadType string
// that will be stored in the database.
// Maybe Type.FullName and Type.GetType(string)?
protected Type MapEntidadTypeToType();
protected string MapTypeToEntidadType(Type t);
[NotMapped]
public Type
{
get { return MapEntidadTypeToType(); }
// maybe also check that Entidad.IsAssignableFrom(value) == true
set { EntidadType = MapTypeToEntidadType(value); }
}
public static IEnumerable<CampoAdicional> GetAll(Entidad ent)
{
return context.CampoAdicionals
.Where(a => a.EntidadType == MapTypeToEntidadType(ent.GetType()));
}
}

Categories

Resources