How do I get a response based on two different IDs in my API? - entity-framework

public class Report
{
[Key]
public int ReportId { get; set; }
[ForeignKey("Subjects")]
public int SubjectId { get; set; }
public Subjects Subjects { get; set; }
[ForeignKey("Teacher")]
public int TeacherId { get; set; }
public Teacher Teacher { get; set; }
[ForeignKey("MarkType")]
public int MarkTypeId { get; set; }
public MarkType MarkType { get; set; }
}
public class Teacher
{
[Key]
public int TeacherId { get; set; }
[MaxLength(50)]
[Required]
public string FName { get; set; }
[MaxLength(50)]
[Required]
public string LName { get; set; }
}
public class Student
{
[Key]
public int StudentId { get; set; }
[MaxLength(50)]
[Required]
public string FName { get; set; }
[MaxLength(50)]
[Required]
public string LName { get; set; }
[ForeignKey("Grade")]
public int GradeId { get; set; }
public Grade Grade { get; set; }
}
public class Grade
{
[Key]
public int GradeId { get; set; }
public int StudentGrade { get; set; }
}
public class Subjects
{
[Key]
public int SubjectId { get; set; }
[MaxLength(50)]
[Required]
public string SubjectName { get; set; }
}
public class Terms
{
[Key]
public int TermId { get; set; }
public int Term { get; set; }
}
public class MarkType
{
[Key]
public int MarkTypeId { get; set; }
[MaxLength(20)]
[Required]
public string TypeName { get; set; }
}
public class StudentMark
{
[Key]
public int StudentMarkId { get; set; }
[ForeignKey("Report")]
public int ReportId { get; set; }
public Report Report { get; set; }
[ForeignKey("Student")]
public int StudentId { get; set; }
public Student Student { get; set; }
public int Mark { get; set; }
[ForeignKey("Terms")]
public int TermId { get; set; }
public Terms Terms { get; set; }
}
In the API I am making I want to have the ability to use two different IDs to get a more specific response.
var report = ReportDBContext.StudentMark
.Include(p => p.Student.Grade).Include(p => p.Report)
.Include(p => p.Terms).Include(a => a.Report.Subjects).Include(a => a.Terms)
.Include(a => a.Report.MarkType).Include(a => a.Report.Teacher).ToList();
This allowed me to get StudentMark as well as it's related entities but I want to have the ability to use The student's Id and the Term's Id to get a student's marks for that term and all the subjects related to the student. I am a beginner to Web API so please let me know if I need to add more context.

If you want to query by either StudentId or TermId, I suggest that you provide two different endpoints for these two different queries. Use LINQ Where to check your conditions.
public StudentMark[] GetMarksByStudentId(int studentId) {
return ReportDBContext.StudentMark
/* .Include(...) */
.Where(mark => mark.StudentId == studentId)
.ToArray();
}
public StudentMark[] GetMarksByTermId(int termId) {
return ReportDBContext.StudentMark
/* .Include(...) */
.Where(mark => mark.TermId == termId)
.ToArray();
}
If you want to query by StudentId and TermId simultaneously, introduce a query object to encapsulate your parameters. You can test for multiple conditions in the Where clause with AND &&.
public StudentMark[] FindMarks(StudentMarkQuery query) {
return ReportDBContext.StudentMark
/* .Include(...) */
.Where(mark => mark.StudentId == query.StudentId
&& mark.TermId == query.TermId)
.ToArray();
}
The StudentMarkQuery class is introduced so you can add additional parameters without changing the overall signature of the endpoint:
public class StudentMarkQuery {
public int StudentId { get; set; }
public int TermId { get; set; }
}

Related

EF-Core query but value missing, even it show in database

here is the simple query code...
var order = await _context.ProductOrders
.FirstOrDefaultAsync(x => x.Id == orderId.ToInt());
This is the entity of ProdictOrders
public class ProductOrder
{
public int Id { get; set; }
public int MovieTicketEnrollmentId { get; set; }
public string ProductOrderStatusCode { get; set; }
public int? InvoiceId { get; set; }
public DateTime CreateDate { get; set; }
public string CreateBy { get; set; }
public DateTime? UpdateDate { get; set; }
public string UpdateBy { get; set; }
/**
* Navigation Property
*/
public MovieTicketEnrollment MovieTicketEnrollment { get; set; }
public ProductOrderStatus ProductOrderStatus { get; set; }
public ICollection<ProductOrderItem> ProductOrderItems { get; set; }
public Invoice Invoice { get; set; }
}
This data in the database
And what I got after executing query command above... what's just happening here?
MovieTicketEnrollmentId should equal to 1

