Get Error : Entity type 'Course' is defined with a single key property, but 2 values were passed to the 'DbSet.Find' method - asp.net-core

I have a GenericRepository,Which CourseRepository inherits from it, and Entities returns a list of courses
public class CourseRepository : GenericRepositories<Course>, ICourseRepository
{
public CourseRepository(LearningSiteDbContext Context) : base(Context)
{
}
}
public class GenericRepositories<TEntity> : IGenericRepositories<TEntity> where TEntity : class, IEntity,new()
{
protected readonly LearningSiteDbContext context;
public DbSet<TEntity> Entities { get; set; }
public GenericRepositories(LearningSiteDbContext Context)
{
context = Context;
Entities = context.Set<TEntity>();
}
}
But When I run this handler In Razor Page
public async Task OnGetAsync(int Id, CancellationToken cancellationToken)
{
var selectedCourse = await courseRepository.Entities.FindAsync(Id,cancellationToken);
Model = mapper.Map<CourseEditVm>(selectedCourse);
}
I get the following error :
Entity type 'Course' is defined with a single key property, but 2 values were passed to the 'DbSet.Find' method
And this is Course entity class
public class Course
{
public Course()
{
}
public Course(DateTime CreateDate)
{
this.CreateDate = CreateDate;
}
public int Id {get;set;}
public string CourseTitle { get; set; }
public string CourseDescription { get; set; }
public decimal CoursePrice { get; set; }
public string ImageName { get; set; }
public string DemoFileName { get; set; }
public DateTime CreateDate { get; set; }
public DateTime? UpdateDate { get; set; }
public bool IsDeleted { get; set; }
//Foreign key
public int? CourseStatusId { get; set; }
public int? CourseLevelId { get; set; }
public Guid? CustomUserId { get; set; }
public int? CourseGroupId { get; set; }
//Navigations
public CourseStatus CourseStatus { get; set; }
public CourseLevel CourseLevel { get; set; }
public CustomUser CustomUser { get; set; }
public CourseGroup CourseGroup { get; set; }
public ICollection<CourseEpisod> CourseEpisods { get; set; }
public ICollection<Keyword> Keywordkeys { get; set; }
}
And it's Course Config
class CourseConfig : IEntityTypeConfiguration<Course>
{
public void Configure(EntityTypeBuilder<Course> builder)
{
builder.HasKey(c => c.Id);
builder.Property(c => c.Id).ValueGeneratedOnAdd();
builder.Property(c => c.CourseTitle).HasMaxLength(50);
builder.Property(c => c.CourseDescription).HasMaxLength(400);
builder.Property(c => c.ImageName).HasMaxLength(255);
builder.Property(c => c.DemoFileName).HasMaxLength(255);
//Relations
builder.HasMany(c => c.Keywordkeys).WithOne(c => c.Course).HasForeignKey(c => c.CourseId);
builder.HasOne(c => c.CustomUser).WithMany(c => c.Courses).HasForeignKey(c => c.CustomUserId);
}
}
But when I run this code, I will no longer receive this error
var selectedCourse = await courseRepository.Entities.FirstOrDefaultAsync(c => c.Id == Id, cancellationToken);
What is the cause of this error? Is my code wrong? How should I fix this error?

You are using the wrong method, if you check here
FindAsync you can see that if you want to pass a cancellation token, you need to pass your keys as an array like this
.FindAsync(new object[]{id}, cancellationToken);

Related

Eager loading use Include got error in .Net

