How to get EF.core to use LEFT JOINs instead of INNER JOINs - left-join

I'm trying to include foreign-key details as part of my query.
How can I get EF.core to use LEFT JOINs instead of INNER JOINs?
public class Offence
{
[Key]
public Int32 offence_id { get; set; }
public Int32 guard_id { get; set; }
public Int32 penalty_id { get; set; }
public DateTime? dt_recorded { get; set; }
public Int32 salary_id { get; set; }
public Decimal? amount { get; set; }
public String status { get; set; }
public Int32 site_id { get; set; }
public Guard Guard { get; set; }
public Salary Salary { get; set; }
public Site Site { get; set; }
public Penalty Penalty { get; set; }
public DateTime? last_modified { get; set; }
public int? last_modified_by { get; set; }
}
In Controller GetList
var offences = db.Offences
.Include(e => e.Guard)
.Include(e => e.Penalty)
.Include(e => e.Site)
.Include(e => e.Salary)
.AsNoTracking();
Generated SQL:
SELECT [e].[offence_id], [e].[amount], [e].[dt_recorded], [e].[guard_id], [e].[last_modified], [e].[last_modified_by], [e].[penalty_id], [e].[salary_id], [e].[site_id], [e].[status], [e.Salary].[salary_id], [e.Salary].[dt_paid], [e.Salary].[guard_id], [e.Salary].[last_modified], [e.Salary].[last_modified_by], [e.Salary].[period], [e.Site].[site_id], [e.Site].[address], [e.Site].[client_id], [e.Site].[last_modified], [e.Site].[last_modified_by], [e.Site].[name], [e.Site].[state], [e.Penalty].[penalty_id], [e.Penalty].[amount], [e.Penalty].[description], [e.Penalty].[dt], [e.Penalty].[last_modified], [e.Penalty].[last_modified_by], [e.Penalty].[name], [e.Guard].[guard_id], [e.Guard].[address], [e.Guard].[bank], [e.Guard].[dob], [e.Guard].[dt_joined], [e.Guard].[dt_trained], [e.Guard].[has_picture], [e.Guard].[height], [e.Guard].[last_modified], [e.Guard].[last_modified_by], [e.Guard].[location_id], [e.Guard].[marital_status], [e.Guard].[mobiles], [e.Guard].[name], [e.Guard].[nuban], [e.Guard].[ref_no], [e.Guard].[religion], [e.Guard].[salary], [e.Guard].[sex], [e.Guard].[state_origin], [e.Guard].[status]
FROM [Offences] AS [e]
left JOIN [Salaries] AS [e.Salary] ON [e].[salary_id] = [e.Salary].[salary_id]
left JOIN [Sites] AS [e.Site] ON [e].[site_id] = [e.Site].[site_id]
left JOIN [Penalties] AS [e.Penalty] ON [e].[penalty_id] = [e.Penalty].[penalty_id]
left JOIN [Guards] AS [e.Guard] ON [e].[guard_id] = [e.Guard].[guard_id]
ORDER BY [e.Guard].[name]

Found the solution, make all the foreign keys null-able, then EF.core uses a LEFT JOIN for the query instead:
public class Offence
{
[Key]
public Int32 offence_id { get; set; }
public Int32? guard_id { get; set; } // make null-able
public Int32? penalty_id { get; set; } // make null-able
public DateTime? dt_recorded { get; set; }
public Int32? salary_id { get; set; } // make null-able
public Decimal? amount { get; set; }
public String status { get; set; }
public Int32? site_id { get; set; } // make null-able
public Guard Guard { get; set; }
public Salary Salary { get; set; }
public Site Site { get; set; }
public Penalty Penalty { get; set; }
public DateTime? last_modified { get; set; }
public int? last_modified_by { 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

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

Why does EF.core sometimes includes invalid columns?

How does EF.Core choose it's columns?
I have these two simple classes: Payment and Site
I use this for my select list:
var payments = db.Payments
.Include(e => e.Site)
.AsNoTracking();
EF.Core generates this invalid sql:
SELECT [e].[pay_id], [e].[amount], [e].[cheque_no], [e].[client_id], [e].[dt], [e].[last_modified], [e].[last_modified_by], [e].[pay_dt], [e].[pay_mode], [e].[site_id], [e.Site].[site_id], [e.Site].[address], [e.Site].[client_id], [e.Site].[last_modified], [e.Site].[last_modified_by], [e.Site].[name], [e.Site].[state]
FROM [Payments] AS [e]
INNER JOIN [Sites] AS [e.Site] ON [e].[site_id] = [e.Site].[site_id]
ORDER BY [e.Site].[name]
There is no client_id in the Payments table or Payment class below
public class Payment
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int32 pay_id { get; set; }
public DateTime? dt { get; set; }
public Int32 site_id { get; set; }
public Decimal? amount { get; set; }
public DateTime? pay_dt { get; set; }
public String pay_mode { get; set; }
public String cheque_no { get; set; }
public virtual Site Site { get; set; }
public DateTime? last_modified { get; set; }
public int? last_modified_by { get; set; }
}
public class Site
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int32 site_id { get; set; }
public String name { get; set; }
public Int32 client_id { get; set; }
public String address { get; set; }
public String state { get; set; }
public virtual Client Client { get; set; }
public List<Deployment> Deployments { get; set; }
public List<Contract> Contracts { get; set; }
public List<Timesheet> Timesheets { get; set; }
public DateTime? last_modified { get; set; }
public int? last_modified_by { get; set; }
}

