EntityFramework 6, update object references in generic controller - c#

I have a generic Controller deriving from ApiController:
public abstract class GenericIntegerIdController<T> : ApiController where T : IObjectWithKey
{
...
[HttpPut]
public async Task<IHttpActionResult> Put([FromBody] T entity)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Context.Entry(entity).State = EntityState.Modified;
try
{
UpdateReferences(entity);
}
catch (Exception e)
{
return InternalServerError(e);
}
try
{
await Context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (await Context.Set<T>().FindAsync(entity.GetId()) == null)
{
return NotFound();
}
throw;
}
return Ok();
}
...
protected virtual IHttpActionResult UpdateReferences(T entity)
{
return null;
}
}
here is an example controller deriving from this:
public class CarsController : GenericIntegerIdController<Car>{
...
protected override IHttpActionResult? UpdateReferences(Car entity)
{
if (entity.Owner != null)
{
var reference = Context.Owner.Find(entity.Owner.Id);
if (reference == null)
{
return NotFound();
}
entity.Owner = reference;
}
return base.UpdateReferences(entity);
}
}
where IObjectWithKey is just an interface with a key (Id)
in this controller im trying to update the entity recieved from the request using EntityFramework 6 but im having some problems when im updating object references
Example
I have a class Car and a class Person:
public class Car: IObjectWithKey
{
[Key]
public int Id { get; set; }
...
[Required]
public virtual Person Owner { get; set; }
}
public class Person: IObjectWithKey
{
[Key]
public int Id { get; set; }
...
[Required]
public virtual ICollection<Car> Cars { get; set; }
}
if i try to PUT a car modifying for example the model everything works fine but if i try and change the Person no error is given but the person does not get changed.
I searched a bit for what could be the cause, and i think its because only the parent entity is put on a modified state while the entity.Owner is still unmodified so it doesnt get updated when calling SaveChanges
I tried some solution like for example manually setting the state of the children to modified on each controller derived from the generic controller like this:
protected override IHttpActionResult? UpdateReferences(Car entity)
{
if (entity.Owner != null)
{
var reference = Context.Owner.Find(entity.Owner.Id);
if (reference == null)
{
return NotFound();
}
entity.Owner = reference;
//Manually setting the state of the child to Modified
Context.Entry(entity.Owner).State = EntityState.Modified;
}
return base.UpdateReferences(entity);
}
but nothing changes.

Related

The property could not be mapped because it is of type 'object', which is not a supported primitive type or a valid entity type

