How to select specific columns and join tables with Entity Framework? - c#

I need to get data from SQL Server to my data grid view in Winforms
SELECT
Managements.OrderID, Managements.BookReturnDate, Managements.Money,
Books.bookName
FROM
Managements
INNER JOIN
Users ON Users.UserID = Managements.Username_UserID
INNER JOIN
Books ON Books.bookID = Managements.Book_bookID
How can I convert the query above to a code for Entity Framework?

Use linq
var s = from management in dbContext.Managements
join user in dbContext.Users on users.UserId equals management.Username_UserID
join book in dbContext.Books on book.BookId equals management.Book_bookID
select management.OrderID, management.BookReturnDate, management.Money,
book.bookName

As far as I can see Management table has one to many with User and Book tables.
If that is the case you can add, properties to the Management model in code and include these tables when you pull data from SQL Management table.
public class Management{
public int ManagmentId { get; set; }
public int UserId { get; set;}
public List<User> Users { get; set;}
public int BookId { get; set;}
public List<Book> Books { get; set;}
}
This should be your Management class.
For the query in Entity Framework try something like this:
public Managment GetData(int managmentId){
var data = context.Management
.Include(u => u.Users)
.Include(b => b.Books)
.FirstOrDefault(m => m.Id == managmentId);
}

Related

Entity Framework Core - Not In

