Entity Framework get all data from table with foreign key - entity-framework

Please help. I can not resolve the issue - the category name is null.
Thanks a lot.
The models
public class Product
{
[Key]
public int ProductID { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public int CategoryID { get; set; }
[ForeignKey("CategoryID")]
public virtual Category Category { get; set; }
}
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CategoryName { get; set; }
}
`
Product Controller
public async Task<IEnumerable<ProductDto>> GetProductsAsync()
{
List<Product> products = await _db.Products
.Include(u => u.Category)
.ToListAsync();
return _mapper.Map<List<ProductDto>>(products);
}
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<ProductDto, Product>().ReverseMap();
}
}
public class ProductDto
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public string CategoryName { get; set; }
}
How to resolve null fields?

Change your MappingProfiles like below:
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Product, ProductDto>()
.ForMember(d => d.CategoryName, a => a.MapFrom(s => s.Category.CategoryName))
.ReverseMap()
.ForPath(b => b.Category, o => o.MapFrom(dto => (Category)null));
}
}

Related

Defining the one to many relationship in OnModelCreating using Entity Framework Core 3.1

I am new to Entity Framework Core 3.1 and trying to define the one-to-many relationship between two tables. I am currently struggling and getting compilation errors. Could somebody tell me what the problem could be.
The error is:
PersonNote does not contain the definition for PersonNote
I am currently getting is at line
entity.HasOne(d => d.PersonNote)
How else could I define one-to-many relationship?
The two tables are Person and PersonNote. One Person can have many PersonNotes. I have defined the models for them
public class Person
{
public int Id { get; set; }
public int? TitleId { get; set; }
public string FirstName { get; set; }
public string FirstNamePref { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime? DateOfBirth { get; set; }
public string Gender { get; set; }
public int AddressId { get; set; }
public string TelephoneNumber { get; set; }
public string MobileNumber { get; set; }
public string Email { get; set; }
public int? PartnerId { get; set; }
public bool Enabled { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
public string ModifiedBy { get; set; }
public DateTime Modified { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDateTime { get; set; }
public Address Address { get; set; }
public Title Title { get; set; }
public Client Client { get; set; }
internal static IEnumerable<object> Include(Func<object, object> p)
{
throw new NotImplementedException();
}
public PersonNote PersonNote { get; set; }
}
public class PersonNote
{
public int Id { get; set; }
public int PersonId { get; set; }
public string Note { get; set; }
public int AuthorId { get; set; }
public string CreatedBy { get; set; }
public DateTime Created { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordStartDateTime { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime RecordEndDateTime { get; set; }
}
public IEnumerable<PersonNote> GetPersonNotes(int personId)
{
var PersonNotes = PersonNote
.Include(x => x.)
.Where(x => x.Id == personId)
.ToList();
return PersonNotes;
}
I have tried the following in OnModelCreating:
modelBuilder.Entity<PersonNote>(entity =>
{
entity.ToTable("PersonNote", "common");
entity.HasOne(d => d.PersonNote)
.WithMany(p => p.Person)
.HasForeignKey(d => d.PersonId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_commonPersonNote_commonPerson");
});
You should have have something like this (other properties are omitted):
class Person
{
[Key]
public int Id { get; set; }
public List<PersonNote> PersonNotes { get; set; }
}
class PersonNote
{
[Key]
public int Id { get; set; }
public int PersonId { get; set; }
}
class StackOverflow : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<PersonNote> PersonNotes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasMany(p => p.PersonNotes)
.WithOne()
.HasForeignKey(p => p.PersonId);
}
}

How to create Map between two tables using where statement?

I have Doctor and Appointment tables.On my DTO I have DoctorName field and I want to bring that name from doctor table by AutoMapper.I tried to Map like below on AutoMapperProfile but it says cannot convert from 'bool' to 'System.Func<<char,bool>. Both values are string that I want to map so doesnt make sense.Here my Models,DTO and CreateMap below.
Doctor
public class Doctor
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public Department Department { get; set; }
public int DepartmentId { get; set; }
}
Appointment
public class Appointment
{
public int Id { get; set; }
public DateTime AppointmentDate { get; set; }
public int DoctorId { get; set; }
public Doctor Doctor { get; set; }
public int? PatientId { get; set; }
public Patient Patient { get; set; }
}
AutoMapperProfile
CreateMap<Appointment, AvilableAppointmentDto>()
.ForMember(dep => dep.DoctorName,
opt => opt.MapFrom(src =>
src.Doctor.Name.Where(src.DoctorId==src.Doctor.Id)));
DTO
public class AvilableAppointmentDto
{
public int Id { get; set; }
public DateTime AppointmentDate { get; set; }
public int DoctorId { get; set; }
public string DoctorName { get; set; }
}
UPDATE(Solution)
Dto should be mapped as IENumerable.Like below
var returnAppointments = _mapper.Map<IEnumerable<AvilableAppointmentDto>>(availableAppointments);
Below thing works for me
Mapping:
CreateMap<Appointment, AvilableAppointmentDto>(MemberList.Destination)
.ForMember(dep => dep.DoctorName, opt => opt.MapFrom(src => src.Doctor.Name));
Entity Classes:
public class Doctor
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public int DepartmentId { get; set; }
}
public class Appointment
{
public int Id { get; set; }
public DateTime AppointmentDate { get; set; }
public int DoctorId { get; set; }
public Doctor Doctor { get; set; }
public int? PatientId { get; set; }
}
public class AvilableAppointmentDto
{
public int Id { get; set; }
public DateTime AppointmentDate { get; set; }
public int DoctorId { get; set; }
public string DoctorName { get; set; }
}
controller:
[HttpGet]
public ActionResult Get([FromServices] IMapper mapper)
{
Appointment ap = new Appointment()
{
AppointmentDate = DateTime.Now,
DoctorId = 1,
Id = 2,
PatientId = 3,
Doctor = new Doctor()
{
Id = 1,
DepartmentId = 2,
Name = "Ajay",
Title = "Mr"
}
};
var x = mapper.Map<AvilableAppointmentDto>(ap);
return Ok(x);
}
startup.cs
public void ConfigureServices(IServiceCollection services)
{
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile<MappingProfile>();
});
var mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
}
Package ref.
<PackageReference Include="AutoMapper" Version="8.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="6.0.0" />

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()`