Using Automapper through a join table in EFCore

I have a many-to-many relationship between Recipe and Item via a join table called Ingredient:
public class Recipe
{
public int RecipeId { get; set; }
public string Name { get; set; }
public ICollection<RecipeInstruction> RecipeInstructions { get; set; }
public ICollection<Ingredient> Ingredients { get; set; }
}
public class Ingredient
{
public Recipe Recipe { get; set; }
public int RecipeId { get; set; }
public Item Item { get; set; }
public int ItemId { get; set; }
public int Quantity { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public ICollection<Ingredient> Ingredients { get; set; }
}
I would like to present the data through this DTO:
public class RecipeForDetailedDto
{
public int RecipeId { get; set; }
public string Name { get; set; }
public ICollection<RecipeInstruction> RecipeInstructions { get; set; }
public ICollection<ItemForDetailedDto> Ingredients { get; set; }
}
Is there a way I can map this relationship to show a list of Ingredient names, which would be the Item Name?
It should look like this:
CreateMap<Ingredient, ItemForDetailedDto>();
CreateMap<Ingredient,RecipeForDetailedDto>()
.ForMember(dest=>dest.Name, opt=>opt.MapFrom(src=>src.Item?.Name));
var result = mapper.Map<ItemDetailedDto>(yourIngredientObject);
In the end this is what worked:
CreateMap<Ingredient, IngredientForDetailedDto>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Item.Name))
With IngredientForDetailedDto as:
public class IngredientForDetailedDto
{
public string Name { get; set; }
public int Quantity { get; set; }
public string QuantityType { get; set; }
}

many to many entity framework + Compose Primary Key

