Not sure how to describe this question.
Here is the example:
I have an entity as Tour. Tour table contains static 3 rows(can be more in the future).
public class Tour
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
}
Each user can turn this options keys.
So I have another table called UserTourSetting to store user options.
public class UserTourSetting
{
public int Id { get; set; }
public int UserId { get; set; }
public int TourId { get; set; }
public bool Enabled { get; set; }
public ApplicationUser User { get; set; }
public Tour Tour { get; set; }
}
When the user loads the page it should always see 3 rows with the status on or off.
When I load UserTourSetting table I get no results as there is no record yet on the table for the user first time. I guess I need to do join with Tour table and also include Tour results as I will need the titles.
When the switches are turned on off then individually it will add a new record if not exists or change Enabled key if exists.
What would be the best way to implement this with EF? I struggle the generate linq statement to load the join table.
here is the method I tried:
public IEnumerable<UserTourSetting> GetUserTourSettings(int userId)
{
var q = from tour in DbContext.Tours
join uts in DbContext.UserTourSettings
on tour.Id equals uts.TourId
where uts.UserId == userId
select uts;
return q;
}
But it does not work. And I cannot access Tour names. Where the include can go in this query?
Thanks,
Please try to set virtual on those properties.
public virtual ApplicationUser User { get; set; }
public virtual Tour Tour { get; set; }
p/s: for more information, you can see this https://stackoverflow.com/a/8542879/5771430
Related
I have three models like followings,
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Application { get; set; }
public ICollection<DocumentResponsible> DocumentResponsibles { get; set; }
public string Pcda { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
public class DocumentResponsible
{
public int Id { get; set; }
public int DocumentId { get; set; }
public int TeamId { get; set; }
}
I want to write an entity framework expression to join three table and select all fields of document table and team names in one row per documents. So basicly I want to join three table use group_concat for team names. Then I want to bind it to a gridview in web form.
What I have tried,
(from dc in DBContext.Document
join dr in DBContext.DocumentResponsible on dc.Id equals dr.DocumentId
join t in DBContext.Team on dr.TeamId equals t.Id
select new
{
Name = dc.Name,
Type = dc.Type,
Application = dc.Application,
Pcda = dc.Pcda,
}).ToList();
and I have just tried it,
var data = DBContext.Dcoument.Include("DocumentResponsibles").Tolist();
It's hard to help without your DbContext and the Entity Mappings, but I'll go out on a limb saying you might just want to mark Document.DocumentResponsibles as virtual.
Also, in DocumentResponsible, maybe you'd want to add a property for Document and one for Team (both marked as virtual too) this way you don't have to do the join keys all the time you want to work with your data, EF would do it for you once properly configured.
If it doesn't work, can you add the following information to your question: First, the context class and the mappings you have. Second, if you do var firstDocument = yoneylemDB.Document.First(), how does firstDocument looks like? Does it have all it's fields and properties filled out? Is there anything weird?
I am using Entity Framework Code First and LINQ to Entities in a Web Api Controller.
I have 3 tables that are related with the following relations/ navigation properties: (the model classes are as follows)
My Reservation class:
[Table("Reservations")]
public class Reservation
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ReservationId { get; set; }
public DateTime Date { get; set; }
public int RoomId { get; set; }
public Room Room { get; set; }
public Customer Customer { get; set; }
public int CustomerId { get; set; }
// create association table ReservationMeals
public virtual ICollection<ReservationMealItems> ReservationMeals { get; set; }
}
Association class:
public class ReservationMealItems
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ReservationId { get; set; }
public int MealId { get; set; }
public virtual Reservation Reservation { get; set; }
public virtual Meal Meal { get; set; }
}
Meals class:
[Table("Meals")]
public class Meal
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public int MealTypeId { get; set; }
public virtual MealType Type { get; set; }
public virtual ICollection<MenuMealItem> Menus { get; set; }
// create association table ReservationMeal
public virtual ICollection<ReservationMealItems> ReservationMeals { get; set; }
// one-to-one with MealPrice where MealPrice is the dependant
public virtual MealPrice MealPrice { get; set; }
}
And here is my controller to which I am passing a Date and then querying the tables in order to bring back all the meals from all the reservations made on that particular date.
public IHttpActionResult GetReservationMealsForDate(DateDto date)
{
using (var context = new CafeteriaContext())
{
var reservationMeals = from p in context.Reservations
where p.Date == date.Date
join d in context.ReservationMeals
on p.ReservationId equals d.ReservationId
select new SimpleId
{
Id = d.MealId
};
var meals = from p in context.Meals
join d in reservationMeals
on p.Id equals d.Id
select new SimpleMeal
{
Name = p.Name
};
return Ok(meals.ToList());
}
}
The controller works and brings back the list of meals from all reservations made on that date but I am certain that it's not optimal.
I tried not using the JOINs and instead make use of the navigation properties but with no success. I wonder if the relations are set up correctly and if the navigation properties are placed as they should be and if yes, then how can the controller be rewritten to bring back the same result set using navigation properties.
And as a bonus question I would like to know how to write the correct query using LINQ method syntax as I did not succeed in writing it as such.
I am currently learning and any explanations are greatly appreciated. I have read this blog post Coding Abel from where I managed to make it work. But I am still curious to know how correct my solution is and eager to learn better alternatives. Thanks!
You can try use eagerly loading with LINQ and method Include. Your table ReservationMealItems have relationships to Meal and Reservation in this case you need include your entities like :
Lambda
var x = context.ReservationMealItems.Include(res => res.Reservation)
.Where(dt => dt.Reservation.Date == DateTime.Now)
.ThenInclude(meal => meal.Meal)
.Select(d => d.Meal.Name)
.ToList();
Here you find more informations MSDN
Query Syntax
var z = (from resevationMealsItems in context.ReservationMealItems
join reservation in context.Reservation on resevationMealsItems.ReservationId equals
reservation.ReservationId
join meal in context.Meal on resevationMealsItems.MealId equals meal.Id
select meal.Name).ToList();
In my IdentityModels class I have a property called Reports like below
public DbSet<Report> Reports { get; set; }
In my database there is a table with the same name which this property pulls the data from.
I also have a model called Report that (in addition to some other properties) has these
public class Report
{
//...
[Required]
public string State { get; set; }
[Column("reporter_id")]
public string ReporterId { get; set; }
[Required]
[Column("report_text")]
public string ReportText { get; set; }
//...
}
I also have a view that is a strong view with Report as its model.
Where I ran into a problem is that my model has reporterId which is a foreign key to different table called AspNetUsers that has the user details.
I want to display the user name not the id and because the Reports table only has the userId there is no way for me to display the user name.
What would be a good way of making a user_name field in the AspNetUsers table be part of my model? There obviously has to be a join statement somewhere between the reports table and aspNetUsers table but I'm not sure how best to do this.
My answer may not be helpful to you but I'm will still post it, ViewModel are specifically used for this kind of situation where you need to have two or more tables data displayed in a view.
First , you will need to create a ViewModel lets call it UserReportViewModel. In this view model, you will include an additional property UserName that will pull the username based on ReporterId.
public class UserReportViewModel
{
[Required]
public string State { get; set; }
[Column("reporter_id")]
public string ReporterId { get; set; }
[Required]
[Column("report_text")]
public string ReportText { get; set; }
public string UserName{ get; set; }
}
Then , assuming you are using LINQ to retrieve the report:
Dim ReportList As List(Of UserReportViewModel) = New List(Of UserReportViewModel)
ReportList = dbContext.Reports.Select(Function(x) New UserReportViewModel With {
.State = x.State, _
.ReporterId= x.ReporterId, _
.UserName= dbContext.Users.Find(x.ReporterId).UserName, _
.ReportText = x.ReportText, _
}).ToList()
You have to change your models as shown below.
public class Report
{
[Required]
public string State { get; set; }
[ForeignKey("UserId")]
public virtual AspNetUser { get; set; }
public int UserId { get; set; }
[Required]
[Column("report_text")]
public string ReportText { get; set; }
}
public class AspNetUser
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Report> Reports { get; set;}
}
Your Query should be like this :
var query =
(from u in db.AspNetUsers
from r in u.Reports
select new { UserName = u.Name, ReportText = r.ReportText }).ToList();
I'am having some problems with an issue that I've encountered. I'm creating a webshop, where it is possible for a logged-in and a non-logged-in user to add products to their basket. When the user checkeouts the user will have to either log in or sign up. In case the user logs in on an existing account and that account already have a basket with products, it is a must to merge the two baskets into one. Below is my two tables (BasketTable) and (BasketProductTable).
public partial class BasketTable
{
public BasketTable()
{
this.BasketProductTables = new HashSet<BasketProductTable>();
this.OrderTables = new HashSet<OrderTable>();
}
public int BasketId { get; set; }
public Nullable<int> UserId { get; set; }
public string SessionId { get; set; }
public Nullable<System.DateTime> Registered { get; set; }
public virtual ICollection<BasketProductTable> BasketProductTables { get; set; }
public virtual UserTable UserTable { get; set; }
public virtual ICollection<OrderTable> OrderTables { get; set; }
}
public partial class BasketProductTable
{
public int BasketProductId { get; set; }
public int BasketId { get; set; }
public int ProductId { get; set; }
public Nullable<int> Quantity { get; set; }
public Nullable<double> TotalSum { get; set; }
public virtual BasketTable BasketTable { get; set; }
public virtual ProductTable ProductTable { get; set; }
}
My question is that I'm uncertain how to merge the baskets. If someone could navigate me to a good article on the subject or show some code then I would be happy. I am coding in Asp.Net (MVC) and using Linq to entity.
- Jakob
----- UPDATE ------
I decided to use two foreach containing the old and new baskets. I checked on if the item from the new basket existed in the old basket then I would update it with quantity and totalSum. If it didn't exist then I would create a new object with its information and then delete all the items in the old. I found out that the items wouldn't be created/updated in runtime so I had problems checking if a product had been created in the foreach, so I created a list where I would add the created/updated products on their productId and check in the code if the product was added to the list before either creating or updating. I hope this helps someone.
I would argue that a better experience is to merge the baskets in such a way that "duplicated" orders are ignored. Here's some rough pseudo-code - something like:
foreach (BasketProductTable newBasketProduct in newBasket.BasketProductTables) {
if(oldBasket.Contains(newBasketProduct)) {
continue; // this ignores duplicate order items
}
// Otherwise add it to the old basket
oldBasket.Add(newBasketProduct);
}
Where newBasketTable is your new Basket of goods, and oldBasket is the existing saved basket.
After this you can discard the "new basket" as your "old basket" has the new items. A word of caution: don't forget to re-calculate the price on each individual item, as the price may have changed since the user last placed the item in their basket.
I walked through a couple of tutorials and it seems they all leave out how you can utilize the logged in user to store information to a database. To help me illustrate my point, here is a model I've been using.
public class Note
{
public int ID { get; set; }
public int UserId { get; set; }
public string Text { get; set; }
}
This each user can write a note to the database. When I created the CRUD controller for this model I then updated the UserId property to the WebSecurity.CurrentUserId when doing Update/Create. Then when retrieving data back I filter the notes using Where in the linq expression. For some reason this just feels wrong.
Trolling through even more examples I came across someone doing it like so.
public class Note
{
public int ID { get; set; }
public virtual UserProfile User { get; set; }
public string Text { get; set; }
}
public class NoteDbContext : DbContext
{
public DbSet<Note> Notes { get; set; }
}
This looks a lot cleaner since the models are properly linked in C#. And wow, it actually builds! So now in my controller I will first get the user object from the database, then using a Where list their notes.
//First get the logged in user
var user = dbUser.UserProfiles.Where(x => x.UserId == WebMatrix.WebData.WebSecurity.CurrentUserId).First();
//Now get all their notes
var notes = db.Notes.Where(x => x.User == user);
However, this unexpectedly fails. So could someone please provide a sample of a good way to store the UserProfile object against other objects in the database? Basically, I just need a good example that shows now the UserProfile object can be linked to a Note object, and how you should properly query for Notes of a specific UserId.
The way you've defined your relationship, is that you are creating a one-to-one relationship between a Note and a User. I would expect that a user can have multiple notes, based on the query that you're having trouble with. Thus, in order to create a one-to-many between a user and their notes, you should create a collection on your UserProfile object. For instance,
public class UserProfile
{
...
public List<Note> Notes {get; set;}
}
...and to query, loading your Notes associated with that user,
var user = myUsers.Include(n=>n.Notes)
.Single(x => x.UserId == WebMatrix.WebData.WebSecurity.CurrentUserId);
Each user can have many notes, right? If so, change your class like this:
public class Note
{
public int ID { get; set; }
public int UserId { get; set; }
public string Text { get; set; }
public virtual UserProfile User { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
public UserProfile()
{
this.Notes = new HashSet<Note>();
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public virtual ICollection<Note> Notes{ get; set; }
}
Now, have users and notes connecting correctly. So, you can easily achive your goal like the following. You also don't need to struggle with WebMatrix.WebData.WebSecurity to get the current user! just use User.Identity.Name :
// ...
var notes = db.Notes.Where(x => x.User.UserName == User.Identity.Name).AsQueryable();