Using Automapper through a join table in EFCore

I have a many-to-many relationship between Recipe and Item via a join table called Ingredient:
public class Recipe
{
public int RecipeId { get; set; }
public string Name { get; set; }
public ICollection<RecipeInstruction> RecipeInstructions { get; set; }
public ICollection<Ingredient> Ingredients { get; set; }
}
public class Ingredient
{
public Recipe Recipe { get; set; }
public int RecipeId { get; set; }
public Item Item { get; set; }
public int ItemId { get; set; }
public int Quantity { get; set; }
}
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public ICollection<Ingredient> Ingredients { get; set; }
}
I would like to present the data through this DTO:
public class RecipeForDetailedDto
{
public int RecipeId { get; set; }
public string Name { get; set; }
public ICollection<RecipeInstruction> RecipeInstructions { get; set; }
public ICollection<ItemForDetailedDto> Ingredients { get; set; }
}
Is there a way I can map this relationship to show a list of Ingredient names, which would be the Item Name?
It should look like this:
CreateMap<Ingredient, ItemForDetailedDto>();
CreateMap<Ingredient,RecipeForDetailedDto>()
.ForMember(dest=>dest.Name, opt=>opt.MapFrom(src=>src.Item?.Name));
var result = mapper.Map<ItemDetailedDto>(yourIngredientObject);
In the end this is what worked:
CreateMap<Ingredient, IngredientForDetailedDto>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Item.Name))
With IngredientForDetailedDto as:
public class IngredientForDetailedDto
{
public string Name { get; set; }
public int Quantity { get; set; }
public string QuantityType { get; set; }
}

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