I am building an asp.net mvc site that allows users (with the role of manager) to add/manage other users
To do this I've added a relational table called ManagerUsers to the database that contains a manager id and a user id. When a manager invites a user, the data is stored in the ManagerUsers table.
When a manger views the users I am doing the following:
using (var context = new ApplicationDbContext())
{
Guid myId = Guid.Parse(User.Identity.GetUserId());
var userIds = context.ManagersUsers.Where(u => u.ManagerId == myId).Select(u => u.UserId.ToString()).ToList();
var userProfiles = context.Users.Where(t => userIds.Contains(t.Id)).ToList();
return View(userProfiles);
}
This works ok but seems kind of slow and long-winded. Does anyone know a better way of doing it?
EDIT: based on some replies I think I need to clarify what I'm asking. What I want to know is whether there is a better way to get a list of users that are under my management than getting a list of users Ids from the ManagerUsers table and then finding them from all of the users in the Users table? Maybe there is a better way of storing this data to make it faster for retrieval?
This tutorial shows examples of defining relationships with Entity Framework and the virtual property:
https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
It would look something like this:
public virtual <ApplicationUser> User { get; set; }
This will actually create a table relating the two models. From here you should be able to get the Users using ManagerUser.Users or something to this effect. I would also follow mason's example and implement a Repository pattern.
You shouldn't tightly couple your data access code to your MVC layer. That makes it difficult to change data layers, and it makes it difficult to test MVC without hitting a real database. You're far better off creating a separate layer that allows them to be loosely coupled.
interface IMembershipService
{
List<UserProfile> GetUsersForManager(Guid managerId);
}
class SqlServerMembershipService : IMembershipService
{
private readonly string ConnectionString;
public SqlServerMembershipService(string connectionString)
{
//Any initialization of the repository goes here
ConnectionString = connectionString;
}
public List<UserProfile> GetUsersForManager(Guid managerId)
{
using (var context = new ApplicationDbContext(connectionString))
{
var userIds = context.ManagersUsers.Where(u => u.ManagerId == myId).Select(u => u.UserId.ToString()).ToList();
var userProfiles = context.Users.Where(t => userIds.Contains(t.Id)).ToList();
return View(userProfiles);
}
}
}
Your MVC controller looks like this:
class UsersController
{
private readonly IMembershipService MembershipService;
public UsersController(IMembershipService membershipService)
{
MembershipService = membershipService;
}
public ActionResult Index()
{
Guid myId = Guid.Parse(User.Identity.GetUserId());
var profiles = MembershipService.GetUsersForManager(myId);
return View(profiles);
}
}
See how UsersController now has no idea about SqlServerMembershipService? All it knows is that it's going to receive some class via its constructor that will handle retrieving data for it. How it gets that class it up to you. You could tightly couple it by saying IMembershipService MembershipService = new SqlServerMembershipService but it's better to use Dependency Injection to do that for you.
Just in case anyone cares, here is what I did in the end:
public class ApplicationManager : ApplicationUser
{
public virtual List<ApplicationUser> Users { get; set; }
public ApplicationManager()
{
Users = new List<ApplicationUser>();
}
}
public class ApplicationUser : IdentityUser
{
public virtual ApplicationManager Manager { get; set; }
...
}
This adds two fields to the AspNetUsers table - Manager_Id and Discriminator (which states whether the user is an ApplcationManager or ApplicationUser).
Related
I'm new to razor pages / efcore / aspnet identity and have been trying to figure this out but its beating me.
Basically, I use AspNet Identity for user authentication & authorisation. I've extended AspNetUsers with an additional OrganisationId, which is an FK to Organisation entity; and added the ID as a claim in the identity claim store. This works fine.
Now I need to set an efcore global filter based on the authenticated user's organisationId so they can only view data that is assigned to their organisation.
However, I can't access the authenticated user details within the ModelBuilder.
public class SDMOxContext : IdentityDbContext<
ApplicationUser, ApplicationRole, string,
ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
ApplicationRoleClaim, ApplicationUserToken>
{
public SDMOxContext(DbContextOptions<SDMOxContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Set global filter so users can only see projects within their organisation.
builder.Entity<Project>().HasQueryFilter(project => project.OrganisationId == 1);
}
Instead of 1 in the global filter, I need to enter the user organisationid, which is stored as a user claim. Usually I get it with this:
User.FindFirstValue("OrganisationId")
However, User doesn't exist in the current context.
So I would need to apply the query filter at a later stage, ie. after user authentication? Any pointers where to start with a mid-tier/logic-tier approach?
Granted this is an opinion on architecture, but I break it down like this:
Data-Tier - This tier's responsibility to to access resources (normally) outside the executing application. This includes; Databases, File IO, Web Api's etc.
Business/Logic-Tier - This tier's responsibility (which could be broken down further) should Authenticate, Authorize, Validate and build objects that represent the businesses needs. To build these objects, it may consume one or more data access objects (for example, it may use an IO DA to retrieve the Image from a local file system or Azure storage and a Database DA to retrieve metadata about that image).
Presentation/Exposure-Tier - This tier's responsibility is to wrap and transform the object into the consumers need (winforms, wpf, html, json, xml, binary serialization etc).
By leaving logic out of the data-tier (even in multi-tenant systems) you gain the ability to access data across all systems (and trust me there is a lot of money to be made here).
This is probably way more than I can explain in such a short place and very my opinion. I'm going to be leaving out quite a bit but here goes.
Data-Tier
namespace ProjectsData
{
public interface IProjectDA
{
IProjectDO GetProject(Guid projectId, Guid organizationId);
}
private class ProjectDA : DbContext, IProjectDA
{
public ProjectDA (...)
public IEnumerable<ProjectDO> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder) {... }
public IProjectDO GetProject(Guid projectId, Guid organizationId)
{
var result = Projects
.FirstOrDefault(p => p.Id == projectId && OrganizationId = organizationId);
return result;
}
}
public interface IProjectDO{ ... }
private class ProjectDO: IProjectDO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
Logic
namespace ProjectBusiness
{
public interface IProjectBO { .. }
public interface IOrganization
{
Guid OrganizationId { get; }
}
private class ProjectBA : IProjectBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
private readonly IOrganization _organization;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity,
IOrganizationContext organizationContext)
{
_projectDA = projectDA;
_identity = identity;
}
public IProjectBO GetProject(Guid id)
{
var do = _projectDA
.GetProject(id, _organization);
var result = map.To<ProjectBO>(do);
return result;
}
}
public interface IProjectBO { .. }
private class ProjectBO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
So under these circumstances the data layer is aware of type of request, but isn't multi-tenant aware. It isn't limiting all request based on anything. This architecture is advantageous in a number of ways.
First, in the above example, your product takes off and your supervisor wants to know what Categories are the most popular.
namespace StatisticsBusiness
{
public interface IStatisticsBO
{
IEnumerable<ICategoryStatisticBO> CategoryStatistics { get; set; }
}
public interface ICategoryStaticBO
{
Guid CategoryId { get; }
int ProjectCount { get; }
}
private class StatisticsBA : IStatisticsBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity)
{
_projectDA = projectDA;
_identity = identity;
}
public IEnumerable<IProjectBO GetOrderedCategoryPopularity()
{
var dos = _projectDA
.GetProjectCategoryCounts()
var result = map.To<IEnumerable<IStatisticsBO>>(dos);
return result;
}
}
public interface IStatisticsBO{ .. }
private class StatisticsBO
{
public Guid CategoryId { get; }
public int ProjectCount { get; }
}
}
Note: Some people prefer to pass an expression as a predicate. Both have their advantages and disadvantages. If you decide to go the predicate route, then you'll have to decide if all your Data Access types use predicates or not. Just realize that using predicates against IO or Web Api might be more effort that it's worth.
Secondly, some requirement causes you not to be able to use Entity Framework. You replace it with Dapper or some other new better technology/framework. All you have to create new I<whataver>DA classes because the consuming logic is unaware of anything other than those interfaces (programming against an interface, the L in SOLID programming principles and the I in SOLID programming principles).
I don't use this pattern all the time because for some smaller websites, it's too much work for the payoff.
I will suggest to decompose the solution in tow parts
Add an organization id in your dbcontext, much like a tenant id in multi-tenant env. See this link for example.
Next challenge will be to pass the organization id as a parameter to DbContext constructor. For this you can create a factory for DbContext. Since you store the OrganizationId in claims. The factory can access the same claim HttpContext and pass the organization id as a parameter while instanting the dbContext.
It's not perfect but can give you a starting point.
Background
I have a website written in ASP.NET Core v2.1.1.
I have a custom identity user class:
public class FooIdentityUser : IdentityUser<string>, IIdentityModel
{
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public bool FooBool { get; set; }
}
and a custom identity role class:
public class FooIdentityRole : IdentityRole<string>
{
}
Which I then reference in the dbcontext:
public class FooIdentityDbContext : IdentityDbContext<FooIdentityUser,FooIdentityRole,string>
{
public FooIdentityDbContext(DbContextOptions<FooIdentityDbContext> options)
: base(options)
{
}
}
Requirement
My overall requirement is that I want to give system admin users the ability to view and eventually manage user data from within the admin area of the website.
Specifically:
I want to provide a list of users that are in a foo role
And / or I want to list all users that have FooBool set to true
And / or I want to query on email address, first name & last name
And / or carry out a sort
Question
Does anyone have any links to web pages where this has been done before or can you respond on how I can implement this feature? I have attempted a couple of approaches below.
Approaches / Research
From what I can see there are two approaches to doing this:
Approach 1
Because I want to list users specifically for a user role based in a view, I can see that user manager provides a method for this:
_userManager.GetUsersInRoleAsync(fooRoleName)
The issue I have with this is it returns an IList so whilst it will return all users with this role, if I want to query on FooBool and / or FirstName, LastName or Email Address, it will need to cycle through the list to filter these out which would be inefficient if there are 10s of thousands or 100s of thousands of users?
Ideally, this would return an IQueryable so it wouldn't hit the database until my where and order by had been applied but I can't find a way of doing this?
Approach 2
The other way may be to query the context directly through my generic repository.
public class GenericIdentityRepository<TModel> : IIdentityRepository<TModel> where TModel : class, IIdentityModel
{
private readonly ILogger _logger;
public FooIdentityDbContext Context { get; set; }
private readonly DbSet<TModel> _dbSet;
public GenericIdentityRepository(FooIdentityDbContext dbContext, ILogger<GenericIdentityRepository<TModel>> logger)
{
Context = dbContext;
_logger = logger;
_dbSet = Context.Set<TModel>();
}
public IQueryable<TModel> GetAll()
{
_logger.LogDebug("GetAll " + typeof(TModel));
IQueryable<TModel> query = _dbSet;
return query;
}
public IQueryable<TModel> GetAllNoTracking()
{
_logger.LogDebug("GetAllNotTracking " + typeof(TModel));
IQueryable<TModel> query = GetAll().AsNoTracking();
return query;
}
}
I was looking to see if I could do something by creating custom classes for userrole and then using linq to give me an IQueryable?
public class FooIdentityUserRole : IdentityUserRole<string>
{
public virtual FooIdentityUser User { get; set; }
public virtual FooIdentityRole Role { get; set; }
}
And then somehow query the data to return an IQueryable but I'm struggling to produce the correct linq I need to do this.
My suggestion is to use the FooIdentityDbContext directly in your controllers and just query the data in the way you want. I don't know a way you could achieve what you want using the UserManager class. Maybe there is but honestly, I wouldn't mix things. UserManager is more useful when you are dealing with a single user and want to do things with it such as AddToRoleAsync or ChangePasswordAsync.
You have much more flexibility using the DbContextclass directly. You don't need some fancy generic repository. Keep it simple and concise unless you definitely need the abstraction (which almost always you don't)
Down to the actual answer: You've already configured the entities correctly, so now just inject the FooIdentityDbContext and start querying. Something like this:
public class HomeController : Controller
{
private readonly FooIdentityDbContext_dbContext;
public HomeController(FooIdentityDbContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task<IActionResult> UserList(string roleName, bool fooBool, string firstName)
{
// You are interested in Users on Roles, so it's easier to start on the UserRoles table
var usersInRole = _dbContext.UserRoles.Select(userRole => userRole);
// filter only users on role first
if (!string.IsNullOrWhiteSpace(roleName))
{
usersInRole = usersInRole.Where(ur => ur.Role.Name == roleName);
}
// then filter by fooBool
usersInRole = usersInRole.Where(ur => ur.User.FooBool == fooBool);
// then filter by user firstname or whatever field you need
if (!string.IsNullOrWhiteSpace(firstName))
{
usersInRole = usersInRole.Where(ur => ur.User.FirstName.StartsWith(firstName));
}
// finally materialize the query, sorting by FirstName ascending
// It's a common good practice to not return your entities and select only what's necessary for the view.
var filteredUsers = await usersInRole.Select(ur => new UserListViewModel
{
Id = ur.UserId,
Email = ur.User.Email,
FooBool = ur.User.FooBool,
FirstName = ur.User.FirstName
}).OrderBy(u => u.FirstName).ToListAsync();
return View("UserListNew", filteredUsers);
}
}
Bonus: I've been reading the EF Core in Action book by Jon Smith and it's great. I highly recommend reading it if you want to keep using EF Core in your projects. It's full of nice tips and real world examples.
use .Users.
await _userManager.Users.Where(w => w.LastChangeUserId == null).ToListAsync();
I have my entity as:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I have my UserViewModel as
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I am using these as below in my controller:
//This is called from my view via ajax
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
MyRepository.UpdateUser(uv);
}
My UpdateUser in repository class is as below:
public void UpdateUser(User u)
{
var user = GetUserDetails(u.Id);
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
How can I correctly map UserViewModel in my controller to my entity
By using AutoMapper you can do something like:
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UserViewModel , User>();
});
User u = config.CreateMapper().Map<User>(uv);
MyRepository.UpdateUser(u);
}
Or manually :
public void Save(UserViewModel uv)
{
User u = new User()
{
Id = uv.Id
Name = uv.Name;
Address = uv.Address;
};
MyRepository.UpdateUser(u);
}
Which is not good to do it manually if you change your view-model and your model then you should change your code also, but with Automapper you don't need to change the code.
EDIT1:
This is not good idea to use model-view in repository (DataAccess Core) so it would be better to keep your public void UpdateUser(User u) and don't change it, in outside it is better to pass user to UpdateUser not UserViewModel like what you have done before.
EDIT2:
In my opinion non of answered posts doesn't related to SOC (Separation on concerns) even mine...
1- When I passed UserViewModel I've violated the SOC ....
2- In the other side if I got User in Peresentation layer directly I also violated the SOC.
I think the best way is a middle layer as proxy....
Presentation <----> Proxy <----> Repository.
Your repository deals with objects of type User, so you need to map the values back to an instance of that type and then make the call.
Assuming you have a method to get the user called GetUser:
public void Save(UserViewModel uv)
{
var user = MyRepository.GetUser(uv.Id);
user.Name = uv.Name;
user.Address = uv.Address;
MyRepository.UpdateUser(user);
}
You can then save the changes in your repository class. You can attach the object to make sure there are no issues if the object was created in a different context:
public void UpdateUser(User u)
{
_context.Users.Attach(u);
_context.Entry(u).State = EntityState.Modified;
_context.SaveChanges();
}
You are doing the mapping of property values(view model->enity model) inside your repositories UpdateUser method. So use the view model class (UserViewModel) as the parameter type of that.
public void UpdateUser(UserViewModel u)
{
// Get the entity first
var user = GetUserDetails(u.Id);
// Read the property values of view model object and assign to entity object
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
Now from your Save method ,you can pass the view model object to this method.
This will fix your compile time error (which is your current problem in the question), but be careful about what classes you are using in what layers. If you are too much worried about using a view model class in your data access layer, you can do that in a middle service layer. But then you are getting the entity model in that layer and doing the update there.
Remember, there is no definite answer for that question. Use the approach you think is readable and consistent with the project/ team. Often times, i tend to use the term "Common DTO classes" than "View models" so i can peacefully pass those around to another layer. I keep them in a separate project (called Common DTO) and this will be cross cutting across other projects. That means i will add a reference to this Common DTO project in my Web/UI layer and my data access/service layer and use those as needed.
I have a standard DbContext with code like the following:
public DbSet<Interest> Interests { get; set; }
public DbSet<User> Users { get; set; }
I've recently implemented multi-tenancy by creating a TenantContext that contains the following:
private readonly DbContext _dbContext;
private readonly Tenant _tenant;
public TenantContext(Tenant tenant)
: base("name=DefaultConnection") {
this._tenant = tenant;
this._dbContext = new DbContext();
}
public IQueryable<User> Users { get { return FilterTenant(_dbContext.Users); } }
public IQueryable<Interest> Interests { get { return FilterTenant(_dbContext.Interests); } }
private IQueryable<T> FilterTenant<T>(IQueryable<T> values) where T : class, ITenantData
{
return values.Where(x => x.TenantId == _tenant.TenantId);
}
So far, this has been working great. Whenever any of my services creates a new TenantContext, all queries directly off of that context are filtered through this FilterTenant method that guarantees I'm only returning tenant-relevant entities.
The problem that I'm encountering is my usage of navigation properties that do not take this into account:
using (var db = CreateContext()) // new TenantContext
{
return db.Users.
Include(u => u.Interests).FirstOrDefault(s => s.UserId == userId);
}
This query pulls up the tenant-specific Users, but then the Include() statement pulls in Interests for that user only - but across all tenants. So if a user has Interests across multiple Tenants, I get all of the user's Interests with the above query.
My User model has the following:
public int UserId { get; set; }
public int TenantId { get; set; }
public virtual ICollection<Interest> Interests { get; set; }
Is there any way that I can somehow modify these navigation properties to perform tenant-specific queries? Or should I go and tear out all navigation properties in favor of handwritten code?
The second option scares me because a lot of queries have nested Includes. Any input here would be fantastic.
As far as I know, there's no other way than to either use reflection or query the properties by hand.
So in your IQueryable<T> FilterTenant<T>(IQueryable<T> values) method, you'll have to inspect your type T for properties that implement your ITenantData interface.
Then you're still not there, as the properties of your root entity (User in this case) may be entities themselves, or lists of entities (think Invoice.InvoiceLines[].Item.Categories[]).
For each of the properties you found by doing this, you'll have to write a Where() clause that filters those properties.
Or you can hand-code it per property.
These checks should at least happen when creating and editing entities. You'll want to check that navigation properties referenced by an ID property (e.g. ContactModel.AddressID) that get posted to your repository (for example from an MVC site) are accessible for the currently logged on tenant. This is your mass assignment protection, which ensures a malicious user can't craft a request that would otherwise link an entity to which he has permissions (a Contact he is creating or editing) to one Address of another tenant, simply by posting a random or known AddressID.
If you trust this system, you only have to check the TenantID of the root entity when reading, because given the checks when creating and updating, all child entities are accessible for the tenant if the root entity is accessible.
Because of your description you do need to filter child entities. An example for hand-coding your example, using the technique explained found here:
public class UserRepository
{
// ctor injects _dbContext and _tenantId
public IQueryable<User> GetUsers()
{
var user = _dbContext.Users.Where(u => u.TenantId == _tenantId)
.Select(u => new User
{
Interests = u.Interests.Where(u =>
u.TenantId == _tenantId),
Other = u.Other,
};
}
}
}
But as you see, you'll have to map every property of User like that.
Just wanted to offer an alternative approach to implementing multi-tenancy, which is working really well in a current project, using EF5 and SQL 2012. Basic design is (bear with me here...):
Every table in the database has a column (ClientSid binary, default constraint = SUSER_SID()) and is never queried directly, only ever via a dedicated view
Each view is a direct select over the table with WHERE (ClientSid = SUSER_SID()) but doesn't select the ClientSid (effectively exposing the interface of the table)
EF5 model is mapped to the VIEW, not the TABLE
The connection string is varied based on the context of the tenant (user / client whatever multi-tenant partition requirement may be)
That's pretty much it - though it might be useful to share. I know it's not a direct answer to your question, but this has resulted in basically zero custom code in the C# area.
I'm new to C# and ASP.NET MVC and i'm trying to understand the repository pattern. I've read a whole lot of articles, but I just don't understand how to use it. I'm currently using LINQ to SQL to access my SQL Server 2005 database and for testing purposes I created two tables. I have an Employees table and an EmployeeContacts table. The pk of both tables is UserName.
Employees
UserName
LastName
FirstName
Position
Email
Status
HireDate
EmployeeContacts
UserName
Contact1
Contact1Phone
Contact1Relationship
There is a one to one relationship between the two tables. An employee can be added, updated, and deleted and so can the data in the EmployeeContacts table.
So would I create a base repository to be used by both entities or should I create a repository for each entity separately? If anybody would be willing to show me some code that would be great.
So far, I have this Employee repository. I also have one for EmployeeContacts.
namespace MvcDirectoryLINQ.Models
{
public class EmployeeRepository
{
private TestDB_DataDataContext db = new TestDB_DataDataContext();
private UserName u = new UserName();
//
// Query Methods
public IQueryable<Employee> FindAllEmployees()
{
return db.Employees;
}
public IQueryable<Employee> FindRecentEmployees()
{
DateTime myDate = DateTime.Today.AddMonths(-6);
return from empl in db.Employees
where empl.HireDate >= myDate
orderby empl.HireDate
select empl;
}
public Employee GetEmployee(string UserName)
{
return db.Employees.SingleOrDefault(d => d.UserName == UserName);
}
//
// Insert/Delete Methods
public void Add(Employee employee)
{
// get the UserName which is created from the email
employee.UserName = u.ReturnUserName(employee.Email);
//Insert the new employee into the database
db.Employees.InsertOnSubmit(employee);
db.EmployeeContacts.InsertOnSubmit(employee.EmployeeContact);
}
public void Delete(Employee employee)
{
db.EmployeeContacts.DeleteOnSubmit(employee.EmployeeContact);
db.Employees.DeleteOnSubmit(employee);
}
//
// Persistence
public void Save()
{
db.SubmitChanges();
}
}
}
I have a class for an EmployeeFormViewModel:
namespace MvcDirectoryLINQ.Models
{
public class EmployeeFormViewModel
{
//Properties
public Employee Employee { get; private set; }
public EmployeeContact EmployeeContact { get; private set; }
//Constructor
public EmployeeFormViewModel(Employee employee, EmployeeContact employeeContact)
{
Employee = employee;
EmployeeContact = employeeContact;
}
}
}
Code for EmployeeController:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(string UserName, FormCollection formValues)
{
Employee employee = employeeRepository.GetEmployee(UserName);
EmployeeContact employeecontact = employeecontactRepository.GetContact(UserName);
try
{
UpdateModel(employee);
UpdateModel(employeecontact);
employeecontactRepository.Save();
employeeRepository.Save();
return RedirectToAction("Details", new { UserName = employee.UserName });
}
catch
{
foreach (var issue in employee.GetRuleViolations())
{
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(new EmployeeFormViewModel(employee, attendingID));
}
}
In my View, i inherit from #model MvcDirectoryLINQ.Models.EmployeeFormViewModel. My Employee data saves correctly but the EmployeeContacts don't and I have no idea why.
Am I implementing the repository pattern correctly?
The main goal when using the Repository Pattern (as far as I understand it) is to decouple your application from using a specific Data Access Layer. You haven't done that here because you create I can see that your EmployeeRepository class does not implement an interface. You really want to have something like EmployeeRepository : IEmployeeRepository
Then, in your Controller code, you can pass around an IEmployeeRepository instead of working concretely with your EmployeeRepository. This will give you two benefits:
Should you ever need to switch the backend code, you only need to make another class that implements the interface.
When you go to test your Controllers, you can pass around a so called mock object that implements the interface instead of hitting the actual database, which slows your tests down and also breaks Unit Testing.
Another thing I noticed is that you spin up a DataContext inside your repository. If you wanted to make changes to multiple different types of objects you would therefore have multiple DataContexts open, which I don't think is what you want, since your changes won't be transactional. You may want to look into the Unit of Work Pattern for a solution.
When learning about a pattern, try to figure out the main benefit first before trying to implement it. In some cases it may not make sense. Please let me know if you would like me to elaborate on anything. Good luck.
So would I create a base repository to be used by both entities or should I create a repository for each entity separately?
The general rule when using the repository pattern is that there should be one repository class per each primary entity type. Can the EmployeeContacts live independently of any Employee? If so, they should have their own repository. Are them always related to an Employee? If so, manage them by using the Employee repository itself.