Complex subquery in Entity Framework 6 - entity-framework

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);

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

EF Lambda How to make projection for GroupJoin

I am trying to query EF models. (GameBank and GameCouponBank) How can I make a projection for left outer join (GoupJoin)?
Can I make projection for Coupons?
Here is my query
var gameBankResult = context.GameBanks.GroupJoin(context.GameCouponBanks, g => g.GameBankID, gc => gc.GameBankID,
(g,gc) => new {
g.quantity,
g.currency,
g.initiationResultCode,
g.productCode,
g.productDescription,
g.referenceId,
g.responseDateTime,
g.unitPrice,
g.totalPrice,
Coupons = gc
})
.Where(g => g.productCode == initiate.productCode)
.Select(s => s);
Here is models:
public class GameBank
{
public int GameBankID { get; set; }
public string referenceId { get; set; }
public string productCode { get; set; }
public int quantity { get; set; }
public string version { get; set; }
public DateTime? requestDateTime { get; set; } = DateTime.Now;
public int? customerID { get; set; }
public string password { get; set; }
public DateTime? responseDateTime { get; set; } = DateTime.Now;
public string initiationResultCode { get; set; }
public string companyToken { get; set; }
public int used { get; set; }
public string productDescription { get; set; }
public string currency { get; set; }
public double unitPrice { get; set; }
public double totalPrice { get; set; }
public virtual List<GameCouponBank> coupons { get; set; }
}
public class GameCouponBank
{
public int Id { get; set; }
public int GameBankID { get; set; }
public DateTime? expiryDate { get; set; }
public string Serial { get; set; }
public string Pin { get; set; }
}
You don't need to use GroupJoin explicitly. You can simply project your query as follows:
var gameBankResult = context.GameBanks.Where(g => g.productCode == initiate.productCode)
.Select(g => new {
g.quantity,
g.currency,
g.initiationResultCode,
g.productCode,
g.productDescription,
g.referenceId,
g.responseDateTime,
g.unitPrice,
g.totalPrice,
Coupons = g.coupons.Select(c => new {c.Id, c.GameBankID,...}).ToList() //<-- Here is the projection for coupons
}).FirstOrDefault(); // I assume you are returning single entity, if not then use `.ToList()` instead of `.FirstOrDefault()`

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

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; }
}

Method based linq queries

