Lambda Query
I am working with with EF6 MVC5 and set up my listing model to include a virtual ICollection of images. I want to query the listings and include an image with each listing title in my view. I am having trouble writing the lambda query in the controller to send to my view.
Listing Class
public class Listing
{
public int Id { get; set; }
public ApplicationUser User;
[Required]
public string UserId { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Website { get; set; }
public string Phone { get; set; }
public string LocationAddress1 { get; set; }
public string LocationAddress2 { get; set; }
public string LocationCity { get; set; }
public string LocationState { get; set; }
public string LocationZip { get; set; }
public string LocationCountry { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
[DataType(DataType.MultilineText)]
public string Comments { get; set; }
public bool isLEO { get; set; }
public bool isProperty { get; set; }
public string Keywords { get; set; }
public bool isNoShowAddress { get; set; }
public bool isActive { get; set; }
public bool isApproved { get; set; }
public virtual ICollection<Image> Images { get; set; }
public virtual ICollection<Review> Reviews { get; set; }
Image Class
public class Image
{
public int Id { get; set; }
public Listing Listing { get; set; }
public int ListingId { get; set; }
public string ImagePath { get; set; }
public bool isPrimary { get; set; }
public DateTime DateAdded { get; set; }
public bool isHidden { get; set; }
}
When you have nested objects I believe you do something like this:
var listings = dbcontext.Listings.Include("Images").Where(...)
Where include tells EF to fetch nested objects.
It could also be something like this:
var listings = dbcontext.Listings.Include(c=>c.Images).Where(...)
You have to project your query to another type specifically designed for use inside Views (this is often referred to as ViewModel) or you can just project it into an anonymous type, the approach is similar.
The following code assumes you will use an anonymous type:
var listings = dbContext.Listings.Select(l => new {
Id = l.Id,
User = l.User,
//add every property you need to show inside your View
Image = l.Images.FirstOrDefault(i => i.isPrimary)
});
Related
I'm building a feature with a jquery datatable, the idea is to have a list of stores in the parent row, and then when expanding the parent to list all the licensed terminals in child rows that are linked to the store parent row by a StoreLicenseId column. The issue I am having is that I have a ViewModel with two models, one for the list of stores and one for the licensed terminals. I'm busy building the method into my controller, my problem is in the second part of the method where I new up "StoreLicenseDetails = sl.Select(tl => new TerminalListViewModel()", all the references to tl.terminalId and tl.Terminalname. I get this error "StoreListViewModel does not contain a definition for TerminalID and no accessible extension method". I can see why this is happening, so my question really is, how do I include this "second" TerminalListViewModel into my method to form part of the query ?
ViewModel
public partial class StoreListViewModel
{
public List<TerminalListViewModel> StoreLicenseDetails { get; set; } = null!;
public int Id { get; set; }
public Guid StoreLicenseId { get; set; }
[DisplayName("Store Name")]
public string StoreName { get; set; } = null!;
[DisplayName("App One Licenses")]
public int QtyAppOneLicenses { get; set; }
[DisplayName("App Two Licenses")]
public int QtyAppTwoLicenses { get; set; }
[DisplayName("Date Licensed")]
public DateTime DateLicensed { get; set; }
[DisplayName("Licensed Days")]
public int LicenseDays { get; set; }
[DisplayName("Is License Active")]
public bool LicenseIsActive { get; set; }
}
public partial class TerminalListViewModel
{
public int Id { get; set; }
public Guid StoreLicenseId { get; set; }
public Guid TerminalId { get; set; }
public string TerminalName { get; set; } = null!;
public string LicenseType { get; set; } = null!;
public int TerminalLicenseDays { get; set; }
public DateTime DateLicensed { get; set; }
public bool LicenseIsActive { get; set; }
public bool IsDecommissioned { get; set; }
public DateTime LastLicenseCheck { get; set; }
}
Controller Method
//sl = StoreList
//tl = TerminalList
public IEnumerable<StoreListViewModel> GetStoreList()
{
return GetStoreList().GroupBy(sl => new { sl.StoreLicenseId, sl.StoreName, sl.QtyAppOneLicenses,
sl.QtyAppTwoLicenses, sl.DateLicensed, sl.LicenseDays,
sl.LicenseIsActive })
.Select(sl => new StoreListViewModel()
{
StoreName = sl.Key.StoreName,
QtyAppOneLicenses = sl.Key.QtyAppOneLicenses,
QtyAppTwoLicenses = sl.Key.QtyAppTwoLicenses,
DateLicensed = sl.Key.DateLicensed,
LicenseDays = sl.Key.LicenseDays,
LicenseIsActive = sl.Key.LicenseIsActive,
StoreLicenseId = sl.FirstOrDefault().StoreLicenseId,
StoreLicenseDetails = sl.Select(tl => new TerminalListViewModel()
{
StoreLicenseId = tl.StoreLicenseId,
TerminalId = tl.TerminalId,
TerminalName = tl.TerminalName,
}).ToList()
}).ToList();
}
Based on the error,I suppose your GetStoreList() method returns List<OrderListViewModel> ,but your OrderListViewModel doesn't contains properties of TerminalListViewModel,So you got the error
GetStoreList() method should return List<SourceModel>( Source is the model which contains all the properties of StoreListViewModel and TerminalListViewModel)
For example,the link your provided:Multiple child rows in datatable, data from sql server in asp.net core
public class OrderList
{
//source of properties of OrderListViewModel(parent rows)
public int OrderId { get; set; }
public string Customer { get; set; }
public string OrderDate { get; set; }
//source of properties of OrderListDetailViewModel(child rows)
public int KimlikId { get; set; }
public string Product { get; set; }
public string Color { get; set; }
public int Qntty { get; set; }
}
public class OrderListViewModel
{
public int OrderId { get; set; }
public string Customer { get; set; }
public string OrderDate { get; set; }
public List<OrderListDetailViewModel> OrderListDetails { get; set; }
}
public class OrderListDetailViewModel
{
public int KimlikId { get; set; }
public string Product { get; set; }
public string Color { get; set; }
public int Qntty { get; set; }
}
Orderlist contains all columns OrderListViewModel and OrderListDetailViewModel needs.
When it comes to your case,you should
create 3 models (source,parentrow,childrows)
model for parentrows contains the properties
StoreLicenseId,StoreName, QtyAppOneLicenses,QtyAppTwoLicenses, DateLicensed, LicenseDays,LicenseIsActive
and model for childrows contains the other properties of source model
If you still have questions,please show the data you pulled form db,and I'll write a demo for you
I have three related Entities in my blazor application Opportunity, AppUser and AssignedOpportunity, What I want to achieve is to map Opportunity and AppUser to a DTO Object ReturnAssignedOpportunityDTO which has similar fields as the entities, using AutoMapper, but am not sure how to do that, below are the entities
public partial class AssignedOpportunity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey("OpportunityID")]
public string OpportunityID { get; set; }
public string Status { get; set; }
public Opportunity opportunity { get; set; }
[ForeignKey("UserID")]
public string UserID { get; set; }
public AppUser User { get; set; }
}
The opportunity
public partial class Opportunity
{
public Opportunity()
{
AssignedOpportunities= new HashSet<AssignedOpportunity>();
}
[Key]
public string ID { get; set; }
public string OpportunityName { get; set; }
public string Description { get; set; }
public string CreatedBy { get; set; }
public DateTime DateCreated { get; set; }
public double EstimatedValue { get; set; }
public string EmployeeNeed { get; set; }
public double RealValue { get; set; }
public string Location { get; set; }
public string ReasonStatus { get; set; }
public string Status { get; set; }
public virtual ICollection<AssignedOpportunity> AssignedOpportunities { get; set; }
}
AppUser Class
public partial class AppUser : IdentityUser
{
public AppUser()
{
AssignedOpportunities = new HashSet<AssignedOpportunity>();
}
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string LGA { get; set; }
public string State { get; set; }
public virtual ICollection<AssignedOpportunity> AssignedOpportunities { get; set; }
}
Here's the DTO Object I want to map to.
public class ReturnOpportunitiesDTO
{
public int ID { get; set; }
public string OpportunityID { get; set; }
public string OpportunityName { get; set; }
public double EstimatedValue { get; set; }
public string EmployeeNeed { get; set; }
public double RealValue { get; set; }
public string Location { get; set; }
public string UserID { get; set; }
public string UserFullName { get; set; }
public string Status { get; set; }
}
Here is my query to fetch the records
var result = await _context.AssignedOpportunities.Include(o => o.opportunity).
ThenInclude(a => a.User).
Where(a=>a.UserID==UserID.ToString()).ToListAsync();
return result;
This is how i usually setup Map Profile
public AssignArtisanProfile()
{
CreateMap<AssignedOpportunity, ReturnOpportunities>();
}
But since I want to map multiple entities, how do I include the other entity
Your scenario is just another example of flattening a complex object. You have properties in child objects, which you want to bring to the ground level, while still leveraging AutoMapper mapping capabilities. If only you could reuse other maps from app user and opportunity when mapping from assigned opportunity to the DTO... Well, there is a method called IncludeMembers() (see the docs) that exists precisely for such case. It allows you to reuse the configuration in the existing maps for the child types:
config.CreateMap<AssignedOpportunity, ReturnOpportunitiesDTO>()
.IncludeMembers(source => source.opportunity, source => source.User);
config.CreateMap<Opportunity, ReturnOpportunitiesDTO>();
config.CreateMap<AppUser, ReturnOpportunitiesDTO>()
.ForMember(
dest => dest.UserFullName,
options => options.MapFrom(source =>
string.Join(
" ",
source.FirstName,
source.MiddleName,
source.LastName)));
Usage:
var mappedDtos = mapper.Map<List<ReturnOpportunitiesDTO>>(assignedOpportuniesFromDatabase);
In simple relational terms, I want each entry of ContractDetails to be assigned to either a Site OR a Company, not both at the same time, and one of them must be selected or there is no link at all. I'm not quite sure how to represent this in entity framework. My Model at Present:
Company Model:
public class Company
{
public int ID { get; set; }
public string Company_Name { get; set; }
public string Company_Prefix { get; set; }
public virtual ICollection<Site> Sites { get; set; }
}
Contract Details Model:
public class ContractDetails
{
public int ContractDetailsID { get; set; }
public int ContractTypeID { get; set; }
public int ContractRenewalPeriodID { get; set; }
public int? CompanyID { get; set; }
public int? SiteID { get; set; }
[Required, StringLength(10)]
public string Reference { get; set; }
[Display(Name = "Contract Start Date"), DataType(DataType.Date)]
public DateTime? Contract_Start_Date { get; set; }
[Display(Name = "Contract End Date"), DataType(DataType.Date)]
public DateTime? Contract_End_Date { get; set; }
[Column(TypeName = "text")]
public string Notes { get; set; }
public string Direct_Debit_Reference { get; set; }
public virtual Company Company { get; set; }
public virtual Site Site { get; set; }
public virtual ContractType ContractType { get; set; }
public virtual ContractRenewalPeriod ContractRenewalPeriod { get; set; }
}
Site Model:
public class Site
{
public int ID { get; set; }
public string Site_Name { get; set; }
public string Site_TelephoneNumber { get; set; }
public string Site_City { get; set; }
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
}
Just implement the IValidatableObject on ContractDetails class. On Validate method put the validation logic. If the object is not valid you must return a collection of ValidationResult. when saving the object, EF will execute the Validate method and verify that your ContractDetails object is coherent.
I work with EF for the first time so I don't know is situation like this normal or I have serious performance issues.
I have following situation:
Bellow are the classes that I have. Item is the main object here. So when I pull a list of Items from database I get for example 1000 items. And now each of this item has all properties filed with data. City contains Country, Country contains list of cities, User has list of created items, each item all data again, city, city has country, country list of cities etc etc...
Maybe I am worrying too much and I don't know should this object's contain all those data and does this make performance issues, or I am doing something wrong here?
public abstract class Item
{
[Key]
public int ItemId { get; set; }
public int ItemTypeId { get; set; }
public Guid UserId { get; set; }
public DateTime CreatedOnDate { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int? MediaId { get; set; }
public int CityId { get; set; }
public virtual City City { get; set; }
public virtual User User { get; set; }
public virtual ICollection<ItemInBoard> ItemsInBoard { get; set; }
public virtual ICollection<Like> Likes { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
public double Longitude { get; set; }
public double Latitude { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
}
public class Country
{
public int CountryId { get; set; }
public string Name { get; set; }
public string CountryCode { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
public class User
{
public Guid UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Gender { get; set; }
public DateTime? BirthDay { get; set; }
public string AboutMe { get; set; }
public int? MediaId { get; set; }
public int CityId { get; set; }
public virtual City City { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Board> Boards { get; set; }
public virtual ICollection<Like> Likes { get; set; }
}
It is up to you. This is a concept called lazy loading. You can enable or disable lazy loading with this code:
context.Configuration.LazyLoadingEnabled = false;
context.Configuration.LazyLoadingEnabled = true;
When enabling this option none of the dependent entities will be loaded. To enforce dependent entities to load you can use the Include lambada expression like this:
var test = context.Tests.Include("SomeOtherDependentEntity");
Hope I got you and this is what you meant.
I would say that what you have is fine for general business logic.
When I have to do a lot of time sensitive processing in a read-only fashion I use SQL commands like this to get exactly and only exactly what I want.
public class myQueryClass
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
var context = new MyDbContext();
context.Database.SqlQuery<myQueryClass>("SELECT Property1 = acolumn, Property2 = acolumn2 FROM myTable WHERE something = somestate");
I want to create my first application using the EF 4.1 Code First Model. I want to model a magazine subscription but just want to check that my POCO classes are fit for purpose.
The following are my classes. Am I missing anything?
Should Customer be a member of Subscription or should it be just that List be a member of Customer?
Thanks.
public class Subscription
{
public int SubscriptionID { get; set; }
public string CardNumber { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public decimal NetPrice { get; set; }
public decimal Tax { get; set; }
public decimal Discount { get; set; }
public string PromotionalCode { get; set; }
public Customer Customer{ get; set; }
}
public class Customer {
public int CustomerID { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public PostalAddress PostalAddress { get; set; }
public string EmailAddress { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public DateTime DateOfBirth { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string SecurityQuestion { get; set; }
public string SecurityAnswer { get; set; }
public bool Valid { get; set; }
public IList<Subscription> Subscriptions { get; set; }
public bool MailMarketing { get; set; }
public bool PartnerMailMarketing { get; set; }
}
public class PostalAddress
{
public int ID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
public string Region { get; set; }
public string Country { get; set; }
}
If a Customer have X suscriptions, every suscription should have the customerID so you need that in your Suscription class (public int CustomerID {get; set;}).
OTOH, I think that you have to put that Customer reference as virtual and the suscription one (I don't know why).
Maybe Im wrong but that works for me.
Anyway, what's your problem?
Your models look correct. You don't necessarily need the Customer property in the Subscription class, but it does help if you want to retrieve a specific Subscription and then want to find the customer that is tied to that subscription. In that instance you can then do var customer = mySub.Customer instead of querying for a customer with the specific subscription id.