I was trying to change a SQL Server database while working on a project using Entity Framework. However, when I altered the table, and added 1 string column Email, the whole thing crashed with exception that doesn't make any sense.
I have tried to add some light changes, but it has no effect.
The exception:
System.Exception: "Couldn't retrieve entities: The property 'Users.Email' could not be mapped because it is of type 'object', which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Here is the DbContext:
using System.Data.Entity;
namespace PlanetariumModelsFramework
{
public class PlanetariumServiceContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<PlanetariumServiceContext>(null);
base.OnModelCreating(modelBuilder);
}
public DbSet<Performance> Performances { get; set; }
public DbSet<Poster> Posters { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Orders> Orders { get; set; }
public DbSet<Tier> Tiers { get; set; }
public DbSet<Hall> Halls { get; set; }
public DbSet<Users> Users { get; set; }
}
}
Users model class:
namespace PlanetariumModelsFramework
{
public class Users
{
public int Id { get; set; }
public string Username { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string UserPassword { get; set; } = string.Empty;
public string UserRole { get; set; } = string.Empty;
}
}
Repository class, where the error occurs:
namespace PlanetariumRepositories
{
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly PlanetariumServiceContext RepositoryPlanetarium;
public Repository(PlanetariumServiceContext repositoryPatternDemoContext)
{
RepositoryPlanetarium = repositoryPatternDemoContext;
}
public IQueryable<TEntity> GetAll()
{
try
{
return RepositoryPlanetarium.Set<TEntity>();
}
catch (Exception ex)
{
throw new Exception($"Couldn't retrieve entities: {ex.Message}");
}
}
public async Task<TEntity> AddAsync(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null");
}
try
{
await RepositoryPlanetarium.AddAsync(entity);
await RepositoryPlanetarium.SaveChangesAsync();
return entity;
}
catch (Exception ex)
{
throw new Exception($"{nameof(entity)} could not be saved: {ex.Message}");
}
}
public async Task<TEntity> UpdateAsync(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null");
}
try
{
RepositoryPlanetarium.Update(entity);
object p = await RepositoryPlanetarium.SaveChangesAsync();
return entity;
}
catch (Exception ex)
{
throw new Exception($"{nameof(entity)} could not be updated: {ex.Message}");
}
}
public async Task<TEntity> DeleteAsync(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException($"{nameof(AddAsync)} entity must not be null");
}
try
{
RepositoryPlanetarium.Remove(entity);
object p = await RepositoryPlanetarium.SaveChangesAsync();
return entity;
}
catch (Exception ex)
{
throw new Exception($"{nameof(entity)} could not be deleted: {ex.Message}");
}
}
}
}
UsersRepository class which inherits it (optional):
using PlanetariumModels;
namespace PlanetariumRepositories
{
public class UsersRepository : Repository<Users>, IUsersRepository
{
public UsersRepository(PlanetariumServiceContext repositoryPatternDemoContext) : base(repositoryPatternDemoContext)
{
}
public Task<Users> GetByUsernameAsync(string username)
{
return base.GetAll().FirstOrDefaultAsync(x => x.Username == username);
}
public Task<Users> GetByEmailAsync(string email)
{
return base.GetAll().FirstOrDefaultAsync(x => x.Email == email);
}
public new List<Users> GetAll()
{
return base.GetAll().ToList<Users>();
}
}
}
My SQL Server table creating script:
I tried to change type to System.String, tried to change its type but nothing works. And, honestly, I don't have any idea cause this error is making literally no sense.
Thank you for your tips in comments.
The problem was manual alteration of database, which is forbidden when using ORM frameworks, like EntityFramework.
Simple migration fixed the issue completely.
For that, I had to open package manager in View -> Package Manager Console.
And enter next 2 commands for migrating:
Add-Migration <MigrationName>
And...
update-database <MigrationName>

EntityType no keys defined