Hi friends I am having problems with a relationship Much to Much with Compose Primary Key.
I have the following:
public class Empleado
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key, Column(Order = 0)]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Nombre { get; set; }
[Key, Column(Order = 1)]
public int? IdentificacionId { get; set; }
public Identificacion Identificacion { get; set; }
[Required]
[StringLength(11)]
[Key, Column(Order = 2)]
public string NoIdentificacion { get; set; }
}
// Entidad relación
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
// FluentApi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Constraint combinado TipoId + NoID
modelBuilder.Entity<Empleado>().HasKey(x => new { x.IdentificacionId, x.NoIdentificacion });
// Relación
modelBuilder.Entity<EmpleadoNomina>().HasKey(k => new { k.NominaId, k.EmpleadoId });
}
The problem arises when the relationship table is created. To this is added the columns Employee_IdentificationId, Employee_NoIdentification. And the EmployeeId column without foreignkey.
The other problem is: I can't use .Find(id); example: db.Empleados.Find(15); This gives an error because it requires me to pass the three keys.
I just want to remove the extra columns Employee_IdentificationId, Employee_NoIdentification and only use EmpleadoId.
Don't use a composite key on Empleado - just use ID as its key. Same for Nomina. The composite key is used on the bridge table. Also, since you are already using fluent code you don't need the annotations. Behavior can be odd when you mix.
public class Empleado
{
// This will be identity key by convention
public int Id { get; set; }
// These could be set in fluent code
[Required]
[StringLength(100)]
public string Nombre { get; set; }
public string NoIdentificacion { get; set; }
// This will be an optional FK by convention
public int? IdentificacionId { get; set; }
public Identificacion Identificacion { get; set; }
public virtual ICollection<Nomina> Nominas { get; set; }
}
public class Nomina
{
// This will be identity key by convention
public int Id { get; set; }
public string XXXXXX { get; set; }
... etc
public virtual ICollection<Empleado> Empleados { get; set; }
}
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
// FluentApi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Empleado>()
.HasMany<Nomina>(e => e.Nominas)
.WithMany(c => c.Empleado)
.Map(cs =>
{
cs.MapLeftKey("Id");
cs.MapRightKey("Id");
cs.ToTable("EmpleadoNomina");
});
}
See here
EDIT: OK, If you need to keep the composite key on Empleado, then you will need to reference it with a composite FK. So you need to add the other 2 FK fields:
// Entidad relación
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int IdentificacionId { get; set; }
public string NoIdentificacion { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
Then the fluent code:
modelBuilder.Entity<EmpleadoNomina>()
.HasRequired(en => en.Empleado)
.WithMany()
.HasForeignKey(en => new {en.EmpleadoId, en.IdentificacionId , en.NoIdentificacion });
Also, I am not sure IdentificacionId can be nullable. See here.
I solved it with Index Dataanotations to create the Unique Composited Index instead of a Composited primary key (this was responsible of my problem).
I removed the composite keys from the main class and added a list of EmployeeNomine to the two classes of entities.
I changed everything as shown below and now it is working very well. This what I wanted to do from the beginning.
// Class 2
public class Empleado
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Nombre { get; set; }
[Index("IX_Identificacion", 1, IsUnique = true)]
public int? IdentificacionId { get; set; }
public Identificacion Identificacion { get; set; }
[Required]
[StringLength(11)]
[Index("IX_Identificacion", 2, IsUnique = true)]
public string NoIdentificacion { get; set; }
public List<EmpleadoNomina> EmpleadoNominas { get; set; }
}
// Class 1
public class Nomina
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Descripcion { get; set; }
public int Frecuencia { get; set; }
public int Dia { get; set; }
public List<EmpleadoNomina> EmpleadoNominas { get; set; }
}
// Relation Entity (Table)
public class EmpleadoNomina
{
public int EmpleadoId { get; set; }
public int NominaId { get; set; }
public decimal Salario { get; set; }
public int DescuentoLey { get; set; }
public decimal? SalarioIngresoEgreso { get; set; }
public Nomina Nomina { get; set; }
public Empleado Empleado { get; set; }
}
// FluentApi
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Nominas -> Empleados
modelBuilder.Entity<EmpleadoNomina>().HasKey(k => new { k.NominaId, k.EmpleadoId });
modelBuilder.Entity<EmpleadoNomina>().HasRequired(e => e.Empleado).WithMany(n => n.EmpleadoNominas).HasForeignKey(r => r.EmpleadoId);
modelBuilder.Entity<EmpleadoNomina>().HasRequired(n => n.Nomina).WithMany(n => n.EmpleadoNominas).HasForeignKey(n => n.NominaId);
}
It's I always wanted to do. thanks for everything

Complex subquery in Entity Framework 6

I have an entity called Insurance like this:
public class Insurance : BaseEntity, IExpirationDocument
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override int Id { get; set; }
[Column(TypeName = "NVARCHAR")]
[StringLength(256)]
public string PathToCertificate { get; set; }
[Column(TypeName = "NVARCHAR")]
[StringLength(50)]
public string Filename { get; set; }
public int Value { get; set; }
public string Name => InsuranceType.Name;
public DateTime ExpiryDate { get; set; }
public DateTime IssueDate { get; set; }
public bool Active { get; set; }
public int InsuranceTypeId { get; set; }
public virtual InsuranceType InsuranceType { get; set; }
public int InsurerId { get; set; }
public virtual Insurer Insurer { get; set; }
public int ApplicantId { get; set; }
public virtual Applicant Applicant { get; set; }
public int? DocumentEmailHistoryId { get; set; }
public virtual DocumentEmailHistory DocumentEmailHistory { get; set; }
public Insurance()
{
Active = true;
}
}
Would it be possible to do this type of query with Entity Framework:
SELECT *
FROM Insurances i1
INNER JOIN
(SELECT
insuranceTypeId, applicantid, MAX(IssueDate) as 'maxissuedate'
FROM
Insurances
GROUP BY
insuranceTypeId, applicantid) AS i2 ON i1.applicantid = i2.applicantid
AND i1.insuranceTypeId = i2.insuranceTypeId
WHERE
i1.issueDate = i2.maxissuedate
If you are trying to get latest issued Insurance according to InsuranceTypeId and ApplicantId you can group data according to needed properties, order by IssueDate descendingly and take only one Insurance info. Of course it will not give you the same query but it will give you the same result:
var result = context.Insurances
.GroupBy(m => new { m.InsuranceTypeId , m.ApplicantId })
.Select( g => new
{
MaxInsurance = g.OrderByDescending(m => m.IssueDate)
.Take(1)
})
.SelectMany(m => m.MaxInsurance);