I'd like to display the 'ApplicationRole' Name with the 'ApplicationUser' data together using Eager loading.
here is my ApplicationUser
public class ApplicationUser : IdentityUser
{
public string? Name { get; set; }
public string? Department { get; set; }
public bool IsDeleted { get; set; }
public string RoleId { get; set; }
public virtual ApplicationRole Role { get; set; } = new ApplicationRole();
}
public class ApplicationUserDTO
{
public string Id { get; set; }
public int UserId { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
// Foreign Key
public string RoleId { get; set; }
public virtual ApplicationRoleDTO Role { get; set; } = new ApplicationRoleDTO();
}
here is ApplicationRole
[NotMapped]
public class ApplicationRole : IdentityRole
{
public string Id { get; set; }
public string Name { get; set; }
//[InverseProperty(nameof(ApplicationUser.Role))]
public virtual ICollection<ApplicationUser> Users { get; set; } = new List<ApplicationUser>();
}
public class ApplicationRoleDTO
{
public string Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicationUserDTO> Users { get; set; } = new List<ApplicationUserDTO>();
}
and here is the UserRepository
public async Task<IEnumerable<ApplicationUser>> GetAllUsersAsync()
{
//return await _dbContext.User.Where(u => u.IsDeleted == false).ToListAsync();
return await _dbContext.User.Include(u => u.Role).Where(u => u.IsDeleted == false).ToListAsync();
}
public async Task<ApplicationUser> GetUserAsync(string id)
{
return await _dbContext.User.Where(u => u.Id == id).Where(u => u.IsDeleted == false).FirstOrDefaultAsync();
}
But when I use the 'Include' keyword it show this error
When I try to use the string "Role", it also show error
public async Task<IEnumerable<ApplicationUser>> GetAllUsersAsync()
{
return await _dbContext.User.Include("Role").Where(u => u.IsDeleted == false).ToListAsync();
}
Here is the error show

ASP.Net core - make a search inside a nested collection

I'm trying to make a nested collection search and I'm really struggling.
My expected result is: I would like to make a search and find all the powerUp objects by a certain date. (PowerUpDate property - that's the searching criteria)
User Model:
public class AppUser : IdentityUser
{
public ICollection<Hero> Heroes { get; set; }
}
Hero Model:
[Table("Heroes")]
public class Hero
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Ability { get; set; }
[Required]
public string SuitColors { get; set; }
public double CurrentPower { get; set; }
public double StartingPower { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public ICollection<PowerUp> PowerUps { get; set; }
public AppUser AppUser { get; set; }
[Required]
public string AppUserId { get; set; }
}
PowerUp Model:
[Table("PowerUps")]
public class PowerUp
{
public int Id { get; set; }
[Required]
public double PowerUpIncrement { get; set; }
[Required]
public DateTime PowerUpDate { get; set; } = DateTime.Now;
public Hero Hero { get; set; }
[Required]
public int HeroId { get; set; }
}
DataContext:
public class DataContext : IdentityDbContext<AppUser>
{
public DataContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Hero>().HasMany(hero => hero.PowerUps).WithOne(powerUp => powerUp.Hero)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<AppUser>().HasMany(user => user.Heroes).WithOne(hero => hero.AppUser)
.OnDelete(DeleteBehavior.Cascade);
}
}
Could someone please explain to me how can I implement such a search on a nested collection?
Inject your AppUser user using Dependency injection
(better use the repository pattern) anyway it should be something like this: user.Heroes.PowerUps.OrderBy(x=>x.PowerUpDate == Datetime.Now).ToList();
x.PowerUpDate == To whatever date you will insert

How to get the discriminator from the id of an entitie?

I would like to now wether my id is an invoice or an individualinvoice
individualinvoice.cs
public class IndividualInvoice : Invoice {
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
invoice.cs
public class Invoice {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
[Required]
public string Company { get; set; }
[Required]
public string Address { get; set; }
[Required]
public int HouseNumber { get; set; }
[Required]
public string Zipcode { get; set; }
[Required]
public string City { get; set; }
[Required]
public string Country { get; set; }
[Required]
public string VATNumber { get; set; }
public Customer Customer { get; set; }
[ForeignKey("Customer")]
[Required]
public string CustomerId { get; set; }
}
gingsengdbcontext.cs
public class GingsengDbContext : IdentityDbContext<GingsengUser> {
public DbSet<Gingseng> Gingsengs { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<IndividualInvoice> IndividualInvoices { get; set; }
public GingsengDbContext(DbContextOptions<GingsengDbContext> options) : base(options)
{
}
}
And here is my controller where i would like to know from the id if the id corresponds to an individialinvoice or just an invoice? is there any cleaner way than to use singleordefault?
public class InvoicesController : Controller {
private readonly GingsengDbContext context;
private readonly IMapper mapper;
public InvoicesController(GingsengDbContext context, IMapper mapper)
{
this.context = context;
this.mapper = mapper;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetInvoice(string id) {
}
}
Well, the only clean way which works with all EF Core supported inheritance models (currently TPH and TPT) is to use C# is operator. However the classes must not inherit other non abstract class from the same hierarchy like in your example, because IndividualInvoice is a Invoice, hence will be included in DbSet<Invoice> and any query (OfType etc.) which checking for Invoice.
So you can check just for final classes, e.g.
bool isIndividualInvoice = await context.Invoices
.AnyAsync(e => e.Id == id && e is IndividualInvoice);
which btw is the same as
bool isIndividualInvoice = await context.IndividualInvoices
.AnyAsync(e => e.Id == id);
and similar (using Set<IndividualInvoice>() or Set<Invoice>().OfType<IndividualInvoice>).
Another not so clean option which works only for TPH is to retrieve the discriminator property value directly. You have to know its name and type (the defaults are "Discriminator" and string) and use the special EF.Property method similar to this:
var type = await context.Invoices
.Where(e => e.Id == id)
.Select(e => EF.Property<string>(e, "Discriminator")) // <--
.FirstOrDefaultAsync();
// here type will be ether null, "Invoice" or "IndividualInvoice"

How to update an existing entity that has a nested list of entities?

I'm trying to update an entity using entity framework but, everytime I try to do it, it raises an error saying that a nested entity the main class contains cannot be tracked.
These are my classes:
public abstract class BaseEntity
{
public int Id { get; set; }
}
public class Dashboard : BaseEntity
{
public int Order { get; set; }
public string Title { get; set; }
public bool Enabled { get; set; }
public virtual ICollection<Submenu> Submenu { get; set; }
}
public class Submenu : BaseEntity
{
public int Order { get; set; }
public bool Enabled { get; set; }
public string Title { get; set; }
public string Image { get; set; }
public string Descriptions { get; set; }
public virtual ICollection<Action> Actions { get; set; }
public int DashboardId { get; set; }
public virtual Dashboard Dashboard { get; set; }
}
public class Action : BaseEntity
{
public string Type { get; set; }
public string Label { get; set; }
public string Url { get; set; }
public string Extension { get; set; }
public virtual Submenu Submenu { get; set; }
public int SubmenuId { get; set; }
}
The one I am using to update is Dashboard, which contains the rest of the classes.
I'm trying to do it using a generic service layer and a generic repository that are defined this way:
public class GenericService<T> : IGenericService<T> where T : BaseEntity
{
private readonly IBaseRepository<T> baseRepository;
public GenericService(IBaseRepository<T> baseRepository)
{
this.baseRepository = baseRepository;
}
public async Task Update(T entity, T attachedEntity)
{
await baseRepository.Update(entity, attachedEntity);
}
}
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly PortalContext dataContext;
private DbSet<T> DbSet { get; set; }
public BaseRepository(PortalContext context)
{
dataContext = context;
DbSet = dataContext.Set<T>();
}
public async Task Update(T entity, T attachedEntity)
{
dataContext.Entry(attachedEntity).State = EntityState.Detached;
DbSet.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
await dataContext.SaveChangesAsync();
}
}
And, at last but no least, this is the way I am configuring everything at Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PortalContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("PortalContext"))
);
services.AddTransient(typeof(IGenericService<>), typeof(GenericService<>));
services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));
services.AddTransient<Func<string, ClaimsPrincipal, IRoleCheck>>((serviceProvider) =>
{
return (controllerName, claimsPrincipal) =>
new RoleCheck(serviceProvider.GetRequiredService<IGenericService<Dossier>>(),
serviceProvider.GetRequiredService<IGenericService<DossierTemplate>>(),
serviceProvider.GetRequiredService<IGenericService<Dashboard>>(),
controllerName, claimsPrincipal);
});
}
What the application first does is calling the RoleCheck class to retrieve and filter the required entities and, after that, the user can update them.
When I call the update function at the controller
public async Task<ActionResult<Dashboard>> Put(int id, [FromBody] Dashboard dashboard)
{
var currentDashboard = await service.Get(id);
if (currentDashboard == null)
{
return NotFound();
}
await service.Update(dashboard, currentDashboard);
return Ok();
}
I always receive the next error at the repository:
error
Is there something I am doing wrong? I have been stuck with this for a week now...
Thanks in advance and sorry for the long text, but I wanted it to be clear.
I could finally solve it by adding .AsNoTracking() at the Get() method of my repository:
public async Task<T> Get(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> includes)
{
IQueryable <T> query = DbSet.AsNoTracking();
if (includes != null)
{
query = includes(query);
}
return await query.FirstOrDefaultAsync(m => m.Id == id);
}

