First of all this is my first question in the forum so please excuse me for any writing mistake.
I have 4 tables
attaching the table diagram
What I want is to get list of attraction name joining 'tblattraction' with 'tblattractionmaster' and count of the exact attraction for each place from 'tblattractions' using 'locationid' , I am using entity framework but don't know how to do that,
Disclaimer:
Each location can consist Multiple Places
Each Place can consist Multiple Attractions
What I have tried
return context.tblLocationMasters.Select(t => new details()
{
locationid = t.LocationId,
locationname = t.LocationName,
attractions =t.tblPlaces.SelectMany(a => a.tblAttractions).Select(b => new attractions(){
AttractionName=b.tblAttractionMaster.attractionname//(Not working),
TotalAttractions=0//???
}).ToList()
}).ToList();
I recreated your model (slightly different) using Code First. I came up with the following structure:
public class Location
{
public int LocationId { get; set; }
public string LocationName { get; set; }
public ICollection<Place> Places { get; set; }
}
public class Place
{
public int PlaceId { get; set; }
public string PlaceName { get; set; }
public int LocationId { get; set; }
public Location Location { get; set; }
public ICollection<AttractionPlace> Attractions { get; set; }
}
public class Attraction
{
public int AttractionId { get; set; }
public string AttractionName { get; set; }
}
public class AttractionPlace
{
public int AttractionPlaceId { get; set; }
public int PlaceId { get; set; }
public Place Place { get; set; }
public int AttractionId { get; set; }
public Attraction Attraction { get; set; }
}
Then, I could get the results in the way you needed with the following query:
var query = (from loc in db.Locations
join pla in db.Places.Include(x => x.Attractions) on loc.LocationId equals pla.LocationId
let count = pla.Attractions.Count()
select new
{
loc.LocationId,
loc.LocationName,
Attractions = pla.Attractions.Select(z => new
{
pla.PlaceName,
z.AttractionId,
z.Attraction.AttractionName
}),
AttractionsByPlaceCount = count
});
The query above returns data in this format
Just a side note though: I didn't went further to see the performance of this query. The SQL generated by Linq wasn't that bad, but you should consider analyzing it before actually using it in production.
Related
I would like to filter my 'TranslationSet' entities, based on their 'Translations' Collection Navigation Property.
E.g.
If a 'Translation' has a 'LanguageId' of 5 (Italian), then the 'TranslationSet' that contains this 'Translation' should be removed from the result.
Here are my Entity classes:
public class Language
{
public int LanguageId { get; set; }
public string NationalLanguage { get; set; }
//Make table multi tenanted.
public int TenantId { get; set; }
public ApplicationTenant Tenant { get; set; }
public List<Translation> Translation { get; set; } = new List<Translation>();
}
public class Translation
{
public int TranslationId { get; set; }
public string TranslatedText { get; set; }
public int LanguageId { get; set; }
public Language Language { get; set; }
//Make table multi tenanted.
public int TenantId { get; set; }
public ApplicationTenant Tenant { get; set; }
public int TranslationSetId { get; set; }
public TranslationSet TranslationSet {get; set;}
}
public class TranslationSet
{
public int TranslationSetId { get; set; }
public int TenantId { get; set; }
public ApplicationTenant Tenant { get; set; }
public IEnumerable<Translation> Translations { get; set; }
}
Here is my attempt
From the image you can see that the query fails because a Translation exists with LanguageId of 5.
I have tried many many attempts to resolve this but I can't even get close the LINQ which returns my query correctly.
Please let me know if any further clarification is needed and thanks in advance to anybody who offers help.
My rule of the thumb that nearly always work is: start by querying the entities you want. That will prevent duplicates as you see in your query result. Then add predicates to filter the entities, using navigation properties. That will be:
var sets = TranslationSets // start the query here
.Where(ts => ts.Translations.All(t => t.LanguageId != 5)); // Filter
Or if you like this better:
var sets = TranslationSets // start the query here
.Where(ts => !ts.Translations.Any(t => t.LanguageId == 5)); // Filter
EF will translate both queries as WHERE NOT EXISTS.
I'm trying to get all time entries for a specific foreman based on his supervisor's supervisor. However, I seem to be having trouble writing a self join query in ORMLite. See my data structure and code below.
public class User
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[References(typeof(User))]
public int SupervisorId { get; set; }
}
public class TimeSheet
{
[AutoIncrement]
public int Id { get; set; }
[References(typeof(User))]
public int ForemanId { get; set; }
}
var query = db.From<TimeSheet>()
.Join<User>()
.Join<User, User>(); // not sure how to write this one.
// .Where(super => super.SupervisorId = 2)
I've created a sample gist to try and better show what I've attempted.
var query = db.From<TimeSheet>()
.Join<User>()
.Join<User, User>((p, q) => p.Id == q.SupervisorId, db.JoinAlias("u2"));
I have two entities:
public class Booking
{
[Key]
public int Id { get; set; }
public int RoomId { get; set; }
[ForeignKey("RoomId")]
public Room Room { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DocumentNumber { get; set; }
public string ContactPhone { get; set; }
}
public class Room
{
[Key]
public int RoomId { get; set; }
public int Number { get; set; }
public int Size { get; set; }
public bool HasBalcony { get; set; }
public int Beds_1 { get; set; }
public int Beds_2 { get; set; }
public double DayPrice { get; set; }
public List<Booking> Bookings { get; set; }
...
public int BookingsCount()
{
return Bookings.Count;
}
public bool IsFree(DateTime dateTime)
{
MessageBox.Show(BookingsCount().ToString());
return true;
}
}
and DbContext:
public class HotelContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Room> Rooms { get; set; }
public DbSet<Booking> Bookings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Booking>()
.HasRequired(b => b.Room)
.WithMany(r => r.Bookings)
.HasForeignKey(b => b.RoomId);
}
}
When MessageBox.Show is called I'm getting exception: An unhandled exception of type 'System.NullReferenceException' occurred in Hotel.exe
When I'm trying to access Room::Bookings, the list is always null. There is one row in Bookings table and multiple rows in Rooms table.
How can I load all of Bookings into Room object?
Depends where you are in the learning curve, however some things stand out
Firstly
You either want to create a relationship via FluentApi or Annotations, not both
Ie. you have this on your Room entity
[ForeignKey("RoomId")]
And this in fluent
modelBuilder.Entity<Booking>()
.HasRequired(b => b.Room)
.WithMany(r => r.Bookings)
.HasForeignKey(b => b.RoomId);
You need to pick one or the other, otherwise you may end-up with multiple Ids in your Booking i.e RoomId and Room_Id
Secondly
If you want to be able to Lazy Load bookings you need to make Bookings collection Virtual
public virtual List<Booking> Bookings { get; set; }
Lastly
To access your data (presuming your connection string is correct)
using(var db = new HoteContext())
{
var rooms = db.Rooms.Include(x => x.Bookings).ToList();
}
Note : Although EF Lazy loads relationships, you might want to make sure you have included the Room->Booking relationship
Consider the following code.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
using (MyDbContext dbContext = new MyDbContext())
{
dbContext.Departments.Add(new Department()
{
Name = "Some Department1",
Employees=new List<Employee>()
{
new Employee() { Name = "John Doe" }
}
});
dbContext.SaveChanges();
var department = dbContext.Departments.FirstOrDefault(d => d.Name == "Some Department1");
if (department.Employees != null)
{
foreach (var item in department.Employees)
{
Console.WriteLine(item.Name);
}
}
}
}
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public List<Employee> Employees { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
}
}
If you have the code in above way, the control will not go into if condition, because department.Employees is null. Now, change the Department entity as follows.
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Employee> Employees { get; set; }
}
And now you should be able to see control go into if condition and outputs the employees name.
That is called Lazy Loading.
If you want to eagerly load, you don't have to put virtual to the property. You can Include the properties as follows.
var department = dbContext.Departments.Include(d => d.Employees).FirstOrDefault(d => d.Name == "Some Department1");
Now you can see the employees names are getting outputted.
You will absolutely run into performance trouble with your design here.
The temptation with EF is to completely map your object model to the DB and have EF do all the magic for you behind the scenes. But you need to think about it in terms of only getting specifically what you need from the db at any point in time. Otherwise you will get all kinds of cartesian product issues. I highly suggest you get yourself a copy of Hibernating Rhino's EF Profiler or similar so you can analyze your code statically and at runtime for EF performance issues (and see what SQL it is generating). For this what you want is a purpose built call to the DB to get the count. Otherwise what will happen is you will pull the entire table of Bookings and then have C# give you the count. That only makes sense if you want to do something with the whole list. Two options would be:
1) Create a VIEW against the Bookings table and map that to EF. The view would look something like SELECT ROOMS.ROOMID, COUNT(*) - you map this view to your model and voila now you have a list of counts by room (id) and you can use them individually or sum it up to get your total count for all rooms. If you have 1,000 bookings in 10 rooms, you are getting back only 10 rows from the DB. Whereas with your design, you are pulling back all 1,000 bookings with all their fields and then filtering down in C#. Bad juju.
2) The architecturally and conceptually simpler approach is going to be to do a direct query as such (obviously this returns only a single int from the db):
public int BookingsCount()
{
int count = 0;
try
{
using (var context = new HotelContext())
{
var sql ="SELECT COUNT(*) FROM Bookings WHERE ROOMID=" + this.RoomId;
count = context.Database.SqlQuery<int>(sql).First();
}
} catch (Exception ex)
{
// Log your error, count will be 0 by default
}
return count;
}
A simple solution would be making the Bookings property virtual.
public class Room
{
[Key]
public int RoomId { get; set; }
public int Number { get; set; }
public int Size { get; set; }
public bool HasBalcony { get; set; }
public int Beds_1 { get; set; }
public int Beds_2 { get; set; }
public double DayPrice { get; set; }
public virtual List<Booking> Bookings { get; set; }
}
More information on Entity Framework Loading Related Entities,
https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
I am trying to get away from the Entity Framework since I have to support HANA Databases aside from SQL server Databases in our solution.
I am doing some research with dapper so I created a quick test environment with some fictitious scenario.
I have the following POCOs that resemble my Database schema (I have more but I limited to showing these for simplicity):
public class Adopter
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public State State { get; set; }
public int StateId { get; set; }
public string Zip { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
public IEnumerable<Pet> Pets { get; set; }
}
public class State
{
public int Id { get; set; }
public string Name { get; set; }
public string Abreviation { get; set; }
}
public class Pet
{
public int Id { get; set; }
public string IdTag { get; set; }
public string Name { get; set; }
public DateTime AdmitionDate { get; set; }
public Status Status { get; set; }
public int StatusId { get; set; }
public string Notes { get; set; }
public DateTime AdoptionDate { get; set; }
public bool IsAdopted { get; set; }
public int? AdopterId { get; set; }
public int Age { get; set; }
public decimal Weight { get; set; }
public string Color { get; set; }
public Breed Breed { get; set; }
public int BreedId { get; set; }
public Gender Gender { get; set; }
public int GenderId { get; set; }
public IEnumerable<PetImage> PetImages { get; set; }
}
public class Status
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class Gender
{
public int Id { get; set; }
public string Name { get; set; }
}
I am using the following in a repository to return a list of all the adopters:
using (SqlConnection connection = new SqlConnection(_connectionString))
{
var adopters = connection.Query<Adopter>("SELECT a.* FROM Adopters a");
foreach (var adopter in adopters)
{
adopter.State = connection.QueryFirst<State>("Select s.* FROM States s WHERE s.Id = #Id", new { Id = adopter.StateId });
adopter.Pets = connection.Query<Pet>("Select p.* FROM Pets p WHERE p.AdopterId = #Id", new { Id = adopter.Id });
foreach (var pet in adopter.Pets)
{
pet.Status = connection.QueryFirst<Status>("Select s.* FROM Status s WHERE s.Id = #Id", new { Id = pet.StatusId });
pet.Gender = connection.QueryFirst<Gender>("Select g.* FROM Genders g WHERE g.Id = #Id", new { Id = pet.GenderId });
}
}
return adopters;
}
As you can see, I am retrieving the data for each POCO individually based on the previous one and doing the Joins manually in code.
Is this the right way of doing it or should I be doing a big query with multiple joins and mapping the result somehow thru dapper and LINQ?
A possible improvement to your actual solution is through the use of QueryMultiple extension like this:
using (SqlConnection connection = new SqlConnection(_connectionString))
{
string query = #"SELECT * FROM Adopters;
SELECT * FROM States;
SELECT * FROM Pets;
SELECT * FROM Status;
SELECT * FROM Genders;";
using (var multi = connection.QueryMultiple(query, null))
{
var adopters = multi.Read<Adopter>();
var states = multi.Read<State>();
var pets = multi.Read<Pet>();
var statuses = multi.Read<Status>();
var genders = multi.Read<Gender>();
foreach (Adopter adp in adopters)
{
adp.State = states.FirstOrDefault(x => x.Id == adp.StateID);
adp.Pets = pets.Where(x => x.IsAdopted &&
x.AdopterID.HasValue &&
x.AdopterID.Value == adp.AdopterID)
.ToList();
foreach(Pet pet in adp.Pets)
{
pet.Status = statuses.FirstOrDefault(x => x.Id == pet.StatusID);
pet.Gender = genders.FirstOrDefault(x => x.Id == pet.GenderID);
}
}
}
}
The benefit here is that you reach the database just one time and then process everything in memory.
However this could be a performance hit and a memory bottleneck if you have a really big data to retrieve, (and from a remote location). Better to look closely at this approach and try also some kind of Async processing and/or pagination if possible.
I don't like to be negative, but... don't do this! Don't even think like this. You want to dump EF, but you're walking into the trap by wanting to emulate EF. The bridge between your app and your DB is not something to be built once for all time, for every conceivable purpose. Concretely, you shouldn't really ever bring back a whole table, and certainly not to then loop on every row and emit more queries. You may feel unjustly criticised, you were just testing the tools ! If so, perhaps tell us what aspect of the tool your examining, and we'll focus in on that.
Dapper or QueryFirst greatly simplify running queries, and consuming the results, so bring back just what you need, just when you need it. Then denormalize a little, for the specific job in hand. Why are there no joins in your queries? RDBMSs are amazing, and amazingly good at doing joins. If you're joining data outside the DB, crazy is the only word, even if Linq gives you a super (sql-like) syntax for doing it. The unthinking assumption that 1 table corresponds to 1 class is the start of a lot of problems.
I'm trying to get data in a suitable format for an api
What I would like is
Place
--Rating1
---RatingImage1.1
---RatingImage1.2
---UserName
---UserId
--Rating2
---RatingImage2.1
---RatingImage2.2
---UserName
---UserId
In a nutshell im trying to fetch a place, with its ratings(and rating images), with the names of the users who did the rating given the googlePlaceId
Tried this but it goes and does some circular fetching where once it fetches the user it then fetches the user rating and the response becomes massive
context.Places
.Include(x => x.Ratings.Select(y => y.User))
.Include(x => x.Ratings.Select(c => c.RatingImages))
.Single(x => x.GooglePlaceId == googlePlaceId);
I think projection or linq joins must be the way, but i havent had any success yet.
here are my POCOS
Place Poco
public class Place
{
public Place()
{
Ratings = new List<Rating>();
Favourites = new List<Favourite>();
}
public int Id { get; set; }
public string Name { get; set; }
public string GooglePlaceId { get; set; }
public ICollection<Rating> Ratings { get; set; }
public ICollection<Favourite> Favourites { get; set; }
}
Rating POCO
public class Rating
{
public Rating()
{
RatingImages = new List<RatingImage>();
}
public int Id { get; set; }
public float RatingValue { get; set; }
public string RatingComment { get; set; }
public int PlaceId { get; set; }
public Place Place { get; set; }
public string UserId { get; set; }
public AspNetUser User { get; set; }
public ICollection<RatingImage> RatingImages { get; set; }
}
User POCO
public partial class AspNetUser
{
public string UserName { get; set; }
public string Id { get; set; }
// the rest of the fields are omitted
}
Although you've omitted the definition of AspNetUser, I'm guessing it has a navigation property back to Ratings. Is this required anywhere else in your application? It won't affect the structure of your database, and removing it would allow your projection to work exactly as you've got it here. You'd still be able to display all ratings by a single user using a separate query - you've got to optimise for your most common scenario though.