Get a specific info from an other table with Linq - c#

My database is created using code first. Here is the model for my client.
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Display(Name = "Phone number")]
public int PhoneNumber { get; set; }
[Display(Name = "Email adress")]
[EmailAddress]
public string EmailAdress { get; set; }
[Display(Name = "Date of the wedding")]
public DateTime DateOfTheWedding { get; set; }
[Display(Name = "Type of client")]
public TypeOfClient TypeOfClient { get; set; }
[NotMapped]
public int DaySinceLastVisit { get; set; }
I also have a model Visit :
public int Id { get; set; }
public Client Client { get; set; }
[Required]
[ForeignKey("Client")]
public int ClientId { get; set; }
[Display(Name = "Date of the visit")]
public DateTime DateOfTheVisit { get; set; }
public String Comment { get; set; }
Each client have a multiple visit.
How can I get the last visit and add it to my model ?
Here is my resquest linq that I want to improve.
var clientsQueryable = _context.Clients
.Include(c => c.TypeOfClient);
if (!String.IsNullOrWhiteSpace(query))
clientsQueryable.Where(client => client.FirstName.Contains(query));
var listClients = clientsQueryable
.ToList()
.Select(Mapper.Map<Client, ClientDto>);
return Ok(listClients);
I want to have in Client the info DaySinceLastVisit.
I presume that we need to get the last visit and make a calculation to get the days that have passed since this visit ?

First of all:
If a Client has multiple Visits, it would be good to add a
public virtual ICollection<Visit> Visits { get; set; }
To your Client entity, so you can easily access all the Visits linked to a Client.
Then it's easy to calculate the "DaySinceLastVisit" using similar code like this:
public int DaySinceLastVisit => (DateTime.Now - Visits.OrderByDescending(v => v.DateOfTheVisit).FirstOrDefault().DateOfTheVisit).Days;

Related

Automapper Mapping Exception

