Eager loading of virtual link being lost when using linq 'select new ...' - c#

I have a problem with my virtual link. Entity framework doesn't seem to eager load in cases where the UserID = 0. I'm using Entity Framework 6 .NET Framework 4.5.2 and VS2013
public class Booking
{
[Key]
public int Booking_ID { get; set; }
public int Customer_ID { get; set; }
public DateTime StartDate_DT { get; set; }
public DateTime BookingDate_DT { get; set; }
public int UserID { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
This does not include any record from the Booking table with a UserID of 0:
var list = (from booking in _db.Booking
where booking.Customer_ID == customerId
orderby booking.StartDate_DT, booking.BookingDate_DT
select new CustomerBookingLine
{
BookingId = b.Booking_ID,
StartDate = b.StartDate_DT,
BookingDate = b.BookingDate_DT,
UserName = b.UserProfile != null ? b.UserProfile.UserName : null
}).ToList();
return list;
This does return the booking records including eager-loaded user profile records
var list = (from b in _db.Booking
where b.Customer_ID == customerId
orderby booking.StartDate_DT, booking.BookingDate_DT
select b).ToList();
return (from b in bookingList
select new CustomerBookingLine
{
BookingId = b.Booking_ID,
StartDate = b.StartDate_DT,
BookingDate = b.BookingDate_DT,
UserName = b.UserProfile != null ? b.UserProfile.UserName : null
}).ToList();
I've tried using includes but the 2nd method is the only way I can get it to work. Can anyone explain why?

Related

LINQ To Entity - Inner Join issue

I have two related tables like below :
Users :
public partial class Users
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Users()
{
}
public int ID { get; set; }
public int UserType_ID { get; set; }
public string Email { get; set; }
public virtual UserTypes UserTypes { get; set; }
}
UserTypes :
public partial class UserTypes
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public UserTypes()
{
this.Users = new HashSet<Users>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Title { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Users> Users { get; set; }
}
For access Name of UserType i wrote this linq to entity :
string[] UserTypes = new string[1];
using (Crypto_Entities entities = new Crypto_Entities())
{
int User_ID_Integer = int.Parse(User_ID.Trim());
var user = (from User in entities.Users
//join UserType in entities.UserTypes on User.UserType_ID equals UserType.ID
where User.ID == User_ID_Integer
select User).FirstOrDefault();
if (user != null)
{
UserTypes[0] = user.UserTypes.Name;
}
}
My question is why user.Name does not work for my purpose and what is the benefit of join in linq to entity?
If i remove join as i did in my query i still can see Name field of UserType with user.UserTypes.Name.
You do not need join if you have defined correctly navigation properties. And if you just need Name, do not retrieve full entity.
string[] UserTypes = new string[1];
using (Crypto_Entities entities = new Crypto_Entities())
{
int User_ID_Integer = int.Parse(User_ID.Trim());
var query =
from User in entities.Users
where User.ID == User_ID_Integer
select User.UserTypes.Name;
var name = query.FirstOrDefault();
if (name != null)
{
UserTypes[0] = name;
}
}
If you use navigation property in query, EF automatically generates all needed joins. But if you just select whole entity without defining Include - EF will not load related data. It makes sense, because otherwise you may load almost whole database if there are a lot of relations.
Since you have set up the relations in your entities you don't need to manually write join to load related data:
var user = entities.Users
.Include(u => u.UserTypes)
.Where(u => u.ID == User_ID_Integer)
.FirstOrDefault();
As for your join being useless - EF Core translates the code into actual SQL (which you can check) and since you are not selecting any data from the joined table - it is as useless as it would be in SQL query where you have selected fields only from one table of join result.

Comparing two table IDs. ASP.NET MVC

I am currently loading two Orders and Colors tables, I wanted the Colors table to list the items that have the ID equal to Orders. For this, what occurred to me was to assign the IdOrders values ​​to a variable and compare it with my IdOrders (in my table Colors), but it is not possible to assign the database's balance to my variable
My tables:
public partial class Orders
{
public int ID_Orders { get; set; }
public Nullable<System.DateTime> Data_Registo { get; set; }
public string Num_Encomenda { get; set; }
public string Ref_Cliente { get; set; }
}
public partial class Colors
{
public int ID_Orders { get; set; }
public int ID_Programa_Malha { get; set; }
public int ID_Linha_Cor { get; set; }
public string Cor { get; set; }
}
I am working with a database already in operation and possible these tables are already used in a sql join but not how to process that information.
As I said the first thing I remembered was to do this:
My Controller:
var id = from d in db.Orders
select d.ID_Orders;
var color = db.Colors.Where(x => x.ID_Orders = id).ToList();
var tables = new EncomendaViewModel
{
Orders= db.Orders.ToList(),
Colors= color.ToList(),
};
return View(tables);
Error in id: CS0029 C# Cannot implicitly convert type to 'int'
Is it possible to process the data in this way?
Thanks for anyone who can help!
-------------------(Update)------------------------------------------------
Using == cs0019 operator '==' cannot be applied to operands of type
My view in Broswer
dbEntities sd = new dbEntities();
List<Orders> orders= sd.Orders.ToList();
List<Colors> colers= sd.Colors.ToList();
var multipletable = from c in orders
join st in colers on c.ID_Programa equals st.ID_Programa into table1
from st in table1.DefaultIfEmpty()
select new MultipleClass { orders= c, colers= st };
There could be one or more values returned from the below query.
var id = from d in db.Orders
select d.ID_Orders;
That is the reason why it was throwing an error.
So lets try it this way
var color = db.Colors.Where(x => id.Contains(x.ID_Orders)).ToList();
public class OrderWithColorsViewModel
{
public Order order { get; set; }
public List<Colors> colers{ get; set; }
}
Public class TestOrderController : Controller
{
public DailyMVCDemoContext db = new DailyMVCDemoContext();
public ActionResult Index()
{
var orders= db.Orders.ToList();
var colers = db.Colors.ToList();
var result = (from c in orders
join st in colers on c.ID_Orders equals st.id into table1
select new OrderWithColorsViewModel { order =c, colers =
table1.ToList() }).ToList();
return View(result);
}
}
credits: YihuiSun

Sql query combining results

I have the following:
void Main()
{
// I need to get the below query, but with the newest comment date as well of each post.
// Gives me all the posts by the member
var member_id = 1139;
var query = (from posts in Ctm_Forum_Posts
join category in Ctm_Forum_Categories on posts.FK_Categori_ID equals category.Id
where posts.Archieved == false
&& posts.Deleted == false
&& posts.FK_Member_ID == member_id
select new ForumPostModel(){
Id = posts.Id,
BodyText = posts.BodyText,
Summary = posts.Summary,
Title = posts.Title,
Created = posts.Created,
Updated = posts.Updated,
CategoryName = category.Title,
});
// this gives the newest comment date (registration_timestamp) from a specific post with id = 1
var NewestCommentDate = (from comments in Ctm_Comments
join posts in Ctm_Forum_Posts on comments.Page_ID equals posts.Id
where posts.Id == 1 && comments.Deleted == false
orderby comments.Reqistration_timestamp descending
select comments.Reqistration_timestamp).FirstOrDefault();
}
// Model of the result.
public class ForumPostModel{
public int Id { get; set; }
public string Title { get; set; }
public string BodyText { get; set; }
public string Summary { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string CategoryName { get; set; }
public DateTime LatestCommentTime { get; set; }
}
Right now the two queries work, but my problem is the performance sucks,, so im trying to combine the results into a single query, so my load time will be much less.
I tried searching about unions, but have been unable to figure it out.

EF Core error: "Comparison on entity type is not supported because it is a keyless entity" when doing outer join

I have two tables. One for invoices and one for payments.
The invoice table has the schema like this.
public class Invoice
{
public Guid ID {get;set;}
public string InvoiceNum {get;set;}
public decimal Amount {get;set;}
}
The payment table has the schema like this.
public class Payment
{
public Guid ID {get;set;}
public InvoiceID {get;set;}
public decimal PaidAmount {get;set;}
public DateTimeOffset? VoidDate {get;set;}
public virtual Invoice Invoice { get; set; }
}
I want to get all invoices which have not been fully paid. Firstly I declared an entity called PaymentStatus.
public class PaymentStatus
{
public Guid InvoiceID {get;set;}
public decimal TotalPaidAmount {get;set;}
}
Then in my DbContext class, I have
public virtual DbSet<Invoice> Invoices { get; set; }
public virtual DbSet<PaymentStatus> PaymentStatuses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PaymentStatus>().ToQuery(() =>
Payment.Where(r => r.VoidDate == null)
.GroupBy(r => r.InvoiceID)
.Select(r => new PaymentStatus{ InvoiceID = r.Key, TotalPaidAmount = r.Sum(p => p.PaidAmount) }))
.HasNoKey()
.HasOne(r=>r.Invoice)
.WithOne().HasForeignKey<PaymentStatus>(r=>r.InvoiceID);
}
Then I tried to get the list of unpaid invoices by outer joining Invoices and PaymentStatuses.
public Task<List<Receivables>> GetReceivables()
{
var qry = from inv in MyDbContext.Invoices
from pay in MyDbContext.PaymentStatuses.Where(p => p.InvoiceID == inv.ID).DefaultIfEmpty()
select new Receivables
{
InvoiceID = inv.ID,
InvoiceAmount = inv.Amount,
PaidAmount = pay==null ? 0 : pay.TotalPaidAmount, --here causes the error
};
return qry.Where(r=>r.InvoiceAmount > r.PaidAmount ).ToListAsync();
}
When the above codes execute, I gets the error
System.InvalidOperationException: Comparison on entity type 'PaymentStatus' is not supported because it is a keyless entity.
The error is caused by the line
PaidAmount = pay==null ? 0 : pay.TotalPaidAmount
If it is changed to
PaidAmount = pay.TotalPaidAmount
Then it works in UnitTest (where InMemory Database is used) but failed in SQL Server because the translated SQL is filtering records using
WHERE (inv.Amount > pay.PaidAmount)
When an invoice is not paid, then PaymentStatuses has no corresponding record for the invoice and so pay.PaidAmount is Null (not zero). inv.Amount > Null is not satisfied.
I'm using EntityFrameworkCore 3.1.5
What I do not understand is why
pay==null ? 0 : pay.TotalPaidAmount
throwing error and how can it be solved
I solved the problem by making TotalPaidAmount nullable in PaymentStatus
public class PaymentStatus
{
public Guid InvoiceID {get;set;}
public decimal? TotalPaidAmount {get;set;}
}
Then change the query in getting Receivables
public Task<List<Receivables>> GetReceivables()
{
var qry = from inv in MyDbContext.Invoices
from pay in MyDbContext.PaymentStatuses.Where(p => p.InvoiceID == inv.ID).DefaultIfEmpty()
select new Receivables
{
InvoiceID = inv.ID,
InvoiceAmount = inv.Amount,
PaidAmount = pay.TotalPaidAmount ?? 0,
};
return qry.Where(r=>r.InvoiceAmount > r.PaidAmount ).ToListAsync();
}

OrmLite Selecting Multiple Columns Across Joined Tables

I'm having some difficulty populating some columns in a POCO using OrmLite. I have three tables named Dog, Bowl and DogBowl. DogBowl is a junction table and holds the id of Dog and Bowl.
Dogs
PK Id: int, not null
Breed: varchar(20), not null
Name: varchar(20), not null
Bowls
PK Id: int, not null
Type: varchar(20), not null
Color: varchar(20), not null
Dogs_Bowls
PK: DogId, not null
PK: BowlId, not null
Here are the POCOs I have mapped
public class Dog : IHasId<int>
{
[AutoIncrement]
public int Id { get; set; }
[Required]
public string Breed { get; set; }
[Required]
public string Name { get; set; }
}
public class Bowl : IHasId<int>
{
[AutoIncrement]
public int Id { get; set; }
[Required]
public string Type { get; set; }
[Required]
public string Color { get; set; }
}
public class DogBowl
{
[Required]
public int DogId { get; set; }
[Required]
public int BowlId { get; set; }
[Ignore]
public string DogName { get;set; }
[Ignore]
public string BowlColor { get;set; }
}
This is the c# code I'm running.
var dogBowl = db.Select<DogBowl>(db
.From<Dog>()
.Join<Dog, DogBowl>((d, db) => d.Id == db.DogId)
.Join<DogBowl, Bowl>((db, b) => db.BowlId == b.Id)
.Where<Dog>(d => d.Id == 5))
.ToList();
The SQL I would like to produce is this:
select
db.DogId,
db.BowlId,
d.Name AS DogName,
b.Color as BowlColor
from DogBowl db
join dog d on db.DogId = d.Id
join bowl b on db.BowlId = b.Id
where d.Id = 5
My problem is that the DogBowl.DogName and DogBowl.BowlColor properties are null after the code executes. I'm using the instructions provided on https://github.com/ServiceStack/ServiceStack.OrmLite from the section entitled "Selecting multiple columns across joined tables" but it's not working. How can I get the DogBowl.DogName and DogBowl.BowlColor properties populated?
The SQL generated may be correct. You can verify the generated SQL after execution by checking the property db.GetLastSql().
The problem is that by assigning the result as
db.Select<DogBowl>
, you are creating a List of DogBowl objects. The DogBowl properties DogName and BowlColor would always be null because there is no field in the SQL statement which matches those names exactly. OrmLite will not magically figure out what goes there - you have to have them match by name.
If you want to assign the result to a "flat" object with fields from Dog and Bowl, you could define a new DTO and assign the result, like so:
public class FullDogBowl
{
public int DogId { get; set; }
public int BowlId { get; set; }
public string Breed { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Color { get; set; }
}
var dogBowl = db.Select<FullDogBowl>(db
.From<Dog>()
.Join<Dog, DogBowl>((d, db) => d.Id == db.DogId)
.Join<DogBowl, Bowl>((db, b) => db.BowlId == b.Id)
.Where<Dog>(d => d.Id == 5))
.ToList();
Alternatively, if you know exactly the SQL you want to use, just use it:
string sql = #"select
db.DogId,
db.BowlId,
d.Name AS DogName,
b.Color as BowlColor
from DogBowl db
join dog d on db.DogId = d.Id
join bowl b on db.BowlId = b.Id
where d.Id = #dog_id ";
var dogBowlList = db.SqlList<DogBowl>(sql, new { dog_id = 5, });
Wanted to add to Raul's answer that [Ignore] tells OrmLite to ignore the property completely so your approach of re-using a table as the merged POCO "view" wont work. I recommend instead splitting the resultset POCO out into a separate POCO with all the fields you want returned:
public class DogBowl
{
[Required]
public int DogId { get; set; }
[Required]
public int BowlId { get; set; }
}
public class DogBowlInfo
{
public int DogId { get; set; }
public int BowlId { get; set; }
public string DogName { get; set; }
public string BowlColor { get; set; }
}
Which now returns a populated resultset with:
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<Dog>();
db.DropAndCreateTable<Bowl>();
db.DropAndCreateTable<DogBowl>();
var dog = new Dog { Breed = "Breed", Name = "Name" };
var bowl = new Bowl { Color = "Color", Type = "Type" };
db.Save(dog);
db.Save(bowl);
db.Insert(new DogBowl { DogId = dog.Id, BowlId = bowl.Id });
var dogBowl = db.Select<DogBowlInfo>(
db.From<Dog>()
.Join<Dog, DogBowl>((d, b) => d.Id == b.DogId)
.Join<DogBowl, Bowl>((d, b) => d.BowlId == b.Id)
.Where<Dog>(d => d.Id == dog.Id));
dogBowl.PrintDump();
}

Categories

Resources