.NET Core many-to-many query - asp.net-core

I have a many to many realtionship between a Project-model an a user. I'm trying to display all projects in which the logged in user is a member of, but I can't really get it right.
User Model
public class AppUser : IdentityUser
{
public ICollection<UserProject> UserProjects { get; set; }
}
Project Model
public class Project
{
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public ICollection<UserProject> UserProjects { get; set; }
}
UserProject Model
public class UserProject
{
public string UserId { get; set; }
public AppUser AppUser { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
I have tried to write the query in SQL like this but it doesn't work
AppUser user = await userManager.GetUserAsync(HttpContext.User);
var q = (from e in context.Projects
join t in context.UserProjects on e.ProjectId equals t.ProjectId
where t.UserId == user.Id
select e.ProjectName);
I have managed to write a query to get all the projects by an id and all it's members like this
var project = context.Projects
.Where(x => x.ProjectId == id)
.Include(x => x.UserProjects)
.ThenInclude(x => x.AppUser)
.First();
But I don't manage to get all the projects a user is a member of. I appriciate any help I can get, Thanks!

You can get all the projects a user is a member of like :
var userID = User.Claims.FirstOrDefault(x=>x.Type== ClaimTypes.NameIdentifier).Value;
var projects = _context.Users
.Where(p => p.Id == userID)
.SelectMany(p => p.UserProjects)
.Select(pc => pc.Project).ToList() ;
In a many-to-many relationship :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserProject>()
.HasKey(pc => new { pc.UserId, pc.ProjectId });
modelBuilder.Entity<UserProject>()
.HasOne(pc => pc.AppUser)
.WithMany(p => p.UserProjects)
.HasForeignKey(pc => pc.UserId);
modelBuilder.Entity<UserProject>()
.HasOne(pc => pc.Project)
.WithMany(c => c.UserProjects)
.HasForeignKey(pc => pc.ProjectId);
}

Related

Entity Framework ApplicationUser child entities return null

We have an ApplicationUser class which inherits from IdentityUser. An ApplicationUser can have many devices.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<Device> Devices { get; set; }
}
A Device is defined as
public partial class Device
{
[Key]
public int DeviceID { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public string DeviceName { get; set; }
}
The one-to-many relationship is set in
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Device>(entity =>
{
entity.HasKey(e => e.DeviceID);
entity.Property(e => e.DeviceName)
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar");
entity.Property(e => e.UserId)
.IsRequired()
.HasMaxLength(450);
entity.HasOne(d => d.User).WithMany(p => p.Devices).HasForeignKey(d => d.UserId).OnDelete(DeleteBehavior.Restrict);
});
public virtual DbSet<Device> Devices { get; set; }
}
The following code correctly returns the user
var membershipUser = await _userManager.FindByEmailAsync(email);
I know membershipUser has 6 devices in the Devices table, however the Devices property is null.
The following also returns null for this users devices
var deviceList = membershipUser.Devices.ToList();
How do I wire up Devices so they are correctly returned?
Lazy loading doesn't work on EF7. But You can use Include in your request.

nhibernate child collection limitation