I'm trying to replicate a SQL statement in EF Core but cant seem to find a way to do it, to set the scene I have the following table structure
Slot -> SlotInstance -> SlotInstanceUser
(a Slot can have many SlotInstances, a SlotInstance can have many SlotInstanceUsers)
When a user registers for a SlotInstance a record is created in SlotInstanceUsers storing the SlotInstanceId and UserId - all good there.
I'm able to write SQL to get a list of slot instances which the user has not registered for e.g.
SELECT
S.StartDate, S.EndDate, S.StartTime, S.EndTime, S.DayOfWeek,
SI.Date
FROM
Slot S WITH (NOLOCK)
INNER JOIN
SlotInstance SI WITH (NOLOCK) ON S.Id = SI.SlotId
WHERE
SI.ID not in (
SELECT
SlotInstanceId
FROM
SlotInstanceUser SIU WITH (NOLOCK)
WHERE
SIU.UserId = #UserID
)
ORDER BY
SI.Date
But I just cant seem to replicate this in EF core - what am I missing?
You can write the LINQ query pretty much the same way as the SQL query. Just remember that in LINQ select is last, variables (aliases) are mandatory, and the equivalent of SQL NOT IN is !Contains. e.g.
var query =
from s in db.Slots
join si in db.SlotInstances on s.Id equals si.SlotId
where !(from siu in db.SlotInstanceUsers
where siu.UserId == userId)
select siu.SlotInstanceId).Contains(si.Id)
orderby si.Date
select new
{
s.StartDate, s.EndDate, s.StartTime, s.EndTime, s.DayOfWeek,
si.Date
};
But in EF Core you have more options, especially for joins, since normally the relationships (and associated joins) are encapsulated with navigation properties. So the model you are describing with words in EF Core/C# terms is something like
public class Slot
{
public int Id { get; set; }
// Other properties...
public ICollection<SlotInstance> SlotInstances { get; set; }
}
public class SlotInstance
{
public int Id { get; set; }
// Other properties...
public Slot Slot { get; set; }
public ICollection<SlotInstanceUser> SlotInstanceUsers { get; set; }
}
public class SlotInstanceUser
{
public int Id { get; set; }
// Other properties...
public SlotInstance SlotInstance { get; set; }
}
and the query would be like
var query =
from s in db.Slots
from si in s.SlotInstances
where !si.SlotInstanceUsers.Any(siu => siu.UserId == userId)
orderby si.Date
select new
{
s.StartDate, s.EndDate, s.StartTime, s.EndTime, s.DayOfWeek,
si.Date
};
(this actually translates to SQL NOT EXISTS, but that's not essential).
And if you don't need projection, but simply slot instances (with slot info) which the user has not registered for, then it would be simply
var query = db.SlotInstances
.Include(si => si.Slot)
.Where(si => !si.SlotInstanceUsers.Any(siu => siu.UserId == userId))

How to return data from different tables using Sql Query in EF Core without using table valued functions/Linq

How can i select data from two tables using SQL query using ef core.
Below are two tables
public class Category
{
public int Id { get; set; } // Don't want to return this.
public string Name { get; set; } // Only want to return this.
}
public class Product
{
public int ProductId {get;set;}
public string Name { get; set; }
public int CategoryId {get;set;}
}
I want to execute Select P.Name , C.Name from tblCategory C JOIN tblProduct P ON C.Id = P.CategoryId
in entity framework core.
One option is to use table valued functions but i dont want to consider it?
I dont want to use linq because the sql generated is not efficient.
Above is just an example not a real scenario.
If you want to do this using EF Core instead of ADO.NET or Dapper, you can use raw Sql Queries as below:
EF Core 2.1 example:
var blogs = context.Blogs
.FromSql("SELECT * FROM dbo.Blogs")
.ToList();
In EF Core 3.1, FromSql is Obsolete, Hence use FromSqlRaw
var blogs = context.Blogs
.FromSqlRaw("SELECT * FROM dbo.Blogs")
.ToList();
Similarly, your query can be executed as
var results = context.ProductCategories.FromSqlRaw(Select P.Name as ProductName, C.Name as CategoryName from tblCategory C JOIN tblProduct P ON C.Id = P.CategoryId)
Note: Also, you need to define ProductCategories in context class.
public DbQuery<ProductCategory> ProductCategories { get; set; }
ProductCategory.cs
public class ProductCategory
{
public string ProductName { get; set; }
public string CategoryName { get; set; }
}
Here, ProductCategory is known as query type in EF Core 2.1.
In EF Core 3.0 the concept was renamed to keyless entity types. It serves as return type for raw Sql Queries.
You can refer learn.microsoft.com for more details
You can just execute an SQL statement with the query that you want:
using (var connection = new SqlConnection(_ConnectionString))
{
connection.Open();
{
string query = #"select P.Name, C.Name from tblCategory C JOIN P ON C.Id=P.CategoryId";
result = (await connection.QueryAsync<MyDto>(query)).ToList();
}
}
In EF you would normally not use a raw SQL query for something so simple.
Instead, add a Navigation Property to from Product to Category:
public class Category
{
public int Id { get; set; } // Don't want to return this.
public string Name { get; set; } // Only want to return this.
}
public class Product
{
public int ProductId {get;set;}
public string Name { get; set; }
public Category Category {get;set;}
public int CategoryId {get;set;}
}
Then you can write a LINQ query like
from p in db.Product
select new {ProductName = p.Name, CategoryName = p.Category.Name };
In Code-First, this will also ensure that you can't have a Product whose CategoryId is not the Id of some Category by creating a Foreign Key in the database, and (at least for SQL Server) create a non-clustered index on Product.CategoryId for improved performance.

Fetching a list of emails from ASPNetUser table given id from another table

I have a list of id's from a profile table. In the profile table I have a reference back to the ASPNetUser table that holds the emails addresses for each user and I need to get each email address for the users that I have an id for from the profile table. I'm looking for an easy query for this.
So I have a list if ids from the profile table (1,2,3,4,5)
YogaProfile with ref back to AspNetUser table
public class Profile
{
[Key]
public int YogaProfileId { get; set; }
[Column(TypeName = "VARCHAR")]
[StringLength(36)]
[Index]
public string ApplicationUserGuid { get; set; }
}
I'm trying to do something like this join but not sure how to include a list
List<string> emails = from s in dbContext.YogaProfiles
join c in dbContext.Users
on s.ApplicationUserGuid equals c.Id
where s.YogaProfileId in (List of ids here)
select c.Email).ToList()
You can use the Contains method.
var ids = new List<int> { 1,2 };
List<string> emails = (from s in dbContext.YogaProfiles
join c in dbContext.Users
on s.ApplicationUserGuid equals c.Id
where ids.Contains(s.YogaProfileId)
select c.Email).ToList();

Linq to Entities Join to Show Name in Lookup Table

