I have table Player and table Statistic and other tables that are not important in this question.
Table Player has PK Player_ID and it is FK in table Statistic. The relationship between these tables is one-to-many (one player can have more statistics).
Here is the code:
GenericRepository(I have created it to have unique class for CRUD methods)
public async Task<int> Delete<T>(Guid id) where T : class
{
try
{
T entity = await Get<T>(id);
Context.Set<T>().Remove(entity);
return await Context.SaveChangesAsync();
}
catch (Exception ex)
{
throw ex;
}
}
PlayerRepository(for managing operations on Player table)
public async Task<int> Delete(Guid id)
{
try
{
var player = await GenRepository.Get<Player>(id);
if (player == null)
{
return 404;
}
else
{
return await GenRepository.Delete(player);
}
}
catch (Exception ex)
{
throw ex;
}
}
PlayerService(connection between repository and controller in WebAPI)
public async Task<int> Delete(Guid id)
{
try
{
return await PlayerRepository.Delete(id);
}
catch (Exception ex)
{
throw ex;
}
}
PlayerController
[HttpDelete]
[Route("deleteplayer")]
public async Task<HttpResponseMessage> Delete(Guid id)
{
try
{
var Finder = Mapper.Map<PlayerView>(await PlayerService.Get(id));
if(Finder == null)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Player doesn't exist in database.");
}
var Response = await PlayerService.Delete(id);
var profile = "../../../app/pictures/FootballFanAppPictures/" + Finder.Club_ID.ToString().ToUpper() + "/profiles/" + id.ToString().ToUpper() + ".png";
var details = "../../../app/pictures/FootballFanAppPictures/" + Finder.Club_ID.ToString().ToUpper() + "/" + id.ToString().ToUpper() + ".png";
if (System.IO.File.Exists(profile))
{
System.IO.File.Delete(profile);
}
if (System.IO.File.Exists(details))
{
System.IO.File.Delete(details);
}
return Request.CreateResponse(HttpStatusCode.OK, Response);
}
catch(Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
Entity models:
-database models
public partial class Player
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Player()
{
this.Statistic = new HashSet<Statistic>();
}
public System.Guid Player_ID { get; set; }
public System.Guid Club_ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public double Height { get; set; }
public int Weight { get; set; }
public System.DateTime BirthDate { get; set; }
public string Nationality { get; set; }
public string Position { get; set; }
public int Shirtnmbr { get; set; }
public virtual Club Club { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Statistic> Statistic { get; set; }
}
using System;
using System.Collections.Generic;
public partial class Statistic
{
public System.Guid Statistic_ID { get; set; }
public System.Guid Player_ID { get; set; }
public int Goals { get; set; }
public int Assists { get; set; }
public int FoulsFor { get; set; }
public int FoulsAgainst { get; set; }
public int ShotsTotal { get; set; }
public int ShotsGoal { get; set; }
public virtual Player Player { get; set; }
}
-domain models (used in repository)
public class PlayerDomain : IPlayerDomain
{
public Guid Player_ID { get; set; }
public Guid Club_ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public double Height { get; set; }
public int Weight { get; set; }
public DateTime BirthDate { get; set; }
public string Nationality { get; set; }
public string Position { get; set; }
public int Shirtnmbr { get; set; }
public virtual ICollection<IStatisticDomain> Statistic { get; set; }
}
public class StatisticDomain: IStatisticDomain
{
public Guid Statistic_ID { get; set; }
public Guid Player_ID { get; set; }
public int Goals { get; set; }
public int Assists { get; set; }
public int FoulsFor { get; set; }
public int FoulsAgainst { get; set; }
public int ShotsTotal { get; set; }
public int ShotsGoal { get; set; }
}
-view models (used in controller)
public class PlayerView
{
public Guid Player_ID { get; set; }
public Guid Club_ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public double Height { get; set; }
public int Weight { get; set; }
public DateTime BirthDate { get; set; }
public string Nationality { get; set; }
public string Position { get; set; }
public int Shirtnmbr { get; set; }
public virtual ICollection<StatisticView> Statistic { get; set; }
}
public class StatisticView
{
public Guid Statistic_ID { get; set; }
public Guid Player_ID { get; set; }
public int Goals { get; set; }
public int Assists { get; set; }
public int FoulsFor { get; set; }
public int FoulsAgainst { get; set; }
public int ShotsTotal { get; set; }
public int ShotsGoal { get; set; }
}
Every class is in a separate file. I use database first approach so i got .edmx file along with database models. Database is created in SQL Server Management Studio.
I can update Player but when I try to delete it i get this error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
I have searched various answers on google and stackoverflow but I couldn't find an answer that solves my problem
Before you call this line:
var Response = await PlayerService.Delete(id);
You are going to need to retrieve a list of your statistics that are assigned to the player you are trying to delete.
Then loop trough each of the statistics and delete those from your database first.
var stats = Finder.Statistic.ToList();
if(stats != null && stats.Any())
{
foreach(var stat in stats)
{
//retrieve your stat record here from the database
//check that the stat record is not null
//delete your stat record here
}
}
Then you should be able to delete your player record as there will no longer be references to it.
OR
You could just set ON DELETE CASCADE to true, but you need to be careful that you fully understand what all will be deleted on player deletion.
I don't see Statistic deletion on Player delete in your code.
Before you can delete a Player, you need to delete all Player related Statistics first. If there is any left, a player will fail on delete operation.
Set PrimaryKey and Foreignkey Relationship :according to #Bad Dub suggestion.
Related
How do I delete the order without getting this exception?
UserLicenses references SerialOnOrderDetails and vice-versa:
The DELETE statement conflicted with the REFERENCE constraint "FK_SerialsOnOrderDetail_UserLicenses". The conflict occurred in database "sales", table "dbo.SerialsOnOrderDetail", column 'UserLicenseId'.
Delete confirmed controller action code:
[Authorize(Roles = "admin")]
[HttpPost, ActionName("Delete")]
public async Task<ActionResult> DeleteConfirmed(int id)
{
Order order = GetOrderById(id);
if (order.UserLicenses.Count > 0)
{
context.UserLicenses.RemoveRange(order.UserLicenses);
}
if (order.SerialsOnOrderDetails.Count > 0)
{
context.SerialsOnOrderDetails.RemoveRange(order.SerialsOnOrderDetails);
}
context.Orders.Remove(order);
context.SaveChanges(); // Exception here !!!
}
[EDIT] Added live data
Live data (Id = UserLicenseId):
Additional classes:
public partial class UserLicense
{
public string Id { get; set; }
public int OrderId { get; set; }
public string ProductId { get; set; }
public int CustomerId { get; set; }
public string AssignedUserId { get; set; }
public bool IsActive { get; set; }
public virtual AspNetUser AspNetUser { get; set; }
public virtual Customer Customer { get; set; }
public virtual Order Order { get; set; }
public virtual Product Product { get; set; }
public virtual ICollection<SerialsOnOrderDetail> SerialsOnOrderDetails { get; set; }
}
public partial class SerialsOnOrderDetail
{
public int orderID { get; set; }
public string serial { get; set; }
public string productID { get; set; }
public string UserLicenseId { get; set; }
public int customerID { get; set; }
public virtual Product Product { get; set; }
public virtual Serial Serial1 { get; set; }
public virtual Order Order { get; set; }
public virtual UserLicense UserLicense { get; set; }
public virtual Customer Customer { get; set; }
}
public partial class Order
{
public Order()
{
this.OrderDetails = new HashSet<OrderDetail>();
this.SerialsOnOrderDetails = new HashSet<SerialsOnOrderDetail>();
this.UserLicenses = new HashSet<UserLicense>();
}
public int orderID { get; set; }
public int customerID { get; set; }
public string promoCodeID { get; set; }
public System.DateTime date { get; set; }
public Nullable<int> resellerID { get; set; }
public string invoiceID { get; set; }
public string poNumber { get; set; }
public Nullable<System.DateTime> paymentDate { get; set; }
public Nullable<bool> validated { get; set; }
public string resellerOrderID { get; set; }
public Nullable<int> parentOrderID { get; set; }
public int months { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
public virtual PromoCode PromoCode { get; set; }
public virtual Reseller Reseller { get; set; }
public virtual ICollection<SerialsOnOrderDetail> SerialsOnOrderDetails { get; set; }
public virtual Order ParentOrder { get; set; }
public virtual ICollection<UserLicense> UserLicenses { get; set; }
}
Did you verify if UserLicenses and SerialsOnOrderDetails collections are properly loaded and not empty ? Are you sure removerange is the proper way to do it ? I suggest you to read a few tutorials about EF if you are not used to it.
Maybe you'll have to update GetOrderById with .Include("....") directives to load these collections, or load the related items manually.
That constraint message you quote is from the underlying SQL Server. It's not an EF error as such, it's passed through.
When I get this sort of problem in TSQL the solution is generally to delete both of the mutually referencing rows in a single transaction.
While I'm not terribly familiar with EF, a quick search turn up this example of putting multiple operations into a single transactional context instead of the default behaviour of a single transaction per operation.
{
context.Database.Log = Console.WriteLine;
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
Author author1 = new Author() { Name = "Mark" };
Author author2 = new Author() { Name = "John" };
context.Authors.Add(author1);
context.SaveChanges();
context.Authors.Add(author2);
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
}
}
}
I notice another respondent describes other things that could be wrong. It is entirely possible that we are both right and there are multiple issues to resolve.
Basically when calling Put method from controller and trying to update properties im getting conflict from Db.
Using repository pattern with unit of work.
Car model -
public class Car : BaseModel
{
public Guid CategoryId { get; set; }
public Category Category { get; set; }
public Guid ManufacturerId { get; set; }
public Manufacturer Manufacturer { get; set; }
public Guid ManufacturerModelId { get; set; }
public ManufacturerModel ManufacturerModel { get; set; }
public DateTime ManufactureDate { get; set; }
public string Engine { get; set; }
public Guid FuelTypeId { get; set; }
public FuelType FuelType { get; set; }
public Guid FrameTypeId { get; set; }
public FrameType FrameType { get; set; }
public Guid ColorId { get; set; }
public Color Color { get; set; }
public Guid TransmissionId { get; set; }
public Transmission Transmission { get; set; }
public Guid DefectsId { get; set; }
public Defects Defects { get; set; }
public Guid SteeringWheelPosId { get; set; }
public SteeringWheelPos SteeringWheelPos { get; set; }
public Doors Doors { get; set; }
public int Seats { get; set; }
public DateTime VehicleInspection { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
}
BaseModel class
public class BaseModel : IBaseModel
{
public BaseModel()
{
Id = Guid.NewGuid();
CreatedOn = DateTime.Now;
}
[Key]
public Guid Id { get; set; }
public DateTime CreatedOn { get; set; }
}
put method (im using cqrs here, and controller already passes carDto to method)
public async Task<Unit> Handle(Command request, CancellationToken cancellationToken)
{
if(request.obj == null)
{
throw new StatusCodeException(HttpStatusCode.BadRequest, "Object is empty");
}
var DbEntry = uow.carRepository.GetById(request.Id);
if (DbEntry == null)
{
throw new StatusCodeException(HttpStatusCode.NotFound, "was not found in database", request.Id);
}
request.obj.Id = request.Id;
var s = uow.Mapper.Map(request.obj, DbEntry);
uow.carRepository.Update(s);
uow.Commit();
return Unit.Value;
}
//Update method in generic repository class
public void Update(T obj)
{
context.Attach(obj);
context.Entry(obj).State = EntityState.Modified;
}
I can't find where i do something wrong. Checked with breakpoints, data is correct everywhere. getting error when calling uow to commit changes. Thank you. Looking forward.
The reason why this error happens is because, your foreign key 'DefectId' in 'Car' probably has value 0.
When entity framework core encounters value 0 for foreign key, it throws this kind of error, because it cannot insert foreign value 0 which is the primary key of some object. Obviously, primary keys cannot be value 0.
Hope, this answers to your question.
I'm bulding an application and when I want to insert a form into my form table I get the following error:
Cannot insert explicit value for identity column in table 'Relation'
when IDENTITY_INSERT is set to OFF.
These are my models:
Form model:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("FormType")]
public int? TypeId { get; set; }
public virtual FormType Type { get; set; }
[ForeignKey("FormStatusType")]
public int? StatusTypeId { get; set; }
public virtual FormStatusType StatusTknype { get; set; }
[ForeignKey("Relation")]
public int? SupplierId { get; set; }
public virtual Relation Supplier { get; set; }
[ForeignKey("Relation")]
public int? CustomerId { get; set; }
public virtual Relation Customer { get; set; }
public String SupplierReference { get; set; }
public Guid ApiId { get; set; }
public DateTime DueDate { get; set; }
public FormFile FormFiles { get; set; }
public String FormName { get; set; }
public DateTime UploadDate { get; set; }
Relation model:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("FormType")]
public int? TypeId { get; set; }
public virtual FormType Type { get; set; }
[ForeignKey("FormStatusType")]
public int? StatusTypeId { get; set; }
public virtual FormStatusType StatusTknype { get; set; }
[ForeignKey("Relation")]
public int? SupplierId { get; set; }
public virtual Relation Supplier { get; set; }
[ForeignKey("Relation")]
public int? CustomerId { get; set; }
public virtual Relation Customer { get; set; }
public String SupplierReference { get; set; }
public Guid ApiId { get; set; }
public DateTime DueDate { get; set; }
public FormFile FormFiles { get; set; }
public String FormName { get; set; }
public DateTime UploadDate { get; set; }
My context looks like this:
public class DataContext: DbContext
{
public DataContext(DbContextOptions<DataContext> options): base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer();
}
public DbSet<Relation> Relation { get; set; }
public DbSet<Setting> Settings { get; set; }
public DbSet<Notification> Notification { get; set; }
public DbSet<FormStatusType> FormStatusType { get; set; }
public DbSet<File> File { get; set; }
public DbSet<FormFile> FormFile { get; set; }
public DbSet<FormType> FormType { get; set; }
public DbSet<Form> Form { get; set; }
public DbSet<User> User { get; set; }
public DbSet<RelationUser> RelationUser { get; set; }
public DbSet<SupplierCustomer> SupplierCustomer { get; set; }
}
The method I use to add a form looks like this:
public async Task<Form> AddForm(Form form, int currentUserId)
{
try
{
if (form != null)
{
//huidige gebruiker als supplier aanduiden
Relation r = await GetCurrentUser(currentUserId);
form.Supplier = r;
form.SupplierId = r.Id;
//form aan de db toevoegen
_datacontext.Form.Add(form);
_datacontext.SaveChanges();
return form;
}
else
{
return null;
}
}
catch (Exception e)
{
LogError(e);
return null;
}
}
The get current user method
private async Task<Relation> GetCurrentUser(int currentUserId)
{
var relation = from r in _datacontext.RelationUser
where r.UserId == currentUserId
select r.Relation;
return await relation.FirstOrDefaultAsync();
}
This is where I call the AddForm method:
[HttpPost]
[Route("addform")]
[Authorize]
// api/form/addform
public async Task<IActionResult> AddForm([FromBody] Form form)
{
if (ModelState.IsValid)
{
Form f = await _formRepository.AddForm(form, GetUserIdFromToken());
if(f != null)
{
QueueObject qo = new QueueObject()
{
ActionTypeId = 1,
FormId = f.Id
};
await new QueueHandler().SendMessageToQueue(qo);
}
return Ok(f);
}
else
{
return NotFound("model is niet geldig");
}
}
I already searched but found nothing that solved the problem
Another possible reason this may happen, is if you have a timeout in some call to SaveChanges when trying to insert new entities to your database, then try calling SaveChanges again, using the same DbContext instance.
This is reproducible:
using(var context = new MyDbContext())
{
context.People.Add(new Person("John"));
try
{
// using SSMS, manually start a transaction in your db to force a timeout
context.SaveChanges();
}
catch(Exception)
{
// catch the time out exception
}
// stop the transaction in SSMS
context.People.Add(new Person("Mike"));
context.SaveChanges(); // this would cause the exception
}
This last SaveChanges would cause Cannot insert explicit value for identity column in table 'People' when IDENTITY_INSERT is set to OFF.
You have multiple errors on your model. The ForeignKey attribute must point to properties in the class, not to the type of the dependent entity:
//FORM MODEL
[ForeignKey("Type")]
public int? TypeId { get; set; }
public virtual FormType Type { get; set; }
[ForeignKey("StatusTknype")]
public int? StatusTypeId { get; set; }
public virtual FormStatusType StatusTknype { get; set; }
[ForeignKey("Supplier")]
public int? SupplierId { get; set; }
public virtual Relation Supplier { get; set; }
[ForeignKey("Customer")]
public int? CustomerId { get; set; }
public virtual Relation Customer { get; set; }
//RELATION MODEL
[ForeignKey("Type")]
public int? TypeId { get; set; }
public virtual FormType Type { get; set; }
[ForeignKey("StatusTknype")]
public int? StatusTypeId { get; set; }
public virtual FormStatusType StatusTknype { get; set; }
[ForeignKey("Relation")]
public int? SupplierId { get; set; }
public virtual Relation Supplier { get; set; }
[ForeignKey("Customer")]
public int? CustomerId { get; set; }
public virtual Relation Customer { get; set; }
Also, if you followed Convention Over Configuration, you could drop the ForeignKeyAttribute completely by just naming the properties conventionally:
public int? StatusTypeId { get; set; }
public virtual FormStatusType StatusType { get; set; }
Pretty new to ASP.NET and programming. I have two models, two API controllers, two repositories. How do I post the data to the second model while attaching it to the first (I'm guessing by ID.) Do I possibly need a View Model? Also reading a little about unit of work. Maybe neither are necessary? Below is some code. Thanks!
Record.cs
namespace Train.Models {
public class Record {
public int Id { get; set; }
public int Quantity { get; set; }
public DateTime DateCreated { get; set; }
public bool IsActive { get; set; }
public string UserId { get; set; }
public virtual ICollection<Cars> Cars { get; set; }
}
}
Cars.cs
namespace Train.Models {
public class Cars {
public int Id { get; set; }
public string EmptyOrLoaded { get; set; }
public string CarType { get; set; }
//Hopper, flatbed, tank, gondola, etc.
public string ShippedBy { get; set; }
//UP(Union Pacific) or BNSF
public string RailcarNumber { get; set; }
//public virtual ApplicationUser ApplicationUser { get; set; }
public string UserId { get; set; }
public string RecordId { get; set; }
public virtual Record Record { get; set; }
}
}
Record Repository
public void SaveRecord(Record recordToSave) {
if (recordToSave.Id == 0) {
recordToSave.DateCreated = DateTime.Now;
_db.Record.Add(recordToSave);
_db.SaveChanges();
} else {
var original = this._db.Record.Find(recordToSave.Id);
original.Quantity = recordToSave.Quantity;
original.IsActive = true;
_db.SaveChanges();
}
}
EFRepository (Cars)
public void SaveCar(Cars carToSave) {
if (carToSave.Id == 0) {
_db.Cars.Add(carToSave);
_db.SaveChanges();
} else {
var original = this.Find(carToSave.Id);
original.EmptyOrLoaded = carToSave.EmptyOrLoaded;
original.CarType = carToSave.CarType;
original.ShippedBy = carToSave.ShippedBy;
original.RailcarNumber = carToSave.RailcarNumber;
_db.SaveChanges();
}
}
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.
I'm using EF Code first and I'm getting error above when I trying do this:
public ActionResult CreateTransaction(TransactionViewModel model)
{
try
{
if (model.MemberId != 0 && model.SubcategoryId != 0 & model.CategoryId != 0)
{
Transaction t = new Transaction();
t.Amount = model.Amount;
t.Comment = model.Comment;
t.Date = DateTime.Now;
t.TransactionSubcategory = db.Subcategories.Find(model.SubcategoryId);//and i have error in this line
//i tried also this code below but it's the same error
//db.Subcategories.Find(model.SubcategoryId).Transactions.Add(t);
db.Members.Find(model.MemberId).Transactions.Add(t);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
return RedirectToAction("CreateTransaction") ;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return RedirectToAction("CreateTransaction");
}
}
And there is my model
public class Subcategory
{
public Subcategory()
{
IsGlobal = false;
Transactions = new List<Transaction>();
}
public int Id { get; set; }
public string Name { get; set; }
public TransactionType TypeOfTransaction { get; set; }
public Category OwnerCategory { get; set; }
public List<Transaction> Transactions { get; set; }
public bool IsGlobal { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public Decimal Amount { get; set; }
public DateTime Date { get; set; }
public string Comment { get; set; }
public Subcategory TransactionSubcategory { get; set; }
public Member OwnerMember { get; set; }
}
I don't know why thats happen because in database in Transaction table i see there is column with FK.
If you need there is rest of the model
public class Budget
{
public Budget()
{
Members = new List<Member>();
}
public int Id { get; set; }
public string BudgetName { get; set; }
public string OwnerName { get; set; }
public DateTime CreatedTime { get; set; }
public List<Member> Members { get; set; }
}
public class Category
{
public Category()
{
IsGlobal = false;
}
public int Id { get; set; }
public string Name { get; set; }
public List<Subcategory> Subcategories { get; set; }
public Budget OwnerBudget { get; set; }
public bool IsGlobal { get; set; }
}
public class Member
{
public Member()
{
Transactions = new List<Transaction>();
}
public Budget OwnerBudget { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedTime { get; set; }
public List<Transaction> Transactions { get; set; }
}
public enum TransactionType
{
Income,
Expenditure
}
Try adding
public int TransactionSubcategoryId { get; set; }
public int OwnerMemberId { get; set; }
In your Transaction class.