Entity Framework/LINQ: Selecting columns from multiple tables? - linq

Models:
public class User
{
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Resource
{
[Key]
public int ResourceId { get; set; }
public string ResourceName { get; set; }
public string ResourceDescription { get; set; }
}
public class UserResource
{
[Key, Column(Order=0)]
public int UserId { get; set; }
[Key, Column(Order=1)]
public int ResourceId { get; set; }
public int ResourceQuantity { get; set; }
}
I want to select "ResourceName" from Resource model and "ResourceQuantity" from UserResource model for a given "UserId". Also, once selected, do I need a brand new model to carry only those two specified columns?
Also note that UserResource model has a composite key so I am confused as to how to make the join... Is this right?
var userResources =
from r in imDB.Resources
join ur in imDB.UserResources
on r.ResourceId equals ur.ResourceId
select new { r.ResourceName, ur.ResourceQuantity };

Hence you're using Code first you can create your models are as below by using EF conventions.
public class User {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Resource> Resources { get; set; }
}
public class Resource {
public int Id { get; set; }
public string ResourceName { get; set; }
public int ResourceQuantity { get; set; }
public virtual ICollection<User> Users {get;set;}
}
Then EF will generate your junction table is as UsersResources.You don't need to create additional model as you did.EF will look after that.
When using POCOs with EF, if you mark your navigation properties as
virtual you can use additional EF supports like Lazy Loading. So in
general use a virtual keyword in navigation properties considered to
be a good practice.
UPDATE
You may try something like below:
Method 1 : Method based syntax
imDB.Resources.Where(r => r.Users.Any(u => u.UserId == userId))
Method 2 : Query based syntax
from r in imDB.Resources
from u in r.Users
where u.UserId == userId
select r;
I hope this will help to you.

Related

EF Core non-existant column in query, how to rectify?

working my way through an EF core project I have inherited and am quite new when it comes to LINQ/EF core.
I'll cut this back to simplify things, and will demonstrate my problem with 4 basic tables.
Customer
CustomerId
Name
Contact
1
John
john#gmail.com
2
Peter
peter#gmail.com
CustomerTrade
Id
CustomerId
OtherDetail
T1
1
xyz
T2
1
abc
CustomerTradeParameter
ParamID
TradeId
Value
X
1
1234
Y
1
5678
CustomerTradeParameterType
ParamID
Name
OtherGenericInfo
X
Hello
Null
Y
Test
Null
Models
public class Customer : AuditableEntity
{
public long CustomerId { get; private set; }
public string Name { get; private set; }
public string Contact { get; private set; }
public virtual ICollection<CustomerTrade> CustomerTradeList{ get; set; }
protected Customer()
{ }
public class CustomerTrade : AuditableEntity
{
public long Id { get; private set; }
public long CustomerId { get; private set; }
public string OtherDetail { get; private set; }
public virtual ICollection<CustomerTradeParameter> CustomerTradeParameterList { get; set; } = new List<CustomerTradeParameter>();
protected CustomerTrade() // For EF Core
{ }
public class CustomerTradeParameter : AuditableEntity
{
public long TradeId { get; set; }
public string ParameterType { get; private set; }
public string Value{ get; private set; }
protected CustomerTradeParameter() // For EF Core
{ }
}
DTOs
public class CustomerTradeDto : AuditableDto
{
public long Id { get; set; }
public long CustomerId { get; set; }
public virtual ICollection<CustomerTradeParameterDto> CustomerTradeParameterList { get; set; } = new List<CustomerTradeParameterDto>();
}
public class CustomerTradeParameterDto : AuditableDto
{
public long TradeId { get; set; }
public string ParameterType { get; set; }
public string Value { get; set; }
}
The below query is attempting to retrieve a specific trade, and a list of its relevant parameters.
IQueryable<CustomerTradeDto> foundTrade = _database.CustomerTrade
.Select(trade => new CustomerTradeDto
{
Id = trade.Id,
CustomerId = trade.CustomerId,
CustomerTradeParameterList = trade.CustomerTradeParameterList.Select(param => new CustomerTradeParameterDto{
ParameterType = param.ParameterType,
TradeId = customerTrade.Id,
Value = param.Value
// (second question written further below) - If I wanted to additionally retrieve the CustomerTradeParameterType record
// (to get the name of this parameter), how would I embed this join?
// I originally had ParameterType defined as a "CustomerTradeParameterType" instead of a string,
// but am not sure how to add this extra join inside this select?
}).ToList()
})
.Where(e => e.Id == find.Id);
The .Select on CustomerTradeParameterList is raising the following error:
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'CustomerTradeId'.
I understand EF core is attempting to define a column that it is unsure of, but I am not sure how to fix this? The correct column is TradeId.
Secondly, I have an additional question regarding joins inside a sub .Select. In the query above I have a comment outlining the question.
Appreciate any tips you can provide!

Data Annotation for foreign key relationship

I have two classes
public class Project
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int ManagerID { get; set; }
public int CoordID { get; set; }
[ForeignKey("ManagerID")]
public virtual Employee Manager { get; set; }
[ForeignKey("CoordID")]
public virtual Employee Coord { get; set; }
}
public class Employee
{
[Key]
public int EmpID { get; set; }
public string Name { get; set; }
[InverseProperty("ManagerID")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("CoordID")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
The ManagerID and CoordID map to the EmpID column of the Employee table.
I keep getting an error for Invalid Columns becauce EF is not able to map correctly. I think it is looking for wrong column.
I think InverseProperty is used to refer to the related navigation property, not the foreign key, e.g.
public class Employee
{
[Key]
public int EmpID { get; set; }
public int Name { get; set; }
[InverseProperty("Manager")]
public virtual ICollection<Project> ManagerProjects { get; set; }
[InverseProperty("Coord")]
public virtual ICollection<Project> CoordProjects { get; set; }
}
Also, is there a reason why your names are ints and not strings?
Best guess would be to use fluent API in your context via OnModelCreating. By renaming the column, EF can't figure out the original object to map so it's confused. However, Fluent API allows you to manually specify the map using something like the following:
public class MyContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Project> Projects { get; set; }
protected override OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasRequired(x => x.Manager)
.WithMany(x => x.ManagerProjects)
.HasForeignKey(x => x.ManagerID);
modelBuilder.Entity<Project>()
.HasRequired(x => x.Coord)
.WithMany(x => x.CoordProjects)
.HasForeignKey(x => x.CoordID);
}
}

EF Code First and linq extension methods - foreign keys, am i doing it wrong?

I have something like this: (pseudocode)
public class Author
{
int id;
public List<Thread> Threads;
public List<ThreadPoints> ThreadPointses;
}
public class Thread
{
int id;
public List<ThreadPoints> ThreadPointses;
}
public class ThreadPoints
{
int id;
int Points;
}
And i am not sure if above is correct, but now i would want to obtain number of points' that specified Author have in specified Thread.
I cannot directly call ThreadPoints.Thread_id, because it's not accessible, even if it physically is in the database.
So do i need to change my model, or am i unaware of some useful methods?
So basically, my model looks like that:
public class Account
{
[Key]
public Guid AccountId { get; set; }
public string UserName { get; set; }
public List<Post> Posts { get; set; }
public List<Post> ModifiedPosts { get; set; }
public List<Thread> Threads { get; set; }
public List<ThreadPoints> ThreadPointses { get; set; }
public List<Thread> LastReplied { get; set; }
public int Points { get; set; }
}
public class Thread
{
[Key]
public int Id { get; set; }
public List<ThreadPoints> ThreadPointses { get; set; }
public List<Post> Posts { get; set; }
public int CurrentValue { get; set; }
public int NumberOfPosts { get; set; }
public int TotalValue { get; set; }
public int Views { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
}
public class ThreadPoints
{
[Key]
public int Id { get; set; }
public int Points { get; set; }
}
And what i need, is, when user creates a thread, he gives some amount of points into it. In the next action, i want to take that amount of points (from database), and increase it. But i only have thread id as input information.
Your answer might be good, (as far i am trying to implement it), but anyways, i am not sure about this model. Maybe i should manually add foreign keys into my model? It surely would be simpler, but then i would have two foreign keys in my database...
Since you're not explicitly mapping your FK's, entity framework is generating them and hiding them away, so to get to the Id's of the properties, you'll need to follow the navigation collections.
I'm not sure about your question, but are you wanting the number of Points, inside of a specific Threadpoint for a given author? Your model doesn't seem to support this very well, but you could do something like this-
public int GetPoints(Author author, Thread thread)
{
int points = author.Threads.FirstOrDefault(t => t.id == thread.id).ThreadPointses.Sum(tp => tp.Points);
}
This would return the sum of all the points contained in the list of threadpoints, which are contained in the list of threads with the same id as the thread you passed in, for the specified author.
If this doesn't work for you - can you post your actual model?

Entity Framework POCO Relationships

I am trying to implement code-first approach of entity framework. I have four entities UserInfo, Client, Admin and Account. I want relationships as:
Each Client has a UserInfo
Each Admin has a `UserInfo
Each Account is linked with a User(UserInfo)
Assuming these things i wrote the POCO models. With the relationships i want, is it correct ?Am i missing something?
public class UserInfo
{
public int UserInfoID { get; set; }
public Name Name { get; set; }
public Address Address { get; set; }
public Contact Contact { get; set; }
}
public class Admin
{
public int AdminID { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
public class Client
{
public int ClientID { get; set; }
public CompanyDetails CompanyDetails { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
public class Account
{
public int AccountID { get; set; }
[Required, Column("Balance"), Display(Name = "Account Balance")]
public double Balance { get; set; }
public int UserInfoID { get; set; }
[ForeignKey("UserInfoID")]
public virtual UserInfo UserInfo { get; set; }
}
What you have appears to be correct based on your requirements however I personally prefer the Entity Framework Model Builder when configuring your entities with Code First.
Using the model builder means that you don't have any attributes on your POCO entities which in turn means that you don't need an EF reference to use the entities.
Take a look at my article here for some more info on how to use the modelbuilder : http://blog.staticvoid.co.nz/2012/07/entity-framework-navigation-property.html

EF 4.1 POCO query

I have this POCO and I want to return a list of the users in a particular company.
public class Company
{
public AccreditedCompany()
{
this.Branches = new HashSet<Branch>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int CompanyId { get; set; }
public bool Active { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
}
public class Branch
{
public Branch()
{
this.Users = new HashSet<User>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int BranchId { get; set; }
public int CompanyId { get; set; }
public string Name { get; set; }
public string ContactName { get; set; }
public virtual Company Company { get; set;}
public virtual ICollection<User> Users { get; set; }
}
public class User
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), ScaffoldColumn(false)]
public int UserId { get; set; }
public int BranchId { get; set; }
public string ComputerSN { get; set; }
public string CameraSN { get; set; }
public virtual Branch Branch { get; set; }
}
This is my LINQ query:
var company = (from u in objDataContext.Companies.Include(c=>c.Branches.Select(v=>v.Users))
where u.CompanyId == 8 select u).FirstOrDefault();
IQueryable<User> users = (from j in company.Branches select j.Users);
I have this compilation error on the second query:
Error 2 Cannot implicitly convert type
'System.Collections.Generic.IEnumerable>'
to 'System.Linq.IQueryable'. An explicit conversion exists (are
you missing a cast?)
I want to get a list of the users, similar to a plain SQL statement like
SELECT dbo.Users.* FROM Branches
INNER JOIN dbo.Users ON dbo.Branches.BranchId = dbo.Users.BranchId
INNER JOIN dbo.Companies ON dbo.Branches.CompanyId = dbo.Companies.CompanyId
WHERE (dbo.Companies.CompanyId = 8)
Thanks in advance.
Your user query could be:
IEnumerable<User> users = company.Branches.SelectMany(branch => branch.Users);
This will return all users in any branch of the company.
It looks to me like you could just use:
IQueryable<User> users = objDataContext.Users
.Where(u => u.Branch.CompanyId == 8);
I notice you have both Company and CompanyId on your Branch entity, though. That seems redundant, even though it simplifies this query slightly. You should be able to get rid of Branch.CompanyId and User.BranchId and just use the entity associations.

Resources