I am trying to build a simple application where I can store and retrieve some details about some devices and the users of them, like an inventory. But when I try to display the list of devices with their owners, Automapper throws this error:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
I don't understand what I am doing wrong here. How can I deal with this?
Startup.cs
builder.Services.AddAutoMapper(typeof(MapConfig));
builder.Services.AddControllersWithViews();
var app = builder.Build();
MapConfig.cs
public class MapConfig : Profile
{
public MapConfig()
{
CreateMap<Asset, AssetVM>().ReverseMap();
CreateMap<AppUser, AppUsersVM>().ReverseMap();
}
}
Asset.Cs
public class Asset
{
public int Id { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
public string? ProductNumber { get; set; }
public string? SerialNumber { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public bool IsAssigned { get; set; }
public string? ISN { get; set; }
public string Status { get; set; }
public bool IsInsured { get; set; }
public string Condition { get; set; }
[ForeignKey("UserId")]
public AppUser AppUser { get; set; }
public string? UserId { get; set; }
}
AssetVM
public class AssetVM
{
public int Id { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
[Display(Name ="Product Number")]
public string? ProductNumber { get; set; }
[Display(Name ="Serial Number")]
public string? SerialNumber { get; set; }
[Display(Name ="Date Created")]
[DataType(DataType.Date)]
public DateTime DateCreated { get; set; }
[Display(Name = "Date Modified")]
[DataType(DataType.Date)]
public DateTime DateModified { get; set; }
[Display(Name ="Assigned")]
public bool IsAssigned { get; set; }
public string? ISN { get; set; }
[Required]
public string Status { get; set; }
[Display(Name ="Has Insurance")]
public bool IsInsured { get; set; }
[Required]
public string Condition { get; set; }
public string? UserId { get; set; }
public SelectList? AppUsersList { get; set; }
public AppUsersVM AppUsers { get; set; }
}
This is how I get and map the data that is to be displayed on the page:
public async Task<AssetVM> GetAssets()
{
var asset = await context.Assets.Include(q => q.AppUser).ToListAsync();
var model = mapper.Map<AssetVM>(asset);
return model;
}
And finally I return the result of GetAssets method to the view in my controller:
var model = await assetRepository.GetAssets();
return View(model);
Well, I found what I'm doing wrong. This is what I've done:
Since I was getting a list after querying the database in my GetAssets method, I had to change my mapping to:
var model = mapper.Map<List<AssetVM>>(asset);
And to be able to return this model, I also had to change my method declaration to:
public async Task<List<AssetVM>> GetAssets()
This changes made it work, except I didn't get the details of the user that is using that asset. And this was due to a typo in my AssetVM viewmodel.
public AppUsersVM AppUser { get; set; }
Those were all the changed I had to do. Still have a looong way to be a competent programmer so I'd be glad if you let me know if I have any flaws in my logic or any recommendations at all.

Entity Framework Core no connected objects

I created local database using EF core and code-first method.
The db imitates library, so I have 3 simple tables: users, books and reservations.
Issue occurs when I want to get nested data like find one book and get its reservation.
I think I should be able to use
List<Reservation> reservations = book.Reservations;
but I have to use
List<Reservation> reservations = libraryContext.Reservations.
Where(r=> r.Book == book).ToList();
But the main reason I need help is this fragment
BookReservationsModel bookReservationsModel = new BookReservationsModel
{
BookTitle = book.Title,
Reservations = reservations
};
// I want to display emails in View.
for (int i = 0; i < bookReservationsModel.Reservations.Count; i++)
{
Debug.WriteLine(bookReservationsModel.Reservations[i].User.Email);
}
I cannot get access to users because they are nulls. In database everything is stored as it should be (correct ids). Of course I could copypaste certain emails to new created list but it's inefficient and I know I should be able to use it that way. I worked before with EF for Framework and I tried google the problem but couldn't find the solution.
Models and context code.
public class User
{
[Key]
public int UserID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Salt { get; set; }
[Required]
public string Password { get; set; }
public ICollection<Reservation> Reservations { get; set; }
}
public class Book
{
[Key]
public int BookID { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Author { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; }
[Required]
public string Description { get; set; }
public ICollection<Reservation> Reservations { get; set; }
}
public class Reservation
{
[Key]
public int ReservationID { get; set; }
[Required]
public DateTime ReservationDate { get; set; }
[Required]
public int UserID { get; set; }
[ForeignKey("UserID")]
public User User { get; set; }
[Required]
public int BookID { get; set; }
[ForeignKey("BookID")]
public Book Book { get; set; }
}
public class LibraryContext : DbContext
{
public LibraryContext(DbContextOptions options) : base(options) { }
public DbSet<User> Users { get; set; }
public DbSet<Book> Books { get; set; }
public DbSet<Reservation> Reservations { get; set; }
}
Attempting to use a navigation property on the book entity without doing either of the following will result in the property being null.
Including the property before materializing the entity with .First()/.Single()
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager
Configuring EFCore to AutoInclude navigation properties by default.
https://dotnetcoretutorials.com/2021/03/07/eager-load-navigation-properties-by-default-in-ef-core/
I suggest when querying for the book to use the Include and ThenInclude methods so the Reservations and Users are populated.
var book = await libraryContext.Books
.Include(x => x.Reservations)
.ThenInclude(x => x.User)
.SingleAsync(x => x.BookID == myBookId);

One to Many and a Many to Many relationship Entity Framework including default Authorization and role managing

I have a system that produce some online classes. In this class we have students and a teacher.
I have used Microsoft default Identity System and Authorization for this system, but something is bothering me in my design.
For more explanation I want to define Roles(Teacher, Students, Admins and etc) , but it is so confusing how to handle relation between Course and Teacher (it is one to many relation) and Course to Students (it has many to many relation).
So I have question is that true way to have two relation between two entities or not? if it is not, How should I handle this?
Here is my Course entity
[Key]
[Display(Name = "شناسه")]
public Guid CourseId { get; set; }
[Required]
[Display(Name = "لوگوی دوره")]
public string LogoPath { get; set; }
[Required]
[Display(Name = "نام دوره")]
public string Name { get; set; }
[Required]
[Display(Name = "شرح دوره")]
public string Description { get; set; }
[Required]
[Display(Name = "شهریه")]
public int Price { get; set; }
[Display(Name = "دارای تخفیف")]
public bool HasDiscount { get; set; }
[Display(Name = "درصد تخفیف")]
public float DiscountPercentage { get; set; }
[Required]
[Display(Name = "آخرین تاریخ به روزرسانی")]
public DateTime LastUpdateUpdate { get; set; }
public string UserId { get; set; }
public AppUser CourseTeacher { get; set; }
public Guid CaptionId { get; set; }
public MainCaption CourseCaption{ get; set; }
public ICollection<Chapter> Chapters { get; set; }
public ICollection<AppUser> Students{ get; set; }
and here is my AppUser entity
[Required]
[Display(Name = "نام")]
public string Firstname { get; set; }
[Required]
[Display(Name = "نام خانوادگی")]
public string LastName { get; set; }
[Required]
[Display(Name = "جنسیت")]
public Gender Gender { get; set; }
[Display(Name = "عنوان")]
public string Title { get; set; }
[Display(Name = "اعتبار")]
public int Credit { get; set; }
[Display(Name = "تاریخ تولد")]
public string BirthDate { get; set; }
[Display(Name = "مدرک تحصیلی")]
public EducationalDegree? Degree { get; set; }
[Display(Name = "آدرس تصویر")]
public string ImagePath { get; set; }
[Display(Name = "تصویر تایید شده")]
public bool? IsImageConfirmed { get; set; }
[Display(Name = "آدرس فیس بوک")]
public string Facebook { get; set; }
[Display(Name = "آدرس اینستاگرام")]
public string Instagram { get; set; }
[Display(Name = "آدرس لینکداین")]
public string Linkedin { get; set; }
[Display(Name = "آدرس توئیتر")]
public string Twitter { get; set; }
[Display(Name = "آدرس وبسایت")]
public string Website { get; set; }
[Display(Name = "تاریخ ثبت نام")]
public DateTime RegisterDate { get; set; }
public ICollection<Course> StudentCourses { get; set; }
public ICollection<Course> TeacherCourses { get; set; }
public ICollection<News> WrittenNews { get; set; }
Tnx to All
Edit
I forgot to say this contains an error Sequence contains more than one matching element and it seems logical
One important this is that if I use same class for inheritance how should I add two relations for this two tables AppUser and Course
I want to define Roles(Teacher, Students, Admins and etc)
You can do it in a couple different ways:
Have User and Role tables and enforce roles on the application level, e.g. Only "teacher" user can do teacher things, only student can enrol into courses etc.
With EF you can use inheritance. Abstract User would have all the common fields and Student, Teacher and Admin would have fields specific only to their role.
Please see the code:
abstract class User
{
public int UserId { get; set; }
public string Name { get; set; }
}
class Teacher : User
{
public string Specialty { get; set; }
}
class Student : User
{
public int Grade { get; set; }
}
See more info here - the example given in this official documentation is very close to what you're trying to achieve.
Course to Students (it has many to many relation)
For this type of a relationship I'd create a new table/entity StudentCourse with composite (StudentId, CourseId) key. And the reason for it is, usually you don't just want a link between 2 entities but also to keep some additional info like Mark, Performance or EnrolmentDate:
class StudentCourse
{
public int StudentId { get; set; }
public int CourseId { get; set; }
public Student Student { get; set; }
public Course Course { get; set; }
// Any additional fields related to the relationship
public int Mark { get; set; }
}

Get property from Icollection (ASP.NET MVC)

I have back-end where I need to get FIO from table
I try to write code and here is what I have
public ActionResult Profile_Data_Questions()
{
var dataforprofile = TempData["FIO"];
var profiledata = db.QuestionBlocks
.Where(x => x.Interview.Interwiers == dataforprofile)
}
But Interwiers is IColection
Here is Model for Interwiers
[Key]
public int Interwier_id { get; set; }
[Display(Name = "ФИО")]
public string FIO { get; set; }
public string Email { get; set; }
[Display(Name = "Телефон")]
public string Telephone { get; set; }
[Display(Name = "День рождения")]
[DataType(DataType.Date)]
public string Birthday { get; set; }
[Display(Name = "Город")]
public string City { get; set; }
[Display(Name = "Зарплата")]
public string Salary { get; set; }
[Display(Name = "Английский")]
public string English { get; set; }
public Nullable<int> Interview_Id { get; set; }
[Display(Name = "Статус")]
public string Status { get; set; }
public virtual Interview Interview { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Link> Links { get; set; }
How I can get FIO property?
i assume the dataforprofile is a string or an int / enum, ... you can search the items that have the prop set to dataforprofile.
var profiledata = db.QuestionBlocks
.Where(x => x.Interview.Interwiers.Any(y=> y.prop == dataforprofile))
the code above will give you all questionblocks where there is an interview containing one or more interviewers with the prop set to the dataforprofile var.
Heck I am not sure what your problem is, and I can't ask for clarification yet, due to not having accumulated enough rep. So here goes:
Either your collection does not contain interviewer objects, and you need to convert your request into the object type like so:
var targetList = origList
.Select(x => new TargetType() { SomeValue = x.SomeValue })
.ToList();
Or, if the question is even simpler I will link to another question that answers my other interpretation of your question:
How to expose a collection property?

Excluding dates from Linq Query in MVC .net application

Within an asp.net MVC app, I'm trying to find rooms which have a guest/client, where the guest is leaving before a certain date.
The Client model class has foreign key, RoomId:
public class Room
{
public int RoomId { get; set; }
[Display(Name = "Room Name")]
public string Name { get; set; }
public bool Disabled { get; set; }
public List<Client> Clients { get; set; }
}
public class Client
{
public int ClientId { get; set; }
public int RoomId { get; set; }
public string RoomName { get; set; }
public DateTime Arrival { get; set; }
public DateTime Departure { get; set; }
public Room Room { get; set; }
}
My current Linq query is:
from r in Rooms
where r.Disabled == false
//where r.Clients.Departure<=DateTime.Parse("2012-07-01")
select new
{
r.Name,
r.Disabled
}
The commented line: //where r.Clients.Departure..... brings up the following error in LinqPad:
'System.Data.Linq.EntitySet' does not contain a
definition for 'Departure' and no extension method 'Departure'
accepting a first argument of type
'System.Data.Linq.EntitySet' could be found
(press F4 to add a using directive or assembly reference)
Is there any way, within Linq, that I can run this query, to exclude the Departure date where clause?
Thanks for any help,
Mark
After your comment, this one should do what you need
&& r.Clients.All(client => client.Departure<=DateTime.Parse("2012-07-01"))
Edit :
Maybe declare the DateTime to compare out of the query
var dt = DateTime.Parse("2012-07-01");
and
&& r.Clients.All(client => client.Departure<=dt)
What happens if you change the Room reference in Client to be virtual and the Client reference in Room to be a virtual ICollection? That should help the EF to make the connection.
See here: http://msdn.microsoft.com/en-us/library/gg715120(v=vs.103).aspx or here for something from Julia Lerman on the subject: http://msdn.microsoft.com/en-us/data/hh134698.aspx
This should enable you to re-add the commented-out clause and it should work.
EDIT: Actually the commented out bit should change in line with Raphaël Althaus's correction.
I reckon a combination of what I and Raphaël have suggested is the best approach.
Like this
public class Room
{
public int RoomId { get; set; }
[Display(Name = "Room Name")]
public string Name { get; set; }
public bool Disabled { get; set; }
public virtual ICollection<Client> Clients { get; set; }
}
public class Client
{
public int ClientId { get; set; }
public int RoomId { get; set; }
public string RoomName { get; set; }
public DateTime Arrival { get; set; }
public DateTime Departure { get; set; }
public virtual Room Room { get; set; }
}

Categories