what's up? I have an API made with .NET Core API + EF where I have a Many-to-Many relationship between Doctor and Specialty, from which a DoctorSpecialities table arises. That is, a doctor can have several specialties.
I use React JS and what I want to do is have in the interface, a table with the specialties of the chosen doctor and when I send the form, the data in the DoctorSpecialities table is updated or deleted.
If the user deleted or added specialties, can all that be done in a single query?
My question is how to do the controller method, with that I have problems.
Doctor
public partial class Doctor
{
public Doctor()
{
DoctorSpecialties = new HashSet<DoctorSpecialties>();
}
public int DoctorId { get; set; }
public string DoctorName { get; set; }
public string DoctorProfileImg { get; set; }
public string DoctorPhoneNumber { get; set; }
public string DoctorEmail { get; set; }
public string DoctorStatus { get; set; }
public string DoctorGender { get; set; }
public ICollection<DoctorSpecialties> DoctorSpecialties { get; set; }
}
Specialty
public partial class Specialty
{
public Specialty()
{
DoctorSpecialties = new HashSet<DoctorSpecialties>();
}
public int SpecialtyId { get; set; }
public string SpecialtyName { get; set; }
public ICollection<DoctorSpecialties> DoctorSpecialties { get; set; }
}
DoctorSpecialties
public partial class DoctorSpecialties
{
public DoctorSpecialties()
{
Appointment = new HashSet<Appointment>();
DoctorSchedule = new HashSet<DoctorSchedule>();
}
public int DoctorSpecialtiesId { get; set; }
public int? DoctorId { get; set; }
public int? SpecialtyId { get; set; }
[JsonIgnore]
public Doctor Doctor { get; set; }
public Specialty Specialty { get; set; }
public ICollection<Appointment> Appointment { get; set; }
public ICollection<DoctorSchedule> DoctorSchedule { get; set; }
}
Context
modelBuilder.Entity<DoctorSpecialties>(entity =>
{
entity.ToTable("doctor_specialties");
entity.Property(e => e.DoctorSpecialtiesId).HasColumnName("doctor_specialties_id");
entity.Property(e => e.DoctorId).HasColumnName("doctor_id");
entity.Property(e => e.SpecialtyId).HasColumnName("specialty_id");
entity.HasOne(d => d.Doctor)
.WithMany(p => p.DoctorSpecialties)
.HasForeignKey(d => d.DoctorId)
.HasConstraintName("FK__doctor_sp__docto__2A4B4B5E");
entity.HasOne(d => d.Specialty)
.WithMany(p => p.DoctorSpecialties)
.HasForeignKey(d => d.SpecialtyId)
.HasConstraintName("FK__doctor_sp__speci__29572725");
});
I tried with:
[HttpPut("test/{id}")]
public async Task<IActionResult> Edit(int id, Doctor doctor)
{
if (id != doctor.DoctorId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
Doctor plantToBeUpdated = await _context.Doctor.Include(p => p.DoctorSpecialties).FirstOrDefaultAsync(p => p.DoctorId == id);
if (plantToBeUpdated != null)
{
plantToBeUpdated.DoctorSpecialties.Clear();
if (doctor.DoctorSpecialties.Count > 0)
{
foreach (var scId in doctor.DoctorSpecialties)
{
plantToBeUpdated.DoctorSpecialties.Add(new DoctorSpecialties()
{
DoctorId = plantToBeUpdated.DoctorId,
SpecialtyId = scId.SpecialtyId
});
}
}
plantToBeUpdated.DoctorName = doctor.DoctorName;
_context.Doctor.Update(plantToBeUpdated);
await _context.SaveChangesAsync();
}
}
catch (DbUpdateConcurrencyException)
{
throw;
}
}
return Ok(doctor);
}
but, doesn't work :c
Related
I am working on blazor server. I have the following class structure for my project.
I have for this error three classes.
ApplicationUser class:
public class ApplicationUser
{
public Guid ApplicationUserID { get; set; }
[Required (ErrorMessage = "Name is required")]
public string Name { get; set; } = null!;
[Required(ErrorMessage = "Surname is required")]
public string Surname { get; set; } = null!;
[Required(ErrorMessage = "Email is required")]
public string Email { get; set; } = null!;
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; } = null!;
[Required(ErrorMessage = "Salary pay date is required")]
public DateTime? SalaryPaymentDate { get; set; }
[Required(ErrorMessage = "Salary amount is required")]
public Decimal? SalaryAmount { get; set; }
[Required (ErrorMessage = "Currency is required")]
public Guid? CurrencyID { get; set; }
public Currency? Currency { get; set; }
public List<CreditCard>? CreditCards { get; set;}
public List<Saving>? Savings { get; set;}
public List<Expense>? Expenses { get; set;}
}
Currency class:
public class Currency
{
public Guid CurrencyID { get; set; }
public string CurrencyName { get; set; } = null!;
public string CurrencyAbbreviation { get; set; } = null!;
public string? CurrencySymbol { get; set; }
public List<ApplicationUser>? ApplicationUsers { get; set; }
}
I seeded the currency class with three values namely:
- USD
- ZAR
- Euro
`Savings` class:
public class Saving
{
public Guid SavingID { get; set; }
public List<Goal>? Goals { get; set; }
public Guid? ApplicationUserID { get; set; }
public ApplicationUser? ApplicationUser { get; set; }
}
So what I am trying to do is when you open the savings view for the first time and the Get method returns 0 values I create a new SavingObject.
In this SavingObject, I assign the CurrentUser which is passed as a cascading parameter and it does have a value.
Then I run my AddMethod to create the saving object. Here is the method:
[CascadingParameter]
public ApplicationUser? CurrentUser { get; set; }
protected override async Task OnInitializedAsync()
{
Savings = await savingsservice.Get();
if (Savings?.Count > 0)
SavingObject = Savings.FirstOrDefault();
else
{
SavingObject = new Saving();
SavingObject.ApplicationUser = CurrentUser!;
SavingObject.ApplicationUserID = CurrentUser!.ApplicationUserID;
await savingsservice.Add(SavingObject);
DataChanged();
}
Goals = await goalsservice.Get();
}
But it then crashes with the following message once the add method is run:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_Currencies'. Cannot insert duplicate key in object 'dbo.Currencies'. The duplicate key value is (c80b1fe5-7d94-4fc5-8328-706db7f29a18).
Violation of PRIMARY KEY constraint 'PK_ApplicationUsers'. Cannot insert duplicate key in object 'dbo.ApplicationUsers'. The duplicate key value is (1d03c731-3034-498b-fef5-08db0eaad3f6).
I understand that it states cannot insert duplicate key for the user and currency but why does it want to insert a duplicate key I don't understand because I am assigning the user so it already has the ID and the CurrencyID?
Also my Savings table is empty and I only have one applicationUser in my table which has one Currency.
Can someone please assist as I can't find any answer online.
I have googled the problem but I can't find my specific problem online therefore I am creating a question.
I have also setup the dbcontext as I thought this might have been the problem.
modelBuilder.Entity<ApplicationUser>()
.HasMany(a => a.Savings)
.WithOne(a => a.ApplicationUser)
.HasForeignKey(a => a.ApplicationUserID)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationUser>()
.HasMany(a => a.CreditCards)
.WithOne(a => a.ApplicationUser)
.HasForeignKey(a => a.ApplicationUserID)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ApplicationUser>()
.HasMany(a => a.Expenses)
.WithOne(a => a.ApplicationUser)
.HasForeignKey(a => a.ApplicationUserID)
.OnDelete(DeleteBehavior.Cascade);
Here is my Savings.cs service:
namespace MoneyTracker.Services
{
public interface ISavingsService
{
Task<List<Saving>> Get();
Task<Saving> Get(Guid id);
Task<Saving> Add(Saving Saving);
Task<Saving> Update(Saving Saving);
Task<Saving> Delete(Guid id);
void DetachEntity(Saving Saving);
}
public class SavingsService : ISavingsService
{
private readonly ApplicationDbContext _context;
public SavingsService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Saving>> Get()
{
return await _context.Savings
.Include(a => a.Goals)
.AsSplitQuery()
.ToListAsync();
}
public async Task<Saving> Get(Guid id)
{
var Saving = await _context.Savings.FindAsync(id);
return Saving;
}
public async Task<Saving> Add(Saving Saving)
{
_context.Savings.Add(Saving);
await _context.SaveChangesAsync();
return Saving;
}
public async Task<Saving> Update(Saving Saving)
{
_context.Entry(Saving).State = EntityState.Modified;
await _context.SaveChangesAsync();
return Saving;
}
public async Task<Saving> Delete(Guid id)
{
var Saving = await _context.Savings.FindAsync(id);
_context.Savings.Remove(Saving);
await _context.SaveChangesAsync();
return Saving;
}
public void DetachEntity(Saving Saving)
{
_context.Entry(Saving).State = EntityState.Detached;
}
}
}
I get this error in my .NET Core 3.1 app:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_DiaryDiaryEntry". The conflict occurred in database "xxxxxx", table "dbo.Diaries", column 'Id'.
I can't see anything wrong with the tables themselves.
public partial class Diaries
{
public long Id { get; set; }
public string CoverImage { get; set; }
public short Year { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public long ChildId { get; set; }
public Children Child { get; set; }
public ICollection<DiaryEntries> DiaryEntries { get; set; }
}
public partial class DiaryEntries
{
public long Id { get; set; }
public DateTime Date { get; set; }
public string Content { get; set; }
public long DiaryId { get; set; }
public Diaries Diary { get; set; }
public ICollection<Images> Images { get; set; }
}
My code? Probably an entirely different matter.
This is the code that generates the error.
[HttpPost("CreateYear/{id}")]
public async Task<IActionResult> CreateYearOfEntries([FromRoute] int id)
{
// The id is the ID of an existing diary
// Make sure the diary does exist first and that it belongs to the current logged-in user
var diary = _diaryRepository.Find(id);
if (diary == null) return NotFound();
var year = diary.Result.Year;
if (await _diaryEntryRepository.OwnerIsLoggedIn(LoggedInUser.ParentId, id))
{
var noOfDays = DateTime.IsLeapYear(year) ? 366 : 365;
var i = 0;
for (; i < noOfDays; i++)
{
var date = new DateTime(year, 1, 1).AddDays(i);
var newDiaryEntry = new DiaryEntries()
{
Content = " ",
Date = date,
DiaryId = diary.Id
};
await _diaryEntryRepository.Add(newDiaryEntry);
}
return Ok();
}
return NotFound();
}
public class DiaryEntryRepository : IDiaryEntryRepository
{
private readonly ApplicationDbContext _context;
public DiaryEntryRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<DiaryEntries> Add(DiaryEntries diaryEntry)
{
await _context.DiaryEntries.AddAsync(diaryEntry);
await _context.SaveChangesAsync();
return diaryEntry;
}
}
I have parent and childs nested tables.
Here is my model:
public class Categories
{
[Key]
public int CategoriesId { get; set; }
public int Order { get; set; }
public string CategoryName { get; set; }
public List<News> News { get; set; }
}
public class News
{
[Key]
public int NewsId { get; set; }
public int CategoriesId { get; set; }
public string Content { get; set; }
public DateTime Date { get; set; }
...
public List<Comments> Comments { get; set; }
public Categories Categories { get; set; }
}
public class Comments
{
[Key]
public int CommentsId { get; set; }
public int NewsId { get; set; }
public string Comment { get; set; }
...
public News News { get; set; }
}
public class NewsImages
{
[Key]
public int ImageId { get; set; }
public int NewsId { get; set; }
public string ImageUrl { get; set; }
public bool Cover { get; set;}
...
public News News { get; set; }
}
I'm trying to send it from ViewComponent to View;
public async Task<IViewComponentResult> InvokeAsync()
{
var group = _dbContext.Categories.Where(k => k.Order != 0).OrderBy(h => h.Order)
.Select(c => new
{
C = c,
N = c.News.OrderByDescending(n => n.Date).Take(5)
.Select(r => new
{
Y = r.Comments,
R = r.NewsImages.Where(rs => rs.Cover == true).FirstOrDefault()
})
});
var model = group
.Select(m => m.C);
return View(await model.ToListAsync()) ;
}
I am sure there are enough News records for every Category, But I get error :
ArgumentNullException: Value cannot be null. (Parameter 'source')
AspNetCore.Views_Shared_Components_IndexKategori_Default.ExecuteAsync() in Default.cshtml
var bp = k.News.FirstOrDefault();
if I use that code works fine :
var model = _dbContext.Categories
.Include(h => h.News).ThenInclude(h => h.Comments)
.Include(h => h.News).ThenInclude(h => h.NewsImages)
.Where(h => h.Order != 0)
.OrderBy(h => h.Order)
But when I use the code above, a few records appear for some categories, and some categories react as if there are no records.
Where am I making mistakes?
Thank you in advance for those who helped ..
Whenever you have a big LINQ statement that throws an exception, and you can't find where the exception comes form, translate the LINQ into smaller steps, and ToList() every step.
public async Task<IViewComponentResult> InvokeAsync()
{
// Temp code: small steps, ToList after every step
var a = dbContext.Categories.Where(category => category.Order != 0).ToList();
var b = a.OrderBy(category => category.Order).ToList();
var c = b.Select(category => new
{
Category = category,
News = category.News.OrderByDescending(news => news.Date)
.Take(5)
.ToList();
})
.ToList();
var d = c.Select(item => new
{
Category = item.Category,
NewsItems = item.News.Select(news => new
{
Comments = news.Comments,
Images = news.NewsImages.Where(newsImage => newsImage.Cover).ToList(),
})
.ToList(),
})
.ToList();
var e = d.Select(item => new
{
Category = item.Category,
NewsItems = item.NewsItems.Select(newsItem => new
{
Comments = newsItem.Comments,
Images = images.FirstOrDefault();
})
.ToList(),
})
.ToList();
// original code:
var group = _dbContext.Categories.Where...
}
I'm sure that your debugger will tell you which step is incorrect.
I have the following entities when I generate migration it creates two columns with name RestrictedCategoryId and RestrictedCategoryId1(FK). How to solve this issue to generate only one column with FK?
Note: I need OrderId in each entity.
`C#
public class Order
{
public Guid Id { get; set; }
public DateTime OrderDate { get; set; }
private List<Category> _categories;
public List<Category> Categories => _categories;
}
public class Category
{
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public Guid OrderId { get; set; }
public Order Order { get; set; }
private List<RestrictionCategory> _restrictedCategories;
public List<RestrictionCategory> RestrictedCategories => _restrictedCategories;
}
public class RestrictionCategory
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid OrderId { get; set; }
public Order Order { get; set; }
public Guid CategoryId { get; set; }
public Category Category { get; set; }
public Guid RestrictedCategoryId { get; set; }
public Category RestrictedCategory { get; set; }
}
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).IsRequired();
}
}
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
{
public void Configure(EntityTypeBuilder<Category> builder)
{
builder.HasKey(c => new { c.Id, c.OrderId });
builder.Property(o => o.Id).IsRequired();
builder.Property(o => o.OrderId).IsRequired();
builder.HasMany(c => c.RestrictedCategories).WithOne(cr => cr.Category)
.HasForeignKey(cr => new { cr.CategoryId, cr.OrderId
}).OnDelete(DeleteBehavior.NoAction);
}
}
public class RestrictionCategoryConfiguration : IEntityTypeConfiguration<RestrictionCategory>
{
public void Configure(EntityTypeBuilder<RestrictionCategory> builder)
{
builder.HasKey(c => new { c.Id, c.OrderId });
builder.Property(o => o.Id).IsRequired();
builder.Property(o => o.OrderId).IsRequired();
builder.HasIndex(cr => new { cr.RestrictedCategoryId, cr.OrderId });
}
}
`
The entities resembles to actual ones.
Actually you get two additional columns:
RestrictedCategoryId = table.Column<Guid>(nullable: false),
RestrictedCategoryId1 = table.Column<Guid>(nullable: true), // <--
RestrictedCategoryOrderId = table.Column<Guid>(nullable: true) // <--
Apparently EF Core Foreign Key Conventions doesn't play well with composite keys, so you have to explicitly configure the relationship - similar to what you did for the other relationship, just since your model has no corresponding collection navigation property you have to use HasMany with generic type argument and no parameters, e.g. inside CategoryConfiguration:
builder.HasMany<RestrictionCategory>()
.WithOne(cr => cr.RestrictedCategory)
.HasForeignKey(cr => new { cr.RestrictedCategoryId, cr.OrderId})
.OnDelete(DeleteBehavior.NoAction);
I'm having trouble accessing foreign key values in my view without using a partial.
I have tblProperty as Primary_Key and tblCustomer as foreign_key. I want to access the values of my foreign keys in my view but can't figure out why.
Model
public partial class tblProperty
{
public tblProperty()
{
this.Images = new HashSet<Image>();
this.tblCustomers = new HashSet<tblCustomer>();
}
public int propertyID { get; set; }
public string address { get; set; }
public string description { get; set; }
public virtual ICollection<Image> Images { get; set; }
public virtual ICollection<tblCustomer> tblCustomers { get; set; }
}
public partial class tblCustomer
{
public int customerID { get; set; }
public string name { get; set; }
public decimal contactNumber { get; set; }
public string notes { get; set; }
public Nullable<int> propertyID { get; set; }
public virtual tblProperty tblProperty { get; set; }
}
controller
public class propertyController : Controller
{
propertyDBEntities2 dc = new propertyDBEntities2();
public ActionResult List()
{
var properties = dc.tblProperties.Include(p => p.tblCustomers);
return View(properties.ToList());
}
public ActionResult Details(int id = 0)
{
var properties = dc.tblProperties.Include(p => p.tblCustomers);
tblProperty property = dc.tblProperties.Find(id);
tblCustomer customer = dc.tblCustomers.Find(id);
if (properties == null)
{
return HttpNotFound();
}
return View(dc.tblProperties.Find(id));
}
public ActionResult Create()
{
return View();
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Create(tblProperty e)
{
if (ModelState.IsValid)
{
using (dc)
{
dc.tblProperties.Add(e);
dc.SaveChanges();
}
}
return RedirectToAction("List");
}
view
(like model.name is trying to access name from tblCustomer)
#model myProject.tblProperty
#Html.DisplayFor(model => model.name)
tblProperty doesnt have name.
I guess you need
#Html.DisplayFor(model => model.tblCustomer.name)
But just debug it or use intellisense
EDIT:
In my project I create a dtoClass Data Transfer Object
So for my avl class I have a dtoAvl
avl Class:
public partial class avl
{
public avl()
{
this.cars = new HashSet<cars>();
}
public long avl_id { get; set; }
public Nullable<long> car_id { get; set; }
public Nullable<decimal> speed { get; set; }
// this class contain info regarding the road
public virtual manila_rto manila_rto { get; set; }
public virtual ICollection<cars> cars { get; set; }
}
I create a dtoAvl
public class dtoAvl
{
public long Avl_ID { get; set; }
public long? Car_ID { get; set; }
public string RoadName { get; set; } // came from manila_rto
public int Speed { get; set; }
}
My controler
List<dtoAvl> result = db.avls.Select(
r => new dtoAvl
{
Avl_ID = r.Avl_ID,
Car_ID = r.Car_ID,
Speed = r.Speed,
// here is a propery but can be a list
RoadName = r.manila_rto.name
}).ToList();
return PartialView(result);
View:
#model IEnumerable<dtoAvl>