Relations with DbQuery

I have this Participant model DbSet<Participants>:
public class Participant {
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int IsCaptain { get; set; }
public Guid TeamId { get; set; }
[ForeignKey("TeamId")]
public Team Team { get; set; }
}
And this ParticipantDataView DbQuery<ParticipantsDataView>:
public class ParticipantDataView {
public Guid Id { get; set; } // = Participant.Id
public double? FirstWeight { get; set; }
public double? LastWeight { get; set; }
public double? WeightLoss => FirstWeight - LastWeight;
public Participant Participant { get; set; }
}
DbContext:
public class DBContext : DbContext {
public DBContext(DbContextOptions<DBContext> options) : base(options) {}
public DbSet<Participant> Participants { get; set; }
public DbQuery<ParticipantDataView> ParticipantsDataView { get; set; }
}
My query:
Participants = await _context.ParticipantsDataView
.Include(p => p.Participant)
.ThenInclude(t => t.Team)
.Where(p => p.Participant.Status == 1 && p.Participant.Team.Status == 1).OrderBy(p => p.WeightLoss)
.AsNoTracking()
.ToListAsync();
The error:
SqlNullValueException: Data is Null.
This method or property cannot be called on Null values.
Every item in ParticipantDataView has a match in Participants so I don't know why I get this error?