Entity framework navigation property is null

I have two models using Entity Framework.
public class Player
{
public int PlayerId { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
public string Plays { get; set; }
public string FavouriteSurface { get; set; }
}
public class SinglesMatch
{
public int SinglesMatchId { get; set; }
public int Player1Id { get; set; }
public int Player2Id { get; set; }
public int PlayerIdWinner { get; set; }
public DateTime Date { get; set; }
public string Venue { get; set; }
public string Score { get; set; }
public List<Player> Players { get; set; }
}
I am using the below code to attempt to display the Name of the player, based on the PlayerId in the SinglesMatch model matching the PlayerID from the Player model.
#foreach (var item in #Model)
{
<ul id="Players" class="bg-success"></ul>
<br/>
<h3>Date - #Html.DisplayFor(#modelItem => item.Date)</h3>
<li>Venue - #Html.DisplayFor(#modelItem => item.Venue)</li>
<li>Player 1 - #Html.DisplayFor(#modelItem => item.Players.First(p => p.PlayerId == item.Player1Id).Name)</li>
<li>Player 2 - #Html.DisplayFor(#modelItem => item.Players.First(p => p.PlayerId == item.Player2Id).Name)</li>
<li>Score- #Html.DisplayFor(#modelItem => item.Score)</li>
}
Upon debugging, the navigation property is always showing as null when the model is retrieved from my repository.
Am I using the navigation property in the correct fashion ? is there a problem with my query ?
Edit to include DbContext:
public TennisTrackerContext() : base("name=TennisTrackerContext")
{
}
public DbSet<Player> Players { get; set; }
public DbSet<PlayerRecord> PlayerRecords { get; set; }
public DbSet<SinglesMatch> SinglesMatches { get; set; }
public DbSet<DoublesMatch> DoublesMatches { get; set; }
public DbSet<Venue> Venues { get; set; }
}
}
You need to add a bridge table. Sql will create this automatically but you won't have access to the variables unless you create it in c#.
public class Player
{
public int PlayerId { get; set; }
public string Name { get; set; }
public string Sex { get; set; }
public string Plays { get; set; }
public string FavouriteSurface { get; set; }
List<PlayerInMatch> Matches { get; set; }
public Player()
{
Matches = new List<PlayerInMatch>();
}
}
public class PlayerInMatch
{
public int Id { get; set; }
public int PlayerId { get; set; }
[ForeignKey("PlayerId")]
public Player Player { get; set; }
public int SinglesMatchId { get; set; }
[ForeignKey("SinglesMatchId")]
public SinglesMatch SinglesMatch { get; set; }
}
public class SinglesMatch
{
public int SinglesMatchId { get; set; }
public int PlayerIdWinner { get; set; }
public DateTime Date { get; set; }
public string Venue { get; set; }
public string Score { get; set; }
public List<PlayerInMatch> Players { get; set; }
public SinglesMatch()
{
Players = new List<PlayerInMatch>();
}
}
static void Main(string[] args)
{
var match = new SinglesMatch();
match.Players.Select(c => c.Player.Name);
}
You need to make your navigation property virtual to enable lazy/eager loading:
public class SinglesMatch
{
public int SinglesMatchId { get; set; }
public int Player1Id { get; set; }
public int Player2Id { get; set; }
public int PlayerIdWinner { get; set; }
public DateTime Date { get; set; }
public string Venue { get; set; }
public string Score { get; set; }
public virtual List<Player> Players { get; set; }
}
Also, did you define the relationship between SinglesMatch and Singles in fluent api?
EDIT: I see you don't have any relations mapped through annotations or fluent api whatsoever, I suggest you take a look at this:
https://msdn.microsoft.com/en-us/data/jj591617.aspx