I have these classes:
public class Document
{
public Document()
{
Descriptions = new List<Descriptions>();
}
public virtual int Id { get; set; }
public virtual IList<DocumentDescription> Descriptions { get; set; }
}
public class DocumentDescription
{
public virtual int DocumentId { get; set; }
public virtual int LanguageId { get; set; }
}
and mappings:
public DocumentMap()
{
Id(x => x.Id);
HasMany(x => x.Descriptions).KeyColumn("DocumentId");
}
public DocumentDescriptionMap()
{
CompositeId()
.KeyProperty(x => x.DocumentId)
.KeyProperty(x => x.LanguageId);
}
my query is:
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds)).List();
I need a query over solution to restrict DocumentDescriptions by few languages, which I will get run-time. I don't want to get all DocumentDescriptions for one Document (only few). Is it possible to set filter/limitation for a child collection?
So, I find out how to add additional join clause to my query:
DocumentDescription dd = null;
ICriterion criterion = Restrictions.On<DocumentDescription>(x => x.LanguageId).IsIn(languageIds.ToArray());
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds));
query.Left.JoinQueryOver(x => x.Descriptions, () => dd, criterion);
SQL:
SELECT * FROM tDocument
LEFT OUTER JOIN tDocumentDescription ON tDocumentDescription.DocumentId = tDocument.Id AND tDocumentDescription.LanguageId IN (#languageIds)
WHERE tDocument.Id IN (#documentIds)

How to get records in first table(projects) not present in second table(finances) with foreign key reference using nhibernate

I'm trying to query on a simple data structure in nhibernate and MSSQL
dbo.Projects : Id(int, not null)
dbo.Finances : Id(int, not null), ProjectId(int,not null), foreign key references to dbo.projects
I want to get all the records in projects table that are not present in finances table where the finances table has a foreign key reference ProjectId.
I am migrating to (Fluent) Nhibernate 3 from EntityFramework?
//So far I have got here:
public IQueryable<ProjectModel> GetProjectsNotPresentInFinance()
{
var factory = Fluently.Configure()
.Database(MsSqlConfiguration
.MsSql2008
.ConnectionString(m_connectionString))
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<ProjectMap>()
).BuildSessionFactory();
using (var session = factory.OpenSession())
{
var allprojects = session.QueryOver<ProjectModel>();
var projectsToReturn = allprojects.List<ProjectModel>().AsQueryable();
//--- Something like : all the records not in finances table ---------
// .Where( proj => !db.Finances.Where(fin => fin.ProjectId == proj.Id).Any())
// .Select(project => new ProjectModel
// {
// Id=project.Id,
// ProjectName = project.ProjectName,
// });
return projectsToReturn;
}
}
public class FinanceModel
{
public virtual int Id { get; set; }
public virtual int ProjectId { get; set; }
}
public class ProjectModel
{
public virtual int Id { get; set; }
public virtual string ProjectName { get; set; }
}
public class ProjectMap:ClassMap<ProjectModel>
{
public ProjectMap() {
Table("Projects");
Id(x => x.Id);
Map(x => x.ProjectName);
}
}
public class FinanceMap : ClassMap<FinanceModel>
{
public FinanceMap()
{
Table("Finances");
Id(x => x.Id);
References(x => x.ProjectModel);
}
}
//-------------------------------------------------------
//This is an Equivalent working code Using EntityFramework :
public IQueryable<ProjectModel> GetProjectsNotPresentInFinance() {
IQueryable<ProjectModel> projectList = db.Projects
.Where( proj => !db.Finances.Where(fin => fin.ProjectId == proj.Id).Any())
.Select(project => new ProjectModel
{
Id=project.Id,
ProjectName = project.ProjectName,
});
return projectList;
}
//-------------------------------------------------------
On second thought, you may try this without changing anything to your mapping, using a subquery :
var notOrphanProjectIdsSubquery = QueryOver.Of<FinanceModel>()
.Select(x => x.ProjectId);
var orphanProjects = session.QueryOver<ProjectModel>()
.WithSubquery
.WhereProperty(x=>x.Id)
.NotIn(notOrphanProjectIdsSubquery)
.List();
----------------------- Initial answer
Assuming you have a mapped Finances Property in your Project class, and according to https://stackoverflow.com/a/14980450/1236044, it should be something like :
var orphanProjects = session.QueryOver<ProjectModel>()
.WhereRestrictionOn(x => x.Finances).IsEmpty()
.List();
I must confess I am not proficient with FluentNH. I guess the classes and mappings should be something like this, hoping I'm not setting you on the wrong track...
public class FinanceModel
{
public virtual int Id { get; set; }
public virtual int ProjectId { get; set; }
public virtual ProjectModel Project{get;set;}
}
public class ProjectModel
{
public virtual int Id { get; set; }
public virtual string ProjectName { get; set; }
public virtual IList<FinanceModel> Finances { get; set; }
}
public class ProjectMap:ClassMap<ProjectModel>
{
public ProjectMap() {
Table("Projects");
Id(x => x.Id);
Map(x => x.ProjectName);
HasMany(x => x.Finances);
}
}
public class FinanceMap : ClassMap<FinanceModel>
{
public FinanceMap()
{
Table("Finances");
Id(x => x.Id);
References(x => x.Project);
}
}

NHibernate Criteria Queries - how use in One to One relationship

I have simple 3 POCO classes:
public class User
{
//PK
public virtual int UserId { get; set; }
//ONE to ONE
public virtual Profil Profil{ get; set; }
//ONE to MANY
public virtual IList<PhotoAlbum> Albums { get; set; }
}
public class Profil
{
//PK
public virtual int ProfilId { get; set; }
public virtual int Age { get; set; }
public virtual int Sex { get; set; }
}
public class PhotoAlbum
{
//PK
public virtual int PhotoAlbumId { get; set; }
public virtual string Name { get; set; }
public virtual int NumberOfPhoto { get; set; }
}
I created these mapping classes:
public class UserMap : ClassMap<User>
{
public UserMap()
{
//PK
Id(p => p.UserId)
.GeneratedBy.Identity();
//FK
References(p => p.Profil)
.Column("ProfilId")
.Cascade.All();
//ONE TO MANY
HasMany(p => p.Albums)
.Cascade.All();
Table("Users");
}
}
public class ProfilMap: ClassMap<Profil>
{
public ProfilMap()
{
Id(p => p.ProfilId)
.GeneratedBy.Identity();
Map(p => p.Age)
.Not.Nullable();
Map(p => p.Sex)
Table("Profiles");
}
}
public class PhotoAlbumMap : ClassMap<PhotoAlbum>
{
public PhotoAlbumMap()
{
Id(p => p.PhotoAlbumId)
.GeneratedBy.Identity();
Map(p => p.Name)
.Not.Nullable();
Map(p => p.NumberOfPhoto)
.Not.Nullable();
Table("PhotoAlbums");
}
}
Then I created simple NHibernate repository class with this method:
public IList<T> GetItemsByCriterions(params ICriterion[] criterions)
{
ICriteria criteria = AddCriterions(_session.CreateCriteria(typeof(T)),
criterions);
IList<T> result = criteria.List<T>();
return result ?? new List<T>(0);
}
For test I created repository for some entity, for example User:
_userRepo = new NHibRepository<User>(NHibeHelper.OpenSession());
and I would like have possibility make query in this style:
var users = _userRepo.GetItemsByCriterions(new ICriterion[]
{
Restrictions.Gt("Profile.Age",10)
});
this attempt finished with error:
could not resolve property: Profile of: Repository.User
User has property Profile type of Profile and this property has properties ProfileId, Age
and sex.
** #1 EDITED:**
# I tried this:
var users = _userRepo.GetItemsByCriterions(new ICriterion[]
{
Restrictions.Where<User>(u=>u.Profil.Sex==0)
});
finished with error:
could not resolve property: Profil.Sex of: Repository.User
#2 EDITED
I tried use Nathan’s advice:
var result = _userRepo.Session.CreateCriteria<User>()
.CreateAlias("Profile", "profile", JoinType.InnerJoin)
.Add(Restrictions.Eq("profile.Sex", 0));
IList<User> users=null;
if (result != null)
users = result.List<User>();
If I tried convert result to List I again get this error: could not resolve property: Profile of: Repository.User
Looking at your example, User has a Profil property not a Profile property.
If it is supposed to be Profil then I would change the Restrictions.Gt(Profile.Age,10) to Restrictions.Gt(Profil.Age,10) otherwise change the name of the property and mapping to match the query.
Edit:
You are trying to query the User Object. you need to include the CreateAlias let nhibernate know that you want to link to a different object.
Try This.
var users = session.CreateCriteria<User>()
.CreateAlias("Profile", "profile", JoinType.InnerJoin)
.Add(Restrictions.Eq("profile.Age", 10));

Many to Many relationship with Fluent NHibernate

I'm getting the following error: "Can't figure out what the other side of a many-to-many should be."
Team Entity:
public class Team : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Employee> Employee { get; set; }
public Team()
{
Employee = new List<Employee>();
}
}
Employee Entity:
public class Employee : IEntity
{
public int Id { get; set; }
public String LastName { get; set; }
public string FirstName { get; set; }
public IList<Team> Team { get; set; }
public string EMail { get; set; }
public Employee()
{
Team = new List<Team>();
}
}
Team mapping:
public class TeamMap : ClassMap<Team>
{
public TeamMap()
{
// identity mapping
Id(p => p.Id);
// column mapping
Map(p => p.Name);
// relationship mapping
HasManyToMany<Employee>(m => m.Employee);
}
}
Employee mapping:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
// identifier mapping
Id(p => p.Id);
// column mapping
Map(p => p.EMail);
Map(p => p.LastName);
Map(p => p.FirstName);
// relationship mapping
HasManyToMany<Team>(m => m.Team);
}
}
Nobody has an answer?
Edit: The error occurs on the following code:
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c=>
c.Database("Ariha")
.TrustedConnection()
.Server("localhost")
).ShowSql())
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<BookMap>()
.AddFromAssemblyOf<MagazineMap>()
.AddFromAssemblyOf<EmployeeMap>()
.AddFromAssemblyOf<TeamMap>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
edit: here the whole solution: http://rapidshare.com/files/309653409/S.O.L.I.D.Ariha.rar.html
Could you provide code that causes your error? I just tried your mappings and they seem to work fine (on fluent 1.0 RTM and NH 2.1.1 GA with an SQLite database), with a minor modification to your EmployeeMap (I have assumed the employee-team relationship is bidirectional, and as per documentation you need to mark one side as inverse).
// relationship mapping
HasManyToMany<Team>(m => m.Team).Inverse();
Of course if the employee-team relationship is not bidirectional, I would have thought you should be able to specify a different .Table(name) for each one - but I have not tested this and you seem to be getting different results anyway (hence why providing example code would be best)
I'd also add that I suspect Set semantics (instead of Bag) would be more appropriate for the Employee.Team and Team.Employee properties. (Irregardless, don't do anything that assumes order is preserved, there is no guarantee that it will be)
Suggested mapping and example:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employee { get; set; }
public Team() { Employee = new List<Employee>(); }
}
public class Employee
{
public int Id { get; set; }
public String LastName { get; set; }
public string FirstName { get; set; }
public ICollection<Team> Team { get; set; }
public string EMail { get; set; }
public Employee() { Team = new List<Team>(); }
}
public class TeamMap : ClassMap<Team>
{
public TeamMap()
{
Not.LazyLoad();
// identity mapping
Id(p => p.Id);
// column mapping
Map(p => p.Name);
// relationship mapping
HasManyToMany<Employee>(m => m.Employee).AsSet();
}
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Not.LazyLoad();
// identifier mapping
Id(p => p.Id);
// column mapping
Map(p => p.EMail);
Map(p => p.LastName);
Map(p => p.FirstName);
// relationship mapping
HasManyToMany<Team>(m => m.Team).Inverse().AsSet();
}
}
[TestFixture]
public class Mapping
{
[Test]
public void PersistDepersist()
{
var fcfg = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile("testdb.sqldb"))
.Mappings(mc =>
{
mc.FluentMappings.Add(typeof (TeamMap));
mc.FluentMappings.Add(typeof (EmployeeMap));
})
.ExposeConfiguration(cfg => new SchemaExport(cfg).Execute(false, true, false));
var sess = fcfg.BuildSessionFactory().OpenSession();
var teams = Enumerable.Range(0, 4).Select(i => new Team() {Name = "Team " + i}).ToArray();
var employees = Enumerable.Range(0, 10).Select(i => new Employee() {FirstName = "Employee " + i}).ToArray();
teams[0].Employee = new List<Employee>() {employees[0], employees[3], employees[5]};
teams[1].Employee = new List<Employee>() {employees[7], employees[2], employees[5]};
teams[3].Employee = new List<Employee>() {employees[0], employees[8], employees[9]};
foreach (var team in teams)
foreach (var employee in team.Employee)
employee.Team.Add(team);
Console.WriteLine("Dumping Generated Team/Employees:");
Dump(teams);
Dump(employees);
using (var t = sess.BeginTransaction())
{
foreach (var team in teams)
sess.Save(team);
foreach (var employee in employees)
sess.Save(employee);
t.Commit();
}
sess.Flush();
sess.Clear();
var teamsPersisted = sess.CreateCriteria(typeof (Team)).List<Team>();
var employeesPersisted = sess.CreateCriteria(typeof (Employee)).List<Employee>();
Assert.AreNotSame(teams, teamsPersisted);
Assert.AreNotSame(employees, employeesPersisted);
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Dumping Depersisted Team/Employees:");
Dump(teamsPersisted);
Dump(employeesPersisted);
}
private static void Dump(IEnumerable<Team> teams)
{
foreach (var team in teams)
Console.WriteLine("Team: " + team.Name + " has members: " + string.Join(", ", team.Employee.Select(e => e.FirstName).ToArray()));
}
private static void Dump(IEnumerable<Employee> employees)
{
foreach (var employee in employees)
Console.WriteLine("Employee: " + employee.FirstName + " in teams: " + string.Join(", ", employee.Team.Select(e => e.Name).ToArray()));
}
}
Fluent NHibernate tries to determine what the other side of a many-to-many relationship is by looking at the entity names and the collection properties. I believe it's not working in your case because your collection properties aren't plural; try renaming your collections Employees and Teams respectively.
Another way is to manually set the many-to-many table name on both sides, as this will disable the prediction.