I'm creating an app where users log in via Facebook oAuth and then set up a list of songs. I am getting the following error message:
BandFinderCsharp.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. Define the key for this EntityType.
BandFinderCsharp.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined.
The error message is occurring within my SongsController:
`namespace BandFinder.Controllers.Bread
{
public class SongsController : Controller
{
private SongDBContext db = new SongDBContext();
// GET: Songs
public ActionResult Index()
{
return View(db.Songs.ToList()); <--- This is where the error occurs
}
// GET: Songs/Details/5
public ActionResult Details(long? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Song song = db.Songs.Find(id);
if (song == null)
{
return HttpNotFound();
}
return View(song);
}
// GET: Songs/Create
public ActionResult Create()
{
return View();
}
// POST: Songs/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,UserId,BandId,Title,Artist,Genre,ListId,CreatedOn")] Song song)
{
if (ModelState.IsValid)
{
song.CreatedOn = DateTime.Now;
db.Songs.Add(song);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(song);
}
// GET: Songs/Edit/5
public ActionResult Edit(long? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Song song = db.Songs.Find(id);
if (song == null)
{
return HttpNotFound();
}
return View(song);
}
// POST: Songs/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,UserId,BandId,Title,Artist,Genre,ListId,CreatedOn")] Song song)
{
if (ModelState.IsValid)
{
db.Entry(song).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(song);
}
// GET: Songs/Delete/5
public ActionResult Delete(long? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Song song = db.Songs.Find(id);
if (song == null)
{
return HttpNotFound();
}
return View(song);
}
// POST: Songs/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(long id)
{
Song song = db.Songs.Find(id);
db.Songs.Remove(song);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}`
The thing I don't understand is, this controller has nothing to do with the IdentityUser code..
This is my ApplicationUser Model:
namespace BandFinderCsharp.Models
{
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
CreatedOn = DateTime.Now;
this.ProfileImage = new byte[0];
this.facebookImage = new byte[0];
}
public byte[] facebookImage { get; set; }
[MaxLength(32)]
public string FirstName { get; set; }
[MaxLength(32)]
public string LastName { get; set; }
public byte[] ProfileImage { get; set; }
//public virtual ICollection<Instrument> Instruments { get; set; }
//public virtual ICollection<Song> Songs { get; set; }
//public virtual ICollection<Band> Bands { get; set; }
public string Zipcode { get; set; }
[Index]
public float Longitude { get; set; }
[Index]
public float Latitude { get; set; }
[Required]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime CreatedOn { get; set; }
//////////////
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
base.OnModelCreating(modelBuilder);
}
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
Why am I getting an error referring to Identity models from the songs controller? There should be no correlation between the two at this point.
The IdentityUser class is a built in .NET class which I don't believe I'm able to edit:
namespace Microsoft.AspNet.Identity.EntityFramework
{
//
// Summary:
// Default EntityFramework IUser implementation
public class IdentityUser : IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string>
{
//
// Summary:
// Constructor which creates a new Guid for the Id
public IdentityUser();
//
// Summary:
// Constructor that takes a userName
//
// Parameters:
// userName:
public IdentityUser(string userName);
}
}
IdentityUserLogin
namespace Microsoft.AspNet.Identity.EntityFramework
{
//
// Summary:
// Entity type for a user's login (i.e. facebook, google)
public class IdentityUserLogin : IdentityUserLogin<string>
{
public IdentityUserLogin();
}
}
If the ApplicationUser class is the object you're looking to save in the Data Base, then it must contain a field named Id which is by default the primary key of the object to which Entity Framework is linking to.
Your Object should look like:
public class ApplicationUser
{
public int Id { get; set; }
public string Name { get; set; }
}
Or if you want to set a different property as the primary key for the object, you should add the [Key] attribute above that field - and you'll also need to add the System.ComponentModel.DataAnnotations namespace:
public class ApplicationUser
{
public int Id { get; set; }
[Key]
public string Name { get; set; }
}
Looking at your entities, I am missing the [Key] attribute that defines the fields for the primary key.
Look at this question, first answer:
EntityType 'Category' has no key defined. Define the key for this EntityType

Dbset is not getting the data from database

class InvoiceAppDBContext: DbContext
{
public InvoiceAppDBContext() : base("InvoiceDBContext")
{
}
public DbSet<Users> User { get; set; }
public DbSet<Invoice> Invoices { get; set; }
}
class DataBaseUserRespository : IUser
{
private InvoiceAppDBContext _dbContext;
public DataBaseUserRespository()
{
_dbContext = new InvoiceAppDBContext();
}
public IEnumerable<Users> _userList
{
get
{
return _dbContext.User; // this is not working _userlist is showing null when i run it
}
}
public void SaveUser(Users user)
{
if(user.UserId == 0)
{
_dbContext.User.Add(user);
}
else
{
Users userEntity = _dbContext.User.Find(user.UserId);
userEntity.Change(user);
}
_dbContext.SaveChanges();
}
}
so DBSet or DBcontext is not getting the data from the table and putting it in IEnumerable<Users> list.
here i am debugging the userlist(accountList) and it is not showing data
hopefully this explains what problem i am getting :)

How to insert record using viewmodels : Asp.Net Mvc