EntityFramework One-To-Many fK?

BasketItem duplicateBasketItem = (from ph in storeDB.BasketItems
where ph.sellerSKU == newItem.sellerSKU
select ph).SingleOrDefault();
{"Invalid column name 'BasketID'."}
My Classes:
public class Basket
{
[Key]
public string BasketID { get; set; }
public virtual IList<BasketItem> BasketItems { get; set; }
public int? Count { get; set; }
public System.DateTime DateCreated { get; set; }
public Guid UserID { get; set; }
}
public class BasketItem
{
[Key]
public int BasketItemID { get; set; }
public virtual string BasketID { get; set; }
[Required]
public int sellerID { get; set; }
[Required]
public string sellerSKU { get; set; }
[Required]
public int Quantity { get; set; }
[Required]
public decimal Price { get; set; }
}
From the research i have done so far, the error is being cause due to relationships not being mapped properly. How would I map the relationship using modelbuilder
Each basket can(optional) contain many basketitems
Each BasketItem has a BaskedID(FK) to map back to the individual Basket.

Entity Framework DELETE statement conflicted with the REFERENCE constraint

I’m pretty new to EF and I have a little problem.
I just want to delete an item in my database. I’m using SQL Server 2012 Express, VS2012, AdventureWorks 2012.
The query that I execute is the following:
context = new AWEntities();
var removedItem = context.Addresses
.Include("StateProvince")
.Include("SalesOrderHeaders")
.Include("BusinessEntityAddresses").Single(d => d.AddressID == 11);
context.Addresses.Remove(removedItem);
context.SaveChanges();
The error that I get is
The DELETE statement conflicted with the REFERENCE constraint "FK_SalesOrderHeader_Address_ShipToAddressID". The conflict occurred in database "AdventureWorks2012", table "Sales.SalesOrderHeader", column 'ShipToAddressID'.
The statement has been terminated.
Is this actually a good way to delete items and the according entries in the other tables?
Please point me into the right direction.
public partial class Address
{
public Address()
{
this.BusinessEntityAddresses = new HashSet<BusinessEntityAddress>();
this.SalesOrderHeaders = new HashSet<SalesOrderHeader>();
}
public int AddressID { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public int StateProvinceID { get; set; }
public string PostalCode { get; set; }
public System.Data.Spatial.DbGeography SpatialLocation { get; set; }
public System.Guid rowguid { get; set; }
public System.DateTime ModifiedDate { get; set; }
public virtual StateProvince StateProvince { get; set; }
public virtual ICollection<BusinessEntityAddress> BusinessEntityAddresses { get; set; }
public virtual ICollection<SalesOrderHeader> SalesOrderHeaders { get; set; }
}
public partial class StateProvince
{
public StateProvince()
{
this.Addresses = new HashSet<Address>();
this.SalesTaxRates = new HashSet<SalesTaxRate>();
}
public int StateProvinceID { get; set; }
public string StateProvinceCode { get; set; }
public string CountryRegionCode { get; set; }
public bool IsOnlyStateProvinceFlag { get; set; }
public string Name { get; set; }
public int TerritoryID { get; set; }
public System.Guid rowguid { get; set; }
public System.DateTime ModifiedDate { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual CountryRegion CountryRegion { get; set; }
public virtual ICollection<SalesTaxRate> SalesTaxRates { get; set; }
public virtual SalesTerritory SalesTerritory { get; set; }
}
}
public partial class BusinessEntityAddress
{
public int BusinessEntityID { get; set; }
public int AddressID { get; set; }
public int AddressTypeID { get; set; }
public System.Guid rowguid { get; set; }
public System.DateTime ModifiedDate { get; set; }
public virtual Address Address { get; set; }
public virtual AddressType AddressType { get; set; }
public virtual BusinessEntity BusinessEntity { get; set; }
}
public partial class SalesOrderHeader
{
public SalesOrderHeader()
{
this.SalesOrderDetails = new HashSet<SalesOrderDetail>();
this.SalesOrderHeaderSalesReasons = new HashSet<SalesOrderHeaderSalesReason>();
}
public int SalesOrderID { get; set; }
public byte RevisionNumber { get; set; }
public System.DateTime OrderDate { get; set; }
public System.DateTime DueDate { get; set; }
public Nullable<System.DateTime> ShipDate { get; set; }
public byte Status { get; set; }
public bool OnlineOrderFlag { get; set; }
public string SalesOrderNumber { get; set; }
public string PurchaseOrderNumber { get; set; }
public string AccountNumber { get; set; }
public int CustomerID { get; set; }
public Nullable<int> SalesPersonID { get; set; }
public Nullable<int> TerritoryID { get; set; }
public int BillToAddressID { get; set; }
public int ShipToAddressID { get; set; }
public int ShipMethodID { get; set; }
public Nullable<int> CreditCardID { get; set; }
public string CreditCardApprovalCode { get; set; }
public Nullable<int> CurrencyRateID { get; set; }
public decimal SubTotal { get; set; }
public decimal TaxAmt { get; set; }
public decimal Freight { get; set; }
public decimal TotalDue { get; set; }
public string Comment { get; set; }
public System.Guid rowguid { get; set; }
public System.DateTime ModifiedDate { get; set; }
public virtual Address Address { get; set; }
public virtual ShipMethod ShipMethod { get; set; }
public virtual CreditCard CreditCard { get; set; }
public virtual CurrencyRate CurrencyRate { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<SalesOrderDetail> SalesOrderDetails { get; set; }
public virtual SalesPerson SalesPerson { get; set; }
public virtual SalesTerritory SalesTerritory { get; set; }
public virtual ICollection<SalesOrderHeaderSalesReason> SalesOrderHeaderSalesReasons { get; set; }
}
Can't really tell much from what you have said, but you may benefit from looking into using the DbModelBuilder to solve cascade issues:
modelBuilder.Entity<Parent>()
.HasMany<Child>(c => c.Children)
.WithOptional(x => x.Parent)
.WillCascadeOnDelete(true);
Again, would need more information about your model structure to determine if this is the right approach.
Either that or in your delete method, remove any children first, and then remove the parent.
modelBuilder.Entity<Parent>()
.HasMany<Child>(c => c.Children)
.WithOptional(x => x.Parent)
.WillCascadeOnDelete(true);
or use Include
var adv = db.Adv.Include(b => b.Features)
.Include(b => b.AdvDetails)
.Include(b => b.AdvGallery)
.FirstOrDefault(b => b.Id == id);
db.Adv.Remove(adv);
for .HasMany(...).WithMany(...) Include is ok
You can resolve this issue on SQL side
Method 1 :
First, you need to find on which table this FK constraint has been defined, through using Replication monitor.
Right click on that FK, click Modify, you should get popup box like one shown below.
From the popup box, Select Cascade for del.
Method 2 :
set ON DELETE CASCADE in sql at the end of constraint.
In EF Core the syntax in builder is as follows:
builder.HasOne(b => b.Parent )
.WithMany(a => a.Children)
.OnDelete(DeleteBehavior.Cascade);
https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete
I got this error when I created Entity B, that referenced Entity A, and then tried to delete Entity A. SQL/EF did not allow me to leave that dangling Id reference, since the objcet no longer existed. Cascading deletes would solve this, but I wanted B to persist. So I have to remove the reference from B before deleting A:
var existingContractApprovers = _repo.Query<ChangeOrderApproverForContract>().Where(coafc => coafc.ContractId == key).ToList();
//remove refs to contract approvers to preserve data integrity
foreach(var contractApp in existingContractApprovers)
{
var associatedChangeOrderApprovers = _repo.Query<ChangeOrderApprover>().AsNoTracking().Where(coafc => coafc.ChangeOrderApproverForContractId == contractApp.Id).ToList();
foreach(var coApp in associatedChangeOrderApprovers)
{
_repo.Edit(coApp);
coApp.ChangeOrderApproverForContractId = null;
}
}
_repo.SaveChanges();
//remove the contract approvers
foreach (var contractApp in existingContractApprovers)
{
_repo.Delete(contractApp);
}
_repo.SaveChanges();
You can do this in EF Core.
modelBuilder.Entity<Parent>()
.HasMany<Child>(c => c.Children)
.WithOne(s => s.Parent)
.OnDelete(DeleteBehavior.Cascade);
The safer alternative is to make sure all children are deleted before deleting the parent. You should cascade on delete only if you are completely aware of how your entities relate. For example, you could have lots of orders connected to
a certain category in your e-commerce store. Once the category is deleted, all the orders and any entity whose foreign keys are connected to this parent category will be gone.