I have five tables in database whose Entity classes are as follows -
Product
public int ProductId { get; set; }
public string Name { get; set; }
public int Category { get; set; }
public string Description { get; set; }
public string Brand { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
public virtual ICollection<ProductImage> ProductImages { get; set; }
public virtual ICollection<ProductVariantMapping> ProductVariantMappings
ProductCategory
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
ProductImage
public int ProductImageId { get; set; }
public int ProductId { get; set; }
public byte[] Image { get; set; }
public virtual Product Product { get; set; }
ProductVariantMapping
public int MappingId { get; set; }
public int ProductId { get; set; }
public int ProductVariantId { get; set; }
public string Value { get; set; }
public System.Guid GUID { get; set; }
public virtual Product Product { get; set; }
public virtual ProductVariant ProductVariant { get; set; }
ProductVariant
public int ProductVariantId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductVariantMapping> ProductVariantMappings
I want to get Product Details which should include ProductId, ProductName, Category, Description, Brand, Image(Only 1 for now), and Variants*
*Variants would be a list of all the variants of a product. A single variant can be a combination of all the VariantIds with same GUIDs. (VariantName is in ProductVariant table and VariantValue is in ProductVariantMapping table and Price is in inventory table).
So, I used method-based linq for this purpose.
EkartEntities ekartEntities = new EkartEntities();
var productDetails = ekartEntities.Products.Include(p =>
p.ProductVariantMappings).Include(p => p.ProductImages).Include(p =>
p.ProductCategory).Where(p => p.ProductId ==
productDetailDTO.ProductId).ToList();
Now I have to convert my product into a ProductDetailDTO.
ProductDetailDTO
public class ProductDetailDTO
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public byte[] Image { get; set; }
public string Description { get; set; }
public string Brand { get; set; }
public List<Variant> Variants { get; set; }
}
public class Variant
{
public string Name { get; set; }
public string Value { get; set; }
public System.Guid Guid { get; set; }
public decimal Price { get; set; }
}
I started doing this like this -
void ToDTO(List<Product> products)
{
EkartEntities ekartEntities = new EkartEntities();
ProductDetailDTO productDetailDTO = new ProductDetailDTO();
foreach (var item in products)
{
productDetailDTO.ProductId = item.ProductId;
productDetailDTO.Name = item.Name;
productDetailDTO.Category = item.ProductCategory.Name;
productDetailDTO.Description = item.Description;
productDetailDTO.Brand = item.Brand;
productDetailDTO.Image = item.ProductImages.ElementAt(0).Image;
foreach (var variant in item.ProductVariantMappings)
{
productDetailDTO.Variants = variant.ProductVariant // ?
}
}
}
I don't know how do I proceed further. How can I extract the variant based on the GUIDs?
The logic of combining of ProductVariant entries with same GUID in mapping table doesn't seem clear from the question, however you can group entries in ProductVariantMappings by GUID and then add any logc you like on group. Here is an example where I take first name and value in a groub of variant with the same GUID:
void ToDTO(List<Product> products)
{
EkartEntities ekartEntities = new EkartEntities();
ProductDetailDTO productDetailDTO = new ProductDetailDTO();
foreach (var item in products)
{
productDetailDTO.ProductId = item.ProductId;
productDetailDTO.Name = item.Name;
productDetailDTO.Category = item.ProductCategory.Name;
productDetailDTO.Description = item.Description;
productDetailDTO.Brand = item.Brand;
productDetailDTO.Image = item.ProductImages.ElementAt(0).Image;
productDetailDTO.Variants = item.ProductVariantMappings
.GroupBy(pm => pm.GUID)
.Select(g => new Variant
{
Guid = g.Key,
// Here should be some logic for getting a name of the combination of Variants
// I just take first
Name = g.FirstOrDefault()?.ProductVariant?.Name,
// Here should be some logic for getting a value of the combination of Variants
// Take first again
Value = g.FirstOrDefault()?.Value,
Price = // need inventory table to compute price
})
.ToList();
}
}
Also note that you need somehow add relation to inventory table, which is not presented in question. Hope it helps.

How to create Model based C# List from Database

I have Models created through Entity Framework as:
public partial class Order
{
public Order()
{
this.OrderDetails = new HashSet<OrderDetail>();
}
public int OrderID { get; set; }
public string OrderNo { get; set; }
public System.DateTime OrderDate { get; set; }
public string Description { get; set; }
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
}
Then another Details Table:
public partial class OrderDetail
{
public int OrderItemsID { get; set; }
public int OrderID { get; set; }
public string ItemName { get; set; }
public int Quantity { get; set; }
public decimal Rate { get; set; }
public decimal TotalAmount { get; set; }
public virtual Order Order { get; set; }
}
To See the Master Detail Data I made MasterDetails model As:
public class OrderVM
{
public string OrderNo { get; set; }
public DateTime OrderDate { get; set; }
public string Description { get; set; }
public List<OrderDetail> OrderDetails {get;set;}
}
I'm trying to make a method that return a LIST with Join query results but
I'm receiving #Anonymous type error here is my Code:
public static List<OrderVM > mylist()
{
List<OrderVM> slist = new List<OrderVM>();
using (MyDatabaseEntities1 dc = new MyDatabaseEntities1())
{
var myvalues = from O in dc.Orders
join D in dc.OrderDetails
on
O.OrderID equals D.OrderID
select new
{
O.OrderID,
O.OrderDate,
D.Quantity,
D.Rate
};
foreach(var myorders in myvalues)
{
slist.Add(myorders);
}
return slist;
}
}
I need a help that how I can I create a generic list with database fields
Create new class:
public class OrderDetailsModel
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public int Quantity { get; set; }
public decimal Rate { get; set; }
}
and return list of objects of this class from your method:
using (MyDatabaseEntities1 dc = new MyDatabaseEntities1())
{
var myvalues = from O in dc.Orders
join D in dc.OrderDetails
on
O.OrderID equals D.OrderID
select new OrderDetailsModel
{
OrderId = O.OrderID,
OrderDate = O.OrderDate,
Quantity = D.Quantity,
Rate = D.Rate
};
return myvalues.ToList();
}