I have a Web API (2) project which has departments and employees. An employee has a department, and a department has a list of employees.
Now in the frontend, when creating or editing an employee, the user must select a department. When posting this to the API, the department contains the list of employees (which causes an invalid modelstate), how can I prevent this?
This is my relevant setup:
Models:
public class Employee : IEntity, ICreatedOn, IModifiedOn, IMappable
{
[Key]
public virtual int Id { get; set; }
public virtual Department Department { get; set; }
// .. other properties
}
public class Department : IEntity, IMappable
{
[Key]
public virtual int Id { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
// .. other properties
}
Web API controller:
public class EmployeesController : ApiController
{
private readonly IEmployeeService _employeeService;
public EmployeesController(IEmployeeService employeeService)
{
this._employeeService = employeeService;
}
// .. GET, POST, DELETE etc.
// PUT: api/Employees/5
[ResponseType(typeof(void))]
public IHttpActionResult PutEmployee(int id, EmployeeVM employee)
{
// This is always invalid, because the employee has a department, which in turn has a list of employees which can be invalid
// What to do to exclude the list of employees from validation, or even better prevent from being sent to the API
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Update etc..
return StatusCode(HttpStatusCode.NoContent);
}
Angular (DataService.js):
app.factory('DataService',
["$http",
function ($http) {
return {
// other functions
updateEmployee: _updateEmployee
}
function _updateEmployee(employee) {
// Maybe exclude the list of employees in employee.department in here??
return $http.put(employeesUrl + "/" + employee.id, employee);
}
// .. other functions
}]);
Notes:
It happens in both the Put and the Post (updating and creating)
I'm using AutoMapper for mapping to the ViewModels, which look the same as the entities
I'm using Entity Framework for the ORM
What I've tried:
[JsonIgnore] attribute on the Employees collection; this causes the Employees also not being loaded when loading the Department
[Bind(Exclude = "Employees")] attribute in the controller action parameters, this did not have any effect
[Bind(Exclude = "Department.Employees")] same
What works, but I'm sure there must be a better solution:
function _updateEmployee(employee) {
var newEmp = angular.copy(employee);
delete newEmp.department.employees;
return $http.put(employeesUrl, newEmp);
}
Create a new request for updating your employee. Like:
public class UpdateEmployeeRequest{
public int EmployeeId {get;set;}
public int DepartmentId {get;set;}
//and so on
}
For this request you can specify concrete validation.
And declare Entities with explicit ID.:
public class Employee : IEntity, ICreatedOn, IModifiedOn, IMappable
{
[Key]
public int Id { get; set; }
[ForeignKey( "Department" )]
public Guid DepartmentId { get; set; }
[Required]
public virtual Department Department { get; set; }
// .. other properties
}
I would modify the Employee entity like this
public class Employee : IEntity, ICreatedOn, IModifiedOn, IMappable
{
[Key]
public virtual int Id { get; set; }
//Add DepartmentId
public Guid? DepartmentId { get; set; }
public virtual Department Department { get; set; }
// .. other properties
}
Then you can only set DepartmentId and that's it. EF will take care of the rest
Related
I have a custom derived class ApplicationUser from IdentityUser. The mapping table AspNetUserRoles consists of the following columns:
The problem is, when I add a role to an user, .AddToRoleAsync() method works fine, EF inserts a new row into the table AspNetUserRoles, UserId and RoleId get correct values, but ApplicationUserId gets NULL value.
What am I missing here?
Here is my code:
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Id = Guid.NewGuid().ToString();
this.Roles = new HashSet<IdentityUserRole<string>>();
this.Claims = new HashSet<IdentityUserClaim<string>>();
this.Logins = new HashSet<IdentityUserLogin<string>>();
}
public int IdUser { get; set; }
public virtual ICollection<IdentityUserRole<string>> Roles { get; set; }
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
}
public class ApplicationRole : IdentityRole, IModifiable
{
public ApplicationRole(): this(null)
{
}
public ApplicationRole(string name) : base(name)
{
this.Id = Guid.NewGuid().ToString();
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
There are a few questions already asked, but nothing seems to solve my issue.
I'm trying to run a query that gets all of my references, but it isn't working.
What I have right now is
from UserGroups
where Id="ActionGroup"
select Accomplishments.ID, Accomplishments.Accomplish
But I need only the Accomplishments.Accomplish that belong in my other collection ActivityAccomplishments and these are nested in another object.
To be exact, I'm trying to figure out how to query the UserGroups collection and only look at the one with id="ActionGroup". After that I need all of the Accomplishments.Accomplish strings within the UserGroup list to be filtered out if they don't match a id in ActivityAccomplishment.
Basically, in the UserGroup I'm looking at it's List Accomplishments needs to filter out all strings within the Acc class that don't match an Id in ActivityAccomplishments. Can someone please help me.
Here are the classes I'm using.
public class UserGroups
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Acc> Accomplishments { get; set; }
}
public class Acc
{
public string Id { get; set; }
public List<string> Accomplish { get; set; }
}
public class ActivityAccomplishments
{
public string Id { get; set; }
}
try this:
from UserGroups
where Id = "ActionGroup" AND Accomplishments[].Accomplish != "theIdYouDontWant"
select Accomplishments[].Accomplish as AccomplishStringsList
(not necessary to add the 'as AccomplishStringsList' - it is just a name for the results)
I feel like I'm missing something obvious here; I'm using .Net 5 with Entity Framework Core. The problem is that the foreign key is correct, but the associated navigation property is always empty and has no data. Do I have to do something with the fluent framework, or do something special with my includes?
I have 3 simplified entities and a database context method in this example, the project is much too large to include entirely. In the method, CalendarEvents is a DbSet:
public class CalendarEvent: IJsonSerializable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <summary>
/// Gets the personnel associated with this event
/// </summary>
public virtual List<SchedulePerson> SchedulePeople { get; set; } = new List<SchedulePerson>();
}
public class SchedulePerson : IJsonSerializable, ICloneable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
public virtual CalendarEvent AssociatedCalendarEvent { get; set; }
}
public class Employee : IJsonSerializable
{
[Key]
public int Id { get; set; }
public virtual List<SchedulePerson> AssociatedSchedulePeople { get; set; } = new List<SchedulePerson>();
}
public class DbContext : DbContext
{
public DbSet<CalendarEvent> CalendarEvents { get; set; }
public DbSet<SchedulePerson> SchedulePeople { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbContext(DbContextOptions<VibrationContext> options): base(options)
{
}
public CalendarEvent GetEvent(int calendarEventId)
{
var currentCalEvent = this.CalendarEvents.Where(x => x.Id == calendarEventId);
var dummy1 = currentCalEvent.FirstOrDefault();
var dummy2 = currentCalEvent.Include(calEvent => calEvent.SchedulePeople).ToList();
var dummy3 = currentCalEvent.Include(calEvent => calEvent.SchedulePeople).ThenInclude(people => people.Employee).ToList();
return currentCalEvent.FirstOrDefault();
}
}
In this case Employee is the associated navigation property, and the associated key EmployeeId, is correct. For the moment I've added extra logic that will populate each employee per schedule person separately, manually based on the foreign key EmployeeId, when I get the event, but I'd rather not have to add logic like that each time. If that's something unavoidable then that's fine, but I'd like to do things properly and let Entity Framework Core handle as much as possible.
For additional context:
SchedulePerson -> CalendarEvent is a many-to-one relationship
Employee -> SchedulePerson is a many-to-one relationship
In other words a calendar event can contain many schedule persons, but a schedule person can only be associated with one calendar event.
Employee can be associated with many schedule persons, but each schedule person is only associated with one employee.
There should only be one employee for each real person, but there can be multiple SchedulePersons for each real person.
Thank you for your help and let me know if there is any more information I can provide.
Also if anything else looks bad or wrong in these code snippets please let me know.
Edit, this is what I have to do if I want to get the employees in my request:
private void UpdateEmployeeContents(CalendarEvent calendarEvent)
{
foreach (SchedulePerson person in calendarEvent.SchedulePeople)
{
person.Employee = this.Employees.Where(x => x.Id == person.EmployeeId).FirstOrDefault();
}
}
below is my code
I am trying to pull data from database using entityframework.
EmployeeDataContext class -
namespace _09032020_1.Models
{
public class EmployeeDataContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
}
}
Employee model -
namespace _09032020_1.Models
{
[Table("TbleEmployee")]
public class Employee
{
public int employeeId { get; set; }
public string employeeName { get; set; }
public string employeeCity { get; set; }
public string employeeGender { get; set; }
public int departmentId { get; set; }
}
}
below are the table columns.
here is the controller code
namespace _09032020_1.Controllers
{
public class EmployeeController : Controller
{
public ActionResult Index()
{
EmployeeDataContext employeeDataContext = new EmployeeDataContext();
Employee employee = new Employee();
List<Employee> employees1 = new List<Employee>();
employees1 = employeeDataContext.Employees.ToList();
return View(employees1);
}
}
}
I am not getting data inside employeeDataContext
Please let me know if more info regarding config file requires.
Create the table w data. The first column is primary key and identity.
In an MVC project (you can use another type, also I am showing database first, and you can use another type, right click on the Models folder and add ADO.NET Entity Data Model named EmployeeDataContext. Choose EF Designer from database. New connection, and choose your db. Save connection as EmployeeDataContext and choose your table.
Put this in your code:
using (EmployeeDataContext context = new EmployeeDataContext())
{
var emps = context.Employees.ToList();
}
I got my answer,
I have written wrong table name as model attribute [Table("TbleEmployee")]
My table name is TblEmplyee. So it should be [Table("TblEmployee")]
As I changed this I abled to proceed forward.
I have two tables within a database, one table is called Booking. This table will record data that a user can input via a form on the webstie. The other table is called Account, this table will record information regarding the login information of a user to the website.
When a user successfully logs into the website they are "Redirected" to a new View called Manage. Within the manage view I want to be able to display a table of just the information from the Booking table that relates to that particular account.
I think that in my Controller, within the Manage ActionResult I need to compare the values within the BookingEmail column of my Booking Table with the values within the AccountEmail column of my Account Table. If there is a match then a list of the information which relates to that email is shown in the manage view.
I have an idea of how to go about it but actually making the code for it too work I don't know. I am new to MVC4.
Please Help.
Booking Model:
public partial class Booking
{
public int BookingId { get; set; }
public string BookingFirstname { get; set; }
public string BookingSurname { get; set; }
public string BookingEmail { get; set; }
public string BookingMobileTel { get; set; }
public string BookingHomeTel { get; set; }
public int BookingAge { get; set; }
public int BookingPassengers { get; set; }
public int DepartureId { get; set; }
public int ArrivalId { get; set; }
}
Account Model:
public partial class Account
{
public System.Guid AccountId { get; set; }
public string AccountEmail { get; set; } // Primary Key
public string AccountPassword { get; set; }
public string AccountPasswordSalt { get; set; }
}
Removed validation for a clearer picture.
In my Account Controller, in a [HttpPost] method for LogIn:
[HttpPost]
public ActionResult LogIn(Account user)
{
if (ModelState.IsValid)
{
if (isValid(user.AccountEmail, user.AccountPassword))
{
FormsAuthentication.SetAuthCookie(user.AccountEmail, false);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Login Data does not exist, Please Register");
}
}
return View(user);
}
UPDATE:
In order to go to the Manage View, which is in HomeController I have used this:
[HttpPost]
public ActionResult LogIn(Account user)
{
if (ModelState.IsValid)
{
if (isValid(user.AccountEmail, user.AccountPassword))
{
FormsAuthentication.SetAuthCookie(user.AccountEmail, false);
bool hasBooking = db.Bookings.Where(b => b.BookingEmail == user.AccountEmail).Any();
if (hasBooking == true)
return RedirectToAction("Manage", "Home");
else
return RedirectToAction("Book", "Home");
}
else
{
ModelState.AddModelError("", "Login Data does not exist, Please Register");
}
}
return View(user);
}
UPDATE:
So I have passed the AccountEmail from the AccountController to the HomeController using TempData:
Account Controller:
TempData["user"] = user.AccountEmail;
Home Controller:
public ActionResult Manage()
{
var user = TempData["user"] as string;
IEnumerable<Booking> bookings = db.Bookings.Where(b => b.BookingEmail == user);
return View(bookings);
}