EF6 Select with Many to Many relationship - linq

I am new to EF attempting to retrieve results that require a many to many relationship.
This is the schema
This is the SQL version of what I'm trying to get with LINQ
select v.ID ViewID, ve.Title, ve.VersionID, r.Role, vr.RoleID
from [View] v, Roles r, Versions ve, View_Roles vr
where v.ID = vr.ViewID
and r.ID = vr.RoleID
and ve.ContentStatusID = 2
and ve.ViewID = v.ID
order by r.Role
This is the results view of the above
This is how the View_Roles table is manifested in my context file
modelBuilder.Entity<Role>()
.Property(e => e.Role1)
.IsUnicode(false);
modelBuilder.Entity<Role>()
.HasMany(e => e.Views)
.WithMany(e => e.Roles)
.Map(m => m.ToTable("View_Roles").MapLeftKey("RoleID").MapRightKey("ViewID"));
This is Role.cs
public class Role
{
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Role()
{
Users_Roles = new HashSet<Users_Roles>();
Views = new HashSet<View>();
}
public int ID { get; set; }
[Column("Role")]
[Required]
[StringLength(50)]
public string Role1 { get; set; }
public bool IsAdminRole { get; set; }
public int OrderBy { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Users_Roles> Users_Roles { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<View> Views { get; set; }
}
This is View.cs
[Table("View")]
public class View
{
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public View()
{
PermanentRedirects = new HashSet<PermanentRedirect>();
Users_Roles = new HashSet<Users_Roles>();
Versions_View = new HashSet<Versions_View>();
View_Links = new HashSet<View_Links>();
View_Localized = new HashSet<View_Localized>();
View1 = new HashSet<View>();
ViewGroups = new HashSet<ViewGroup>();
ViewGroups1 = new HashSet<ViewGroup>();
languages = new HashSet<language>();
Roles = new HashSet<Role>();
}
[StringLength(32)]
public string ID { get; set; }
public bool HideFromNavigationOverride { get; set; }
[StringLength(32)]
public string ParentID { get; set; }
public int? ThemeID { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PermanentRedirect> PermanentRedirects { get; set; }
public virtual Theme Theme { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Users_Roles> Users_Roles { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Versions_View> Versions_View { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<View_Links> View_Links { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<View_Localized> View_Localized { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<View> View1 { get; set; }
public virtual View View2 { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ViewGroup> ViewGroups { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ViewGroup> ViewGroups1 { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<language> languages { get; set; }
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Role> Roles { get; set; }
}
This is the LINQ I'm attempting but since I don't understand how to use the View_Roles table it isn't getting what I need, certainly.
return (
from v in PagesContext.Versions
from r in PagesContext.Roles
where v.ContentStatusID == 2 && r.IsAdminRole == false
select new RestrictedPage
{
ViewID = v.ViewID,
Title = v.Title,
RoleID = r.ID,
Role = r.Role1,
VersionID = v.VersionID
}
).ToList();

I think one way you can achieve what you are trying is doing the following:
var query= PagesContext.Versions.Where(ve=>ve.ContentStatusID == 2)
.SelectMany(ve=>ve.View.Roles
.Where(r=>r.IsAdminRole == false)
.Select(r=> new RestrictedPage
{
ViewID = ve.ViewID,
Title = ve.Title,
RoleID = r.ID,
Role = r.Role1,
VersionID = ve.VersionID
})).ToList();
In your case the junction table is not mapped directly, it's hide, so one solution to get the data related you need is using SelectMany extension method. First apply the condition to one of the to ends of your query, in my example was Versions and then apply SelectMany, that is going generate an inner join between both tables and to flatten the result in one collection.
Update
I think the issue is because Version and View are not really related directly in your DB, so you are going to do an explicit inner join:
var query= PagesContext.Versions.Where(ve=>ve.ContentStatusID == 2)
.Join( PagesContext.Views, ve=>ve.ViewId, v=>v.ID,(ve,v)=>v)
.SelectMany(v=>v.Roles
.Where(r=>r.IsAdminRole == false)
.Select(r=> new RestrictedPage
{
ViewID = ve.ViewID,
Title = ve.Title,
RoleID = r.ID,
Role = r.Role1,
VersionID = ve.VersionID
})).ToList();

Related

How to join 3 tables in linq and get some not included fields in query

I'm implementing asp.net core project. I have 3 tables Apiapp, ApiAppHistory and EntityType. There are three fields with the names SentType, Status and Reason in ApiAppHistory and those fields are of kind Id (int type) in APIApphistory. I joined ApiApp and ApiAppHistory tables in order to get those three fields from ApiAppHistory but because they are of kind int and are unclear when showing the result to the user, I join them with EntityType table which has their related name. In the select part of my query, in addition to ApiApp fields I also need to have SentType, Status and Reason value fields.
Here below is my incomplete query:
var qq = _context.Apiapp
.Include(a => a.Api)
.Include(a => a.Application)
.Include(a => a.Data);
var t12 = (from r in qq
from b in _context.ApiAppHistory
from s in _context.EntityType
where r.LastRequest== b.Id && b.SentType == s.Id
&& b.Reason == s.Id
&& b.Status == s.Id
select new { r, s.name for Reason, s.name for
SentType ,s.name for Status});
I want in select part of my query, obtain name of the fields that I specified from the EntityType table. However, I don't know how to do it. I appreciate if someone helps me.
Here is my EntityType table:
Here are my APIAppHistory and EntityType class model:
public partial class ApiAppHistory
{
public int Id { get; set; }
public int? SentType { get; set; }
public int? Reason { get; set; }
public int? Status { get; set; }
public virtual Apiapp ApiApp { get; set; }
public virtual EntityType StatusNavigation { get; set; }
public virtual EntityType SentTypeNavigation { get; set; }
public virtual EntityType ReasonNavigation { get; set; }
}
public partial class EntityType
{
public EntityType()
{
ApiAppHistoryStatusNavigation = new HashSet<ApiAppHistory>();
ApiAppHistorySentTypeNavigation = new HashSet<ApiAppHistory>();
ApiAppHistoryReasonNavigation = new HashSet<ApiAppHistory>();
}
public int Id { get; set; }
public string Name { get; set; }
public string EntityKey { get; set; }
public virtual ICollection<ApiAppHistory> ApiAppHistoryStatusNavigation { get; set; }
public virtual ICollection<ApiAppHistory> ApiAppHistorySentTypeNavigation { get; set; }
public virtual ICollection<ApiAppHistory> ApiAppHistoryReasonNavigation { get; set; }
}
}

The property is not a navigation property of entity type

I've designed my entities attached in the below diagram.
For this schema I would have written following query in sql to get all the roles, activities, applications for this user in the following way
select * from users u, roles r, userroles ur, roleappactivities raa, applications ap, activities ac
where u.Id = ur.UserId
and ur.RoleId = r.Id
and r.Id = raa.RoleId
and raa.ApplicationId = ap.Id
and raa.ActivityId = ac.Id
and u.id = 1
For the same to be achieved in my core application, I've written following code, which is failing. I ran out of ideas of how to achieve the above query through the following code. Any help much appreciated.
_context.Users
.Include("Roles")
.Include("RoleAppActivities")
.Include("Applications")
.Include("Activities")
.Where(x => x.Id == id)
.Select(x => new User
{
Id = x.Id,
TId = x.TId,
Roles = x.Roles
})
Edit:
Here are my entities
public class User
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserRole> Roles { get; set; }
}
public class UserRole
{
public int UserId { get; set; }
public User User{ get; set; }
public int RoleId { get; set; }
public Role Role{get; set;}
}
public class Role
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<UserRole> Users { get; set; }
public ICollection<RoleApplicationActivity> RoleApplicationActivity { get; set; }
}
public class RoleApplicationActivity
{
public int Id { get; set; }
public int RoleId { get; set; }
public int ApplicationId { get; set; }
public int ActivityId { get; set; }
public Activity Activity { get; set; }
public Application Application { get; set; }
public Role Role { get; set; }
}
public class Activity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<RoleApplicationActivity> RoleApplicationActivity { get; set; }
}
public class Application
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<RoleApplicationActivity> RoleApplicationActivity { get; set; }
}
I think that your entity User should have the others collections (Roles ,
RoleAppActivities..). So you can directly load them by using .include(user=> user.Collection) , i think it is more strongly typed than using the .include("string)"..
I've to modify my query to following to fix the issue.
from u in _context.Users
from r in _context.Roles
from app in _context.Applications
from ac in _context.Activities
where u.Id == 1
select u

How to perform a LINQ join on IQueryable<T> in odata controller

I have a WebAPI method which uses the OData Query Options to return data to the client. The entity has cityid and I want cityname from another entity using joins.
I have tried using below Api method, which is incomplete.
Entity1:-
public partial class UU_DeliveryCharges
{
public int DeliveryChargeId { get; set; }
public Nullable<int> CityId { get; set; }
public Nullable<int> VehicleTypeId { get; set; }
public Nullable<decimal> MileRate { get; set; }
public Nullable<decimal> FlatRate { get; set; }
public Nullable<decimal> FlatMile { get; set; }
public Nullable<decimal> PickUpFee { get; set; }
public Nullable<decimal> DropOffFee { get; set; }
public Nullable<int> CreateBy { get; set; }
public Nullable<System.DateTime> SysDate { get; set; }
public Nullable<bool> Status { get; set; }
}
Entity2:-
public partial class SC_Cities
{
public int CityId { get; set; }
public Nullable<int> StateId { get; set; }
public string CityName { get; set; }
public Nullable<bool> CityStatus { get; set; }
}
WebApi Method:-
public IQueryable<UU_DeliveryCharges> GetUU_DeliveryCharges()
{
//var aaa= db.UU_DeliveryCharges;
//return aaa;
var results = (from deliveries in db.UU_DeliveryCharges
join vehicles in db.UU_VehicleTypes on deliveries.VehicleTypeId equals vehicles.VehicleTypeId
join cities in db.SC_Cities on deliveries.CityId equals cities.CityId
join states in db.SC_States on cities.StateId equals states.StateId
join countries in db.SC_Countries on states.CountryId equals countries.CountryId
where (deliveries.Status == true)
select new { deliveries = deliveries.FlatMile, deliveries.MileRate, deliveries.PickUpFee, deliveries.DropOffFee,
vehicles=vehicles.VehicleType, cities = cities.CityName, states = states.StateName, countries = countries.CountryName
}).ToList();
return results;
}
Also tried by creating a new class as given below but not getting the right way, TIA.
public partial class DeliveryCharges
{
public string ReturnCode { get; set; } //-1:Error/0:missing or validation /1:success
public string ReturnMessage { get; set; }
public string CountryName { get; set; }
public string StateName { get; set; }
public string CityName { get; set; }
public string VehicleName { get; set; }
public Nullable<decimal> MileRate { get; set; }
public Nullable<decimal> FlatRate { get; set; }
public Nullable<decimal> FlatMile { get; set; }
public Nullable<decimal> PickUpFee { get; set; }
public Nullable<decimal> DropOffFee { get; set; }
}
Edit:-
I have replaced Api method by below method, which in turn evaluate the joins but not returning as a result.
public IQueryable<DeliveryCharges> GetUU_DeliveryCharges()
{
//var aaa= db.UU_DeliveryCharges;
//return aaa;
var results = from deliveries in db.UU_DeliveryCharges
join vehicles in db.UU_VehicleTypes on deliveries.VehicleTypeId equals vehicles.VehicleTypeId
join cities in db.SC_Cities on deliveries.CityId equals cities.CityId
join states in db.SC_States on cities.StateId equals states.StateId
join countries in db.SC_Countries on states.CountryId equals countries.CountryId
where (deliveries.Status == true)
select new DeliveryCharges
{
FlatRate = deliveries.FlatRate,
MileRate = deliveries.MileRate,
PickUpFee = deliveries.PickUpFee,
DropOffFee = deliveries.DropOffFee,
CityName = cities.CityName
//UU_DeliveryCharges = deliveries.FlatMile, deliveries.MileRate, deliveries.PickUpFee, deliveries.DropOffFee
//,vehicles = vehicles.VehicleType, cities = cities.CityName, states = states.StateName, countries = countries.CountryName
};
return results;
}
WebApiConfig :-
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<UU_DeliveryCharges>("UU_DeliveryCharges");
config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
}

Entity FrameWork many-to-many

public class Project
{
public virtual int ID { get; set; }
[Required]
public virtual String Title { get; set; }
public String Definition { get; set; }
public DateTime StartDate { get; set; }
[Required]
public int CreaterID { get; set; }
public virtual ICollection<Status> Status { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
public virtual ICollection<User> Users { get; set; }
public Project()
{
Users = new HashSet<User>();
}
}
public class User
{
public int ID { get; set; }
[DisplayName("Kullanıcı Adı")]
[Required]
[MinLength(5, ErrorMessage = "Kullanıcı Adı En Az 5 Karakter Olmalıdır")]
public string username { get; set; }
[DataType(DataType.Password)]
[DisplayName("Şifre")]
[Required]
[MinLength(3,ErrorMessage="Şifre En Az 3 Karakter Olmalıdır")]
public string password { get; set; }
[Required]
public String Name { get; set; }
[Required]
public String Surname { get; set; }
public int? CreaterID { get; set; }
public int level { get; set; }
public ICollection<Task> Tasks { get; set; }
public ICollection<Project> Projects { get; set; }
public User()
{
Projects = new HashSet<Project>();
}
}
public class TaskDB : DbContext
{
public DbSet<Comment> Comments { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<Situation> Situaitons { get; set; }
public DbSet<Task> Tasks { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>().
HasMany(c => c.Users).
WithMany(p => p.Projects).
Map(
m =>
{
m.MapLeftKey("ProjectId");
m.MapRightKey("UserId");
m.ToTable("ProjectUser");
});
}
}
If I add project , current user added to project users list but project not added current user's projects list
This is my project add code
[HttpPost]
public ActionResult Create(Project proje,Status status)
{
proje.StartDate = DateTime.Now;
proje.Status = new HashSet<Status>();
var user = _db.Users.Single(r=> r.ID == UserRole.ID);
proje.Users.Add(user);
proje.Status.Add(status);
user.Projects.Add(proje);
if (ModelState.IsValid)
{
var projeler = _db.Projects;
projeler.Add(proje);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(proje);
}
I Search this problem's cause I did not find , I want to learn why entity framework add user to project's list but not add project to user's list
Your code to add the new project to the database looks correct and the relationship is most likely stored.
But possibly you don't load the Projects list with a User. If you call...
var project = _db.Projects.Single(p => p.ID == 1);
var users = project.Users; // lazy loading because Users is virtual
...you will see the project's users because they get lazily loaded since the Project.Users property is marked as virtual. If you do the same with a User...
var user = _db.Users.Single(u => u.ID == 1);
var projects = user.Projects; // no lazy loading because Projects is not virtual
...the projects don't get loaded because the User.Projects property is not marked as virtual.
Either mark the property as virtual as well to enable lazy loading for the User.Projects collection:
public virtual ICollection<Project> Projects { get; set; }
Or use eager loading:
var user = _db.Users.Include(u => u.Projects).Single(u => u.ID == 1);

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