Introduction
I am working with the mvc project, implementation approach is code-first.
Domain Model i am using, have more than 70 fields.So i made ViewModels.
Need for making view model is due to creating form wizard which store information in server side(Session Variable).
Domain Model
public class RegisterationDM
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int RegisterationId { get; set; }
//other fields
}
Main View Model
public class RegistrationViewModel
{
public PersonalViewModel PersonalViewModel {get;set; }
public DetailViewmodel DetailedViewmodel { get; set; }
public PhysicalDetailViewModel PhysicalDetailViewModel { get; set; }
public RequirementViewModel RequirementViewModel { get; set; }
public CreationInfoViewModel CreationInfoViewModel { get; set; }
}
Separate Classes
public class PersonalViewModel()
{
//fields
}
public class DetailViewmodel()
{
//fields
}
public class PhysicalDetailViewModel()
{
//fields
}
public class RequirementViewModel()
{
fields
}
public class CreationInfoViewModel()
{
//fields
}
Record Insertion Code
public ActionResult SaveInformation()
{
RegisterationDM regdm = new RegisterationDM();
RegistrationViewModel regvm = new RegistrationViewModel();
PersonalViewModel personalvm = (PersonalViewModel)Session["Personal"];
DetailViewmodel detailvm = (DetailViewmodel)Session["Detail"];
PhysicalDetailViewModel physicalvm = (PhysicalDetailViewModel)Session["Physical"];
RequirementViewModel requirementvm = (RequirementViewModel)Session["Requirement"];
CreationInfoViewModel createdinforvm = new CreationInfoViewModel();
createdinforvm.CreatedBy = "";
createdinforvm.CreatedDate = DateTime.Now;
regvm.PersonalViewModel = personalvm;
regvm.DetailedViewmodel = detailvm;
regvm.PhysicalDetailViewModel = physicalvm;
regvm.RequirementViewModel = requirementvm;
regvm.CreationInfoViewModel = createdinforvm;
//here assigning of view model to domain model
db.Reg.Add(regdm);
db.SaveChanges();
return View();
}
All Actions(Updated)
public ActionResult Step1()
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.PersonalViewModel);
}
[HttpPost]
public ActionResult Step1(PersonalViewModel personalvm)
{
if (ModelState.IsValid)
{
//Store the wizard in session
Session["Personal"] = personalvm;
return RedirectToAction("Step2");
}
else
{
return View(personalvm);
}
}
public ActionResult Step2()
{
if (Session["Personal"] != null)
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.DetailedViewmodel);
}
else
{
return RedirectToAction("Step1");
}
}
[HttpPost]
public ActionResult Step2(DetailViewmodel detailvm)
{
if (ModelState.IsValid)
{
//Store the wizard in session
Session["Detail"] = detailvm;
return RedirectToAction("Step3");
}
return View(detailvm);
}
public ActionResult Step3()
{
if (Session["Detail"] != null && Session["Personal"] != null)
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.PhysicalDetailViewModel);
}
else
{
return RedirectToAction("Step1");
}
}
[HttpPost]
public ActionResult Step3(PhysicalDetailViewModel physicalsvm)
{
if (ModelState.IsValid)
{
//Store the wizard in session
Session["Physical"] = physicalsvm;
return RedirectToAction("Step4");
}
return View(physicalsvm);
}
public ActionResult Step4()
{
if (Session["Detail"] != null && Session["Personal"] != null && Session["Physical"] != null)
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.RequirementViewModel);
}
else
{
return RedirectToAction("Step1");
}
}
[HttpPost]
public ActionResult Step4(RequirementViewModel requirementvm)
{
if (ModelState.IsValid)
{
Session["Requirement"] = requirementvm;
return RedirectToAction("SaveInformation");
}
return View(requirementvm);
}
Question
How can i add(record) using main view model.Should i map first?
I understand its not possible like that.So i ask, is there a proper way of doing that.What it might be?The best way, the right way or the wrong way?
I will prefer standard implementation even if it is hard to implement.
If someone have ideas about this problem, please do help.Any kind of help or reference will be appreciated.Thanks for your time.
(Due to lack of knowledge, may be i made some mistakes.Down Voters are welcome but please leave comment so i can improve question.)
By Defining your own Custom model binder would be suitable for such scenarios. I would recommend you to find some useful resources to get knowledge on how to implement this, Also this is one simple straight forward article CodeProject Custom Model Binder . Let me know if this was useful
We used Automapper to map fields. It is very helpful. Keeps code clean. It has customizable pre and post mapping functions too.

Integrating ASP.NET Identity into Existing DbContext

I'm working on an ASP.NET MVC 5 project in VS2013, .NET 4.5.1, that uses Entity Framework 6 Code-First. I have a decent size database built out and somewhat working (project is about two weeks old). I want to integrate user authentication now, but I'm not sure how to approach it. After spending most of the day researching I've decided to give the new ASP.NET Identity framework a shot over having to write custom Membership or Role providers. What I'm confused about is how to make it all work with the existing database/model I have.
Currently I have an object called Employee that holds basic employee information (for now). After having pondered the question all day, I decided to decouple authentication from it into a User object, which is what Identity wants anyway. That being said how do I make it all work?
Here's my Employee class:
public class Employee : Person {
public int EmployeeId { get; set; }
public byte CompanyId { get; set; }
public string Name {
get {
return String.Format("{0} {1}", this.FirstName, this.LastName);
}
}
public string Password { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<Email> Emails { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
public Employee() {
this.Addresses = new List<Address>();
this.Emails = new List<Email>();
this.Phones = new List<Phone>();
}
}
And my DbContext derived class:
public class DatabaseContext : DbContext {
static DatabaseContext() {
Database.SetInitializer<DatabaseContext>(new DatabaseInitializer());
}
public DatabaseContext()
: base("Name=DatabaseContext") {
this.Database.Initialize(true);
}
public DatabaseContext(
string connectionString)
: base(connectionString) {
this.Database.Initialize(true);
}
/// DbSets...
public override int SaveChanges() {
try {
return base.SaveChanges();
} catch (DbEntityValidationException e) {
IEnumerable<string> errors = e.EntityValidationErrors.SelectMany(
x =>
x.ValidationErrors).Select(
x =>
String.Format("{0}: {1}", x.PropertyName, x.ErrorMessage));
throw new DbEntityValidationException(String.Join("; ", errors), e.EntityValidationErrors);
}
}
protected override void OnModelCreating(
DbModelBuilder modelBuilder) {
modelBuilder.Ignore<Coordinate>();
/// Configs...
base.OnModelCreating(modelBuilder);
}
}
So after spending about a day or so reading and reading, I ended up building my own Identity implementation. First what I did was take my existing Employee object and extended it to inherit from IUser<int>. IUser<int> is an interface that's a part of Identity 2.0 (currently in alpha) that allows the primary key type to be configured to something other than string as was default in 1.0. Because of the way I'm storing data, my implementation was really specific. For example, an Employee can have multiple Email objects related to it, and for my application I wanted to use emails as the user names. So, I simply set the UserName property to return the Employee's work email:
public string UserName {
get {
if (this.WorkEmail != null) {
return this.WorkEmail.Address;
}
return null;
}
set {
/// This property is non-settable.
}
}
Side note, since I'm not going to be using the setter for the property, is there a cleaner way of obsoleting it other than simply leaving it empty?
Moving on, I also added the PasswordHash property. I added my own Role object, inheriting from IRole<int>. Lastly the Employee and Role objects each have an ICollection<T> linking to each other. Another side note, the Entity Framework implementation of Identity manually creates the mapping table UserRoles rather than leveraging it's own configuration capabilities and I can't seem to understand the reasoning behind it. The UserRole it creates does get passed into the *Stores it implements, but it doesn't really do anything special other than act as a link. In my implementation I simply used the already established link, which of course creates a mapping table in the database, but is not pointlessly exposed into the application. I just find it curious.
Moving on again, with my configured objects I went ahead and implemented my own IUserStore and IRoleStore classes creatively called EmployeeStore and RoleStore:
public class EmployeeStore : IQueryableUserStore<Employee, int>, IUserStore<Employee, int>, IUserPasswordStore<Employee, int>, IUserRoleStore<Employee, int>, IDisposable {
private bool Disposed;
private IDatabaseRepository<Role> RolesRepository { get; set; }
private IDatabaseRepository<Employee> EmployeesRepository { get; set; }
public EmployeeStore(
IDatabaseRepository<Role> rolesRepository,
IDatabaseRepository<Employee> employeesRepository) {
this.RolesRepository = rolesRepository;
this.EmployeesRepository = employeesRepository;
}
#region IQueryableUserStore Members
public IQueryable<Employee> Users {
get {
return this.EmployeesRepository.Set;
}
}
#endregion
#region IUserStore Members
public async Task CreateAsync(
Employee employee) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
await this.EmployeesRepository.AddAndCommitAsync(employee);
}
public async Task DeleteAsync(
Employee employee) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
await this.EmployeesRepository.RemoveAndCommitAsync(employee);
}
public Task<Employee> FindByIdAsync(
int employeeId) {
this.ThrowIfDisposed();
return Task.FromResult<Employee>(this.EmployeesRepository.FindSingleOrDefault(
u =>
(u.Id == employeeId)));
}
public Task<Employee> FindByNameAsync(
string userName) {
this.ThrowIfDisposed();
return Task.FromResult<Employee>(this.EmployeesRepository.FindSingleOrDefault(
e =>
(e.UserName == userName)));
}
public async Task UpdateAsync(
Employee employee) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
await this.EmployeesRepository.CommitAsync();
}
#endregion
#region IDisposable Members
public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(
bool disposing) {
this.Disposed = true;
}
private void ThrowIfDisposed() {
if (this.Disposed) {
throw new ObjectDisposedException(base.GetType().Name);
}
}
#endregion
#region IUserPasswordStore Members
public Task<string> GetPasswordHashAsync(
Employee employee) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
return Task.FromResult<string>(employee.PasswordHash);
}
public Task<bool> HasPasswordAsync(
Employee employee) {
return Task.FromResult<bool>(!String.IsNullOrEmpty(employee.PasswordHash));
}
public Task SetPasswordHashAsync(
Employee employee,
string passwordHash) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
employee.PasswordHash = passwordHash;
return Task.FromResult<int>(0);
}
#endregion
#region IUserRoleStore Members
public Task AddToRoleAsync(
Employee employee,
string roleName) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
if (String.IsNullOrEmpty(roleName)) {
throw new ArgumentNullException("roleName");
}
Role role = this.RolesRepository.FindSingleOrDefault(
r =>
(r.Name == roleName));
if (role == null) {
throw new InvalidOperationException("Role not found");
}
employee.Roles.Add(role);
return Task.FromResult<int>(0);
}
public Task<IList<string>> GetRolesAsync(
Employee employee) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
return Task.FromResult<IList<string>>(employee.Roles.Select(
r =>
r.Name).ToList());
}
public Task<bool> IsInRoleAsync(
Employee employee,
string roleName) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
if (String.IsNullOrEmpty(roleName)) {
throw new ArgumentNullException("roleName");
}
return Task.FromResult<bool>(employee.Roles.Any(
r =>
(r.Name == roleName)));
}
public Task RemoveFromRoleAsync(
Employee employee,
string roleName) {
this.ThrowIfDisposed();
if (employee == null) {
throw new ArgumentNullException("employee");
}
if (String.IsNullOrEmpty(roleName)) {
throw new ArgumentNullException("roleName");
}
Role role = this.RolesRepository.FindSingleOrDefault(
r =>
(r.Name == roleName));
if (role == null) {
throw new InvalidOperationException("Role is null");
}
employee.Roles.Remove(role);
return Task.FromResult<int>(0);
}
#endregion
}
RoleStore:
public class RoleStore : IQueryableRoleStore<Role, int>, IRoleStore<Role, int>, IDisposable {
private bool Disposed;
private IDatabaseRepository<Role> RolesRepository { get; set; }
public RoleStore(
IDatabaseRepository<Role> rolesRepository) {
this.RolesRepository = rolesRepository;
}
#region IQueryableRoleStore Members
public IQueryable<Role> Roles {
get {
return this.RolesRepository.Set;
}
}
#endregion
#region IRoleStore Members
public async Task CreateAsync(
Role role) {
this.ThrowIfDisposed();
if (role == null) {
throw new ArgumentNullException("role");
}
await this.RolesRepository.AddAndCommitAsync(role);
}
public async Task DeleteAsync(
Role role) {
this.ThrowIfDisposed();
if (role == null) {
throw new ArgumentNullException("role");
}
await this.RolesRepository.RemoveAndCommitAsync(role);
}
public Task<Role> FindByIdAsync(
int roleId) {
this.ThrowIfDisposed();
return Task.FromResult<Role>(this.RolesRepository.FindSingleOrDefault(
r =>
(r.Id == roleId)));
}
public Task<Role> FindByNameAsync(
string roleName) {
this.ThrowIfDisposed();
return Task.FromResult<Role>(this.RolesRepository.FindSingleOrDefault(
r =>
(r.Name == roleName)));
}
public async Task UpdateAsync(
Role role) {
this.ThrowIfDisposed();
if (role == null) {
throw new ArgumentNullException("role");
}
await this.RolesRepository.CommitAsync();
}
#endregion
#region IDisposable Members
public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(
bool disposing) {
this.Disposed = true;
}
private void ThrowIfDisposed() {
if (this.Disposed) {
throw new ObjectDisposedException(base.GetType().Name);
}
}
#endregion
}
Now, what I noticed was that the Entity Framework implementation was creating what looked like a mini-repository. Since my project was already using my own Repository implementation, I decided to leverage it instead. We'll see how that goes...
Now, all of this works and surprisingly does not crash at all, or at least hasn't yet. That being said, I have all of these wonderful Identity implementations, yet I can't seem to figure out how to leverage them inside my MVC application. Since that falls out of scope for this question, I'll go ahead and open a new one addressing that.
I'm leaving this as the answer to the question in case someone else runs into this in the future. Of course, if anyone sees an error in the code I've posted, please let me know.
Take a look at the SimpleSecurity Project source code for an example of how the database context of ASP.NET Identity was extended to include new tables. This may work for your situation. Here is the how the new context was defined by inheriting from the ASP.NET Identity context.
public class SecurityContext : IdentityDbContext<ApplicationUser>
{
public SecurityContext()
: base("SimpleSecurityConnection")
{
}
public DbSet<Resource> Resources { get; set; }
public DbSet<OperationsToRoles> OperationsToRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ResourceConfiguration());
modelBuilder.Configurations.Add(new OperationsToRolesConfiguration());
}
}
The SimpleSecurity Project decouples ASP.NET Identity from your MVC application and extends it.
Since your Employee class appears to be the user profile for membership I would look at tailoring it to fit with how you customize the user profile in ASP.NET Identity, which is discussed here. Basically your Employee class needs to inherit from IdentityUser and you would remove the Password property from Employee, since this is defined in IdentityUser and the framework looks for it there. Then when defining your context you would use the Employee class instead so it would look something like this
public class DatabaseContext : IdentityDbContext<Employee>
{
...
}
No one solution fits all situations, but for my project I found that the easiest thing to do was extend the IdentityUser and IdentityDbContext classes. Below is pseudocode that focuses on the bare minimum you would need to change/add to get this working.
For your user class:
public class DomainUser : IdentityUser
{
public DomainUser(string userName) : base(userName) {}
public DomainUser() {}
}
For your DbContext implementation:
public class DomainModelContext : IdentityDbContext<DomainUser>
{
public DomainModelContext()
: base() {}
public DomainModelContext(string nameOrConnectionString)
: base(nameOrConnectionString) {}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
And in Startup.Auth.cs:
public static Func<UserManager<DomainUser>> UserManagerFactory { get; set; }
static Startup()
{
UserManagerFactory = () => new UserManager<DomainUser>(new UserStore<DomainUser>(new DomainModelContext()));
}
Another potential option is to create a 1-1 relationship between your DomainUser class and the ApplicationUser class which inherits from IdentityUser. This would reduce the coupling between your domain model and the Identity mechanism, especially if you used WithRequiredDependent without creating a bidirectional navigation property, something like so:
modelBuilder.Entity<ApplicationUser>().HasRequired(au => au.DomainUser).WithRequiredPrincipal();

Categories

Resources