I'm using EF Code First (hybrid, database generation disabled) and I have two models/tables. I'm try to select and return all values in T1 and one field in a reference/lookup table so I can perform filtering on the list without requerying the database. I need to have the value of ItemName available so I can do comparisons.
If I were using SQL I'd just do something like this:
SELECT s.*, im.ItemName
FROM Specs s
INNER JOIN ItemMake im ON s.ItemMakeID = im.ID
My classes look something like this:
public class Spec {
public int ID {get; set;}
public int ItemMakeID {get; set;}
[ForeignKey("ItemMakeID")]
public ItemMake itemMake {get; set;}
}
public class ItemMake {
public int ID {get; set;}
public string ItemName {get; set;}
}
Currently my Linq to EF query looks like this. It doesn't work. I can't get at the ItemName property like I need to.
var specs = (from s in db.Specs
join im in db.ItemMakes on s.ItemMakeID equals im.ID
orderby s.modelNo select s).ToList();
What am I doing wrong?
That's because you're selecting just s in select clause. Use anonymous type declaration to get ItemName too:
var specs = (from s in db.Specs
join im in db.ItemMakes on s.ItemMakeID equals im.ID
orderby s.modelNo select new { s, im.ItemName }).ToList();

Entity Framework and LINQ left join and include on same table

I have the following class structure for my Users and the permissions they're in for the different companies they may be associated to:
public class User
{
public Guid Id { get; set; }
public List<Permission> Permissions { get; set; }
public Company DefaultCompany { get; set; }
}
public class Permission
{
public User User { get; set; }
public Company Company { get; set; }
public int PermissionLevel { get; set; }
}
public class Company
{
public Guid Id { get; set; }
public string Name { get; set; }
}
This results in three SQL tables. There is a FK between Permission.User_Id > User.Id and Permission.Company_Id > Company.Id. There is no explicit relationship (ie. FK) between User.DefaultCompany and the Company table. This is on purpose due to a legacy choice in our database schema.
I also have a database repository method that grabs a user by it's Id, and includes the full Company record:
public User GetById(Guid Id)
{
return (from r in this.Context.Users.Include("Permissions.Company")
where r.Id == Id
select r)
.SingleOrDefault();
}
This works fine, but it doesn't set the DefaultCompany property. So I tried setting that by changing this method to the following. It's worth pointing out that the Company record that represents the DefaultCompany shares the same ID value as the User.
public User GetById(Guid Id)
{
return (from r in this.Context.Users.Include("Permissions.Company")
where r.Id == Id
join c in this.Context.Companies on r.Id equals c.Id into companies
from company in companies.DefaultIfEmpty()
select new { User = r, Company = company })
.ToList()
.Select(p => { p.User.DefaultCompany = p.Company; return p.User; })
.SingleOrDefault();
}
And this does, in fact, set the DefaultCompany but it has the side effect of not selecting the Permissions list. I can do this all as two separate operations, as in the following code, but I'd rather not hit the database twice if I don't have to.
public User GetById(Guid Id)
{
var u = (from r in this.Context.Users.Include("Permissions.Company")
where r.Id == Id
select r)
.SingleOrDefault();
u.DefaultCompany = (from r in this.Context.Companies where r.Id == u.Id select r).SingleOrDefault();
return u;
}
Is there another way to accomplish this?
Edit: explaining resulting SQL data model and additional example.
There are two possible solutions for this problem.
The cleanest is to use the Fluent API to indicate the model that there is a 1 to 1 relation between User and Company.
Override the OnModelCreating(DbModelBuilder modelBuilder) method of your context.
Inside it, configure the 1 ro 1 relation like this:
modelBuilder.Entity<User>()
.HasOptional(u => u.DefaultCompany)
.WithRequired();
NOTE: with this configuration there is a relationship of 1 user to 0 or 1 default companies (note the Optional in HasOptional). And the default company must have a User on the other side. When a 1 to 1 (or 1 to 0..1) relation is configured, EF will automatically use the PK of the related tables to create the relation between them. You can fine tune the relation using other Fluent API functions
After doing so, you can include the DefaultCompany using Include():
User user = ctx.Users
.Include(u => u.DefaultCompany)
.SingleOrDefault(u => u.Id == userId);
The other, more ugly solution, is to use your second query and include the missing permissions in the projection, to force EF to recover them from the DB.
// ...
select new { User = r, Company = company, Companies = r.Permissions }
// ...

Categories