Hello all!
I'll preface this with letting you know I'm a beginner developer (learning dotnet core and angular). With that said, I'm having issues with filtering in a join table. I have setup a table that is a one to many, holding two columns - OwnerId and OwneeId.
The ownerId can have many OwneeId's. This was all setup via Entity Framework.
I'm looking for someone who can help walk me through filtering all ownee's that and OwnerId may have, and then return their name via my API (the owner is a organization, and the ownee is a location).
I have searched around for an answer but am having trouble finding something that works. (and I'm not using Linq in my project).
The way I have things setup is as follows:
IOrganizationRepository:
using System.Collections.Generic; using System.Threading.Tasks; using Outmatch.API.Models; namespace Outmatch.API.Data { public interface IOrganizationRepository { void Add<T>(T entity) where T: class; void Delete<T>(T entity) where T: class; Task<bool> SaveAll(); Task<IEnumerable<Organizations>> GetOrganizations(); Task<Organizations> GetOrganization(int id); Task<OrgToLoc> GetOwnees(int OrganizationId, int LocationId); } }
OrganizationRepository:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Outmatch.API.Helpers; using Outmatch.API.Models; namespace Outmatch.API.Data { public class OrganizationsRepository : IOrganizationRepository { private readonly DataContext _context; public OrganizationsRepository(DataContext context) { _context = context; } public void Add<T>(T entity) where T : class { _context.Add(entity); } public void Delete<T>(T entity) where T : class { _context.Remove(entity); } // get an organization only one of them). Pass in the organization ID from the user interface, and pull the organization that corrisponds to the // id, returning it to the GUI public async Task<Organizations> GetOrganization(int id) { var organization = await _context.Organization.FirstOrDefaultAsync(u => u.Id == id); return organization; } // Get the list of all organizations and return them to the GUI public async Task<IEnumerable<Organizations>> GetOrganizations() { var organizations = await _context.Organization.ToListAsync(); return organizations; } public async Task<OrgToLoc> GetOwnees(int OrganizationId, int LocationId) { return await _context.LocationOwners.FirstOrDefaultAsync(u => u.OwnerId == OrganizationId && u.OwneeId == LocationId); } public async Task<bool> SaveAll() { return await _context.SaveChangesAsync() > 0; } } }
ILocationsRepository:
using System.Collections.Generic; using System.Threading.Tasks; using Outmatch.API.Models; namespace Outmatch.API.Data { // This interface Repository queries the DB and retreives the locations public interface ILocationsRepository { void Add<T>(T entity) where T: class; void Delete<T>(T entity) where T: class; Task<bool> SaveAll(); Task<IEnumerable<Locations>> GetLocations(); Task<Locations> GetLocation(int id); } }
LocationsRepository:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Outmatch.API.Models; namespace Outmatch.API.Data { public class LocationsRepository : ILocationsRepository { private readonly DataContext _context; public LocationsRepository(DataContext context) { _context = context; } public void Add<T>(T entity) where T : class { _context.Add(entity); } public void Delete<T>(T entity) where T : class { _context.Remove(entity); } // get a location only one of them). Pass in the location ID from the user interface, and pull the location that corrisponds to the // id, returning it to the GUI public async Task<Locations> GetLocation(int id) { var location = await _context.Locations.FirstOrDefaultAsync(u => u.Id == id); return location; } // Get the lost of all locations and return them to the GUI public async Task<IEnumerable<Locations>> GetLocations() { var locations = await _context.Locations.ToListAsync(); return locations; } public async Task<bool> SaveAll() { return await _context.SaveChangesAsync() > 0; } } }
OrganizationsController:
using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Outmatch.API.Data; using Outmatch.API.Dtos; using Outmatch.API.Models; namespace Outmatch.API.Controllers { // Controller for getting location and locations. // [Authorize] // [Route("api/[controller]")] // [ApiController] [Authorize] [Route("api/[controller]")] [ApiController] public class OrganizationsController : ControllerBase { private readonly IOrganizationRepository _repo; private readonly IMapper _mapper; public OrganizationsController(IOrganizationRepository repo, IMapper mapper) { _mapper = mapper; _repo = repo; } // Get a list of all organizations from the database [HttpGet] public async Task<IActionResult> GetOrganizations() { var organizations = await _repo.GetOrganizations(); var organizationsToReturn = _mapper.Map<IEnumerable<OrganizationForListDto>>(organizations); return Ok(organizations); } // Create an orgaization [HttpPost] public async Task<IActionResult> CreateOrganizations(OrganizationCreateDto organizationCreateDto) { // Grab the current users roles to verify they have access to this API call var userRole = User.FindFirst(ClaimTypes.Role).ToString(); if (userRole != "http://schemas.microsoft.com/ws/2008/06/identity/claims/role: GlobalAdmin") return Unauthorized(); // Create the new organization var organizationToCreate = _mapper.Map<Organizations>(organizationCreateDto); // _repo.Add(organizationToCreate); if (await _repo.SaveAll()) { var organizationToReturn = _mapper.Map<OrganizationCreateDto>(organizationToCreate); return CreatedAtRoute("GetOrganization", new { controller = "Organizations", Id = organizationToReturn.Id }, organizationToReturn); } throw new Exception("Unable to create new organization. Failed to save."); } // Get a specific organization from the database [HttpGet("{id}", Name = nameof(GetOrganization))] public async Task<IActionResult> GetOrganization(int id) { var organization = await _repo.GetOrganization(id); var organizationToReturn = _mapper.Map<OrganizationCreateDto>(organization); return Ok(organizationToReturn); } // Check if the location is already assigned to an owner (organization) [HttpPost("{id}/assign/{locationId}")] public async Task<IActionResult> AssignLocation(int id, int locationId) { // Ensure the user is authorized to assign the location to an organization var userRole = User.FindFirst(ClaimTypes.Role).ToString(); if (userRole != "http://schemas.microsoft.com/ws/2008/06/identity/claims/role: GlobalAdmin") return Unauthorized(); var assignment = await _repo.GetOwnees(id, locationId); // Check if the location is already owned by the same owner. if (assignment != null) return BadRequest("This location is already assigned to this organization"); if (await _repo.GetOrganization(id) == null) return NotFound("This organization does not exist"); assignment = new OrgToLoc { OwnerId = id, OwneeId = locationId }; _repo.Add<OrgToLoc>(assignment); if (await _repo.SaveAll()) return Ok(); return BadRequest("Failed to assign the location to an organization"); } } }
LocationsController:
using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using AutoMapper; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Outmatch.API.Data; using Outmatch.API.Dtos; using Outmatch.API.Models; namespace Outmatch.API.Controllers { // Controller for getting location and locations. // [Authorize] // [Route("api/[controller]")] // [ApiController] [Authorize] [Route("api/[controller]")] [ApiController] public class LocationsController : ControllerBase { private readonly ILocationsRepository _repo; private readonly IMapper _mapper; private readonly IClientRepository _clientRepo; private readonly UserManager<User> _userManager; public LocationsController(ILocationsRepository repo, IMapper mapper, IClientRepository clientRepo, UserManager<User> userManager) { _userManager = userManager; _clientRepo = clientRepo; _mapper = mapper; _repo = repo; } // Get a list of all locations from the database [HttpGet] public async Task<IActionResult> GetLocations() { var locations = await _repo.GetLocations(); var locationsToReturn = _mapper.Map<IEnumerable<LocationForListDto>>(locations); return Ok(locations); } // Create a location [HttpPost] public async Task<IActionResult> CreateLocation(int clientId, LocationCreateDto locationCreateDto) { // Grab the current users roles to verify they have access to this API call var userRole = User.FindFirst(ClaimTypes.Role).ToString(); if (userRole != "http://schemas.microsoft.com/ws/2008/06/identity/claims/role: GlobalAdmin") return Unauthorized(); // Create the new location var locationToCreate = _mapper.Map<Locations>(locationCreateDto); // _repo.Add(locationToCreate); if (await _repo.SaveAll()) { var locationToReturn = _mapper.Map<LocationCreateDto>(locationToCreate); return CreatedAtRoute(nameof(GetLocation), new { id = locationToReturn.Id, userid = clientId }, locationToReturn); } throw new Exception("Unable to create new location. Failed to save."); } // Get a specific location from the database [HttpGet("{id}", Name = nameof(GetLocation))] public async Task<IActionResult> GetLocation(int id) { var location = await _repo.GetLocation(id); var locationToReturn = _mapper.Map<LocationCreateDto>(location); return Ok(locationToReturn); } } }
OrgToLoc model file:
namespace Outmatch.API.Models { public class OrgToLoc { public int OwnerId { get; set; } public int OwneeId { get; set; } public Locations Owner { get; set; } public Locations Ownee { get; set; } } }
DataContext:
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Outmatch.API.Dtos; using Outmatch.API.Models; namespace Outmatch.API.Data { // DbContext is what is called to connect to the database and pass data along. public class DataContext : IdentityDbContext<User, Role, int, IdentityUserClaim<int>, UserRole, IdentityUserLogin<int>, IdentityRoleClaim<int>, IdentityUserToken<int>> { public DataContext(DbContextOptions<DataContext> options) : base(options) {} // Values name represents the table name in the database. public DbSet<Value> Values { get; set; } public DbSet<Locations> Locations { get; set; } public DbSet<Organizations> Organization { get; set; } public DbSet<OrgToClients> OrgToClients { get; set; } public DbSet<OrgToLoc> LocationOwners { get; set; } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Define the relationship betweek user roles and users (Clients) builder.Entity<UserRole>(userRole => { userRole.HasKey(ur => new {ur.UserId, ur.RoleId}); userRole.HasOne(ur => ur.Role) .WithMany(r => r.UserRoles) .HasForeignKey(ur => ur.RoleId) .IsRequired(); userRole.HasOne(ur => ur.User) .WithMany(r => r.UserRoles) .HasForeignKey(ur => ur.UserId) .IsRequired(); }); // Define the relationship between Organization and Clients (or Users) builder.Entity<OrgToClients>() .HasKey(c => new {c.UserId, c.OrganizationId}); builder.Entity<OrgToClients>() .HasOne(u => u.Organization) .WithMany(u => u.UserId) .HasForeignKey(u => u.OrganizationId) .OnDelete(DeleteBehavior.Restrict); builder.Entity<OrgToClients>() .HasOne(u => u.User) .WithMany(u => u.OrganizationId) .HasForeignKey(u => u.UserId) .OnDelete(DeleteBehavior.Restrict); // Define the relationship between Organizations (Owners) and Locations (Ownees) builder.Entity<OrgToLoc>() .HasKey(k => new {k.OwnerId, k.OwneeId}); builder.Entity<OrgToLoc>() .HasOne(u => u.Ownee) .WithMany(u => u.Owners) .HasForeignKey(u => u.OwneeId) .OnDelete(DeleteBehavior.Restrict); builder.Entity<OrgToLoc>() .HasOne(u => u.Owner) .WithMany(u => u.Ownees) .HasForeignKey(u => u.OwnerId) .OnDelete(DeleteBehavior.Restrict); } } }
Can anyone point me in the right direction on this? As mentioned above, I am trying to filter out all OwneeId id's from the OwnerId table, and return the "LocationName" from the Locations table. Any assistance would be greatly appreciated.