How to include related table column names in dynamic linq expressions? - entity-framework

I am using a dynamic linq expressions to filter my records. All works well, if i give the column names which are there in the model. But when i try to give the column name in related table, it breaks. Can anyone please help here
Model
public class User
{
public int UserID { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public UserRole Role { get; set; }
public int RoleID { get; set; }
}
public class UserRole
{
public int RoleID { get; set; }
public string RoleName { get; set; } = String.Empty;
}
ParameterExpression param = Expression.Parameter(typeof(User), "t");
con = Expression.Equal(Expression.Property(param, "Role.RoleName"), Expression.Constant("Admin"));
var deleg = Expression.Lambda<Func<User, bool>>(con, param);
var users= context.UserModel.Include(r => r.Role).Where(deleg).AsQueryable();

Related

EF Core 6: Reading two tables using navigations doesn't load child table data

The role property of a relation record doesn't get read/initialized (i.e. the data from the included table doesn't get read). Why is that? The data is in the database.
public class IDM_Role
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long role_id { get; set; } = -1;
[StringLength(80)]
public string role_name { get; set; } = string.Empty;
}
public class IDM_Relation
{
[Required]
[StringLength(80)]
public string account_id { get; set; } = string.Empty;
[Required]
[ForeignKey("IDM_Role")]
public long role_id { get; set; } = 0;
[ForeignKey("role_id")]
public virtual IDM_Role role { get; set; }
}
modelBuilder.Entity<IDM_Relation>()
.HasKey(e => new { e.role_id, e.account_id })
.HasName("PK_IDM_Relation");
[EnableQuery(PageSize = 15)]
public IQueryable<IDM_Relation> Get()
{
return _context.idm_relations.Include(e => e.role);
}
Removing "virtual" from the property IDM_Role role doesn't help.

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.

Join multiple one to many related tables in EF and select as view model

Database Models of my Application are:
public class Restaurant
{
public int Id { get; set; }
.........
}
public class Review
{
public int Id { get; set; }
public string ReviewTitle { get; set; }
public string ReviewContent { get; set; }
public int UserId { get; set; }
public int RestaurantId { get; set; }
}
public ReviewHelpful
{
public int Id { get; set; }
public int ReviewId { get; set; }
public bool IsHelpfull { get; set; }
}
public ReviewImage
{
public int Id { get; set; }
public string ImageLink { get; set; }
public int ReviewId { get; set; }
}
There is no navigation property in any table. In ReviewHelpful table, If user finds helpfull of this review than value is true otherwise false.
Now I want to create a view-model Like this:
public class ReviewViewModel
{
public int ReviewId { get; set; }
public int RestaurantId { get; set; }
public string ReviewTitle { get; set; }
public string ReviewContent { get; set; }
public int UserId { get; set; }
public int NumberOfHelpfull { get; set; }
public int NumberOfNotHelpfull { get; set; }
public List<string> ImagesLinks { get; set; }
}
For that reason, I want to write this kind of query :
var reviews = (from review in _foodTrackerContext.RestaurantReviews
join helpful in _foodTrackerContext.Helpfuls on review.Id equals helpful.ReviewId
join reviewPicture in _foodTrackerContext.ReviewPictures on review.Id equals reviewPicture.ReviewId
where review.ResturantId == 2
select new ReviewViewModel()
{
Id = review.Id,
RestaurantId = 2,
ReviewTitle = review.ReviewTitle,
ReviewContent = review.ReviewContent,
NumberOfHelpfull = .. ??,
NumberOfNotHelpfull = ... ??,
ImagesLinks = ... ???
}
I can not retrieve HelpfulYes, HelpfulNo, ImagesLinks with this query. What would be query for finding these variables?.
This query produces multiple rows for single review with each ReviewImage and each ReviewHelpful.
The query that ypu need to do is this one:
var model =
from review in ctx.Reviews
where review.RestaurantId == 2
join helpful in ctx.ReviewHelpfuls
on review.Id equals helpful.ReviewId into helpfuls
join image in ctx.ReviewImages
on review.Id equals image.ReviewId into images
select new RestaurantReviewViewModel
{
Id = review.Id,
RestaurantId = 2,
ReviewTitle = review.ReviewTitle,
ReviewContent = review.ReviewContent,
NumberOfHelpfull = helpfuls.Count(h => h.IsHelpfull),
NumberOfNotHelpfull = helpfuls.Count(h => !h.IsHelpfull),
ImagesLinks = (from image in images select image.ImageLink).ToList()
};
Please, note that when you do a one to manyh join you need to include an into to give a nameto the joined entities to be able to work on them.
I've used the dot syntax for selecting the count, but you could use the query syntax if you wanted. Over time, I've found dot synatx more natural.
NOTE: if you used navigation properties this would become much easier. Why are you not using them? With navigation properties you don't need to make the joins explicitly, as they are already available.
List<ReviewViewModel> listModel = new List<ReviewViewModel>();
context.dbRestaurant
.include("Review")
.include("Review.ReviewHelpful")
.include("Review.ReviewImage").ToList().ForEach((item) =>
{
ReviewViewModel model = new ReviewViewModel();
model.ID = item.ID
listModel.Add(model);
});

Error:The specified cast from a materialized 'System.TimeSpan' type to the 'System.DateTime' type is not valid

I am doing my application in MVC. I am using stored procedure to list some
values. I calling that procedure in my controller.
My Procedure is
ALTER procedure [dbo].[spGetWaitingList]
#BusinessUserId int
As
Begin
SELECT WorkHour.WorkHourId, WorkHour.EndTime, WorkHour.Description, tblApp.EndDate
FROM WorkHour INNER JOIN
tblApp ON WorkHour.BusinessUserId = tblApp.UserId2 and WorkHour.EndTime < Cast (tblApp.EndDate as Time)
End
My controllercode is
var parameters = new SqlParameter[1];
parameters[0] = new SqlParameter { ParameterName = "BusinessUserId", Value = id };
List<Appt> wt = new List<Appt>();
using (SYTEntities context = new SYTEntities())
{
wt = context.Database.SqlQuery<Appt>("exec spGetWaitingList #BusinessUserId", parameters).ToList();
}
My model Appt is
public class Appt
{
public int BusinessID { get; set; }
public string FirstName { get; set; }
public string BusinessName { get; set; }
public string BusinessCategory { get; set; }
public int UserID { get; set; }
public int UserTypeID { get; set; }
public int Id { get; set; }
public TimeSpan StartTime { get; set; }
public TimeSpan EndTime { get; set; }
public string AddCustomer { get; set; }
public string EndDate { get; set; }
public DateTime HolidayDate { get; set; }
public string HolidayDesc { get; set; }
}
In Database I specified the end time as datatype TIME
While running the application am getting an error like this
Can anyone please help me to solve this?
Thanks in advance.

Entity framework join 2 tables

Beginner with entity framework and mvc here.
I have 2 table models:
UserProfile
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
and
ChatLogs
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
public override string ToString()
{
return "Person: " + Message + " " + UserId;
}
}
UserId in table ChatLogs is foreign key to UserPorfile UserId primary key.
I am trying to join these 2 tables in Asp.NET MVC 4
Tested SQL query:
select * from UserProfile as a join ChatLogs as b on a.UserId = b.UserId
Tested Linq query:
from b in db.ChatLogs
select new {
ChatLogId = b.ChatLogId,
Message = b.Message,
UserId = b.UserId,
Column1 = (int?)b.UserProfile.UserId,
UserName = b.UserProfile.UserName,
Email = b.UserProfile.Email
}
I am using software called "Linqer" for learning purposes. It conversts SQL to Linq.
ActionResult code:
private ChatLogContext db = new ChatLogContext();
public ActionResult Index()
{
var list = from b in db.ChatLogs
select new
{
ChatLogId = b.ChatLogId,
Message = b.Message,
UserId = b.UserId,
Column1 = (int?)b.UserProfile.UserId,
UserName = b.UserProfile.UserName,
Email = b.UserProfile.Email
};
var vm = new ChatLogsViewModel { LogListString = string.Join("\n", list) };
return View(vm);
}
ChatLogViewModel has only a string variable for printing list in view.
But I get an error:
'Chat.Models.ChatLogs' does not contain a definition for 'UserProfile' and no extension method 'UserProfile' accepting a first argument of type 'Chat.Models.ChatLogs' could be found (are you missing a using directive or an assembly reference?)
So do I have to connect those 2 entities somehow so they would know about each other?
Try this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
**public virtual ICollection<ChatLogs> ChatLogs { get; set; }**
}
[Table("ChatLogs")]
public class ChatLogs
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ChatLogId { get; set; }
[Column("Message")]
public string Message { get; set; }
[Column("UserId")]
public int UserId { get; set; }
**public virtual UserProfile UserProfile { get;set; }**
public override string ToString()
{
return "Person: " + Message + " " + UserId;
}
}
The easiest way to connect is to make Chatlogs available on the user as a List:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public List<ChatLog> ChatLogs{ get; set;}
}
Now you can do the following:
var users = Users.Include("ChatLogs");
Every user will now have its list of ChatLogs filled in correctly.