Soft deleted child objects appear in parent objects list - c#

I'm using Entity Framework Core 2.2 code first. Soft delete is implemented in the overriden Context class. There is an UnitOfWork class that initializes Repositories for specific entities, while taking the Context as argument in constructor.
Some of the application entities have been left out in this sample, for a clearer overview. I will only use Team, Member, Project and Employee.
While testing, a realized the following: when I delete an object (for instance, a Project), it successfully sets it's "Deleted" property to "true" after SaveChanges(). However, if the solution project is still running, I realized that if this object (Project) has a parent object (Team), it will still appear in the team's List Projects, even though their "Deleted" property is clearly "true". While the process is still running, if I call the repository Get() method that returns all Projects, the deleted project won't show up in the returned projects list, but only in the team's List Projects.
Only when I rerun the solution project, these child objects don't appear in parent's List Projects anymore.
Here is a NUnit test I've written to analyze the issue:
[Test, Order(2)]
public void GetTeamAfterDeletingProject()
{
//There are initially 11 projects
var projects = unit.Projects.Get().ToList();
Assert.AreEqual(11, projects.Count());
Project project = unit.Projects.Get(8);
unit.Projects.Delete(project);
unit.Save();
//now there are 10 projects
projects = unit.Projects.Get().ToList();
Assert.AreEqual(10, projects.Count());
//this team had initially 2 projects, and one of them was deleted
Team team = unit.Teams.Get(3);
//only one project should be left, but there are actually two showing up - this test fails
Assert.AreEqual(1, team.Projects.Count);
project.Deleted = false;
unit.Save();
}
This interferes with my implemented Delete method in the TeamsRepository, which doesn't allow deletion of an object if there are child object's in his list. This means, that, If I wanted to delete the team in the previous test, even if I deleted all his projects, I still wouldn't be able to do it without restarting the app (the whole repository can be found below)
public override void Delete(int id)
{
Team old = Get(id);
if (old.Projects.Count != 0 || old.Members.Count != 0)
{
Services.ThrowChildrenPresentException();
}
Delete(old);
}
I'm wondering if this is the normal behavior when using lazy loading, or did I miss something in the configuration.
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
{
if(_conStr != null)
{
optionBuilder.UseNpgsql(_conStr);
}
optionBuilder.UseLazyLoadingProxies(true);
base.OnConfiguring(optionBuilder);
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Member>().HasQueryFilter(x => !x.Deleted);
builder.Entity<Employee>().HasQueryFilter(x => !x.Deleted);
builder.Entity<Project>().HasQueryFilter(x => !x.Deleted);
builder.Entity<Team>().HasQueryFilter(x => !x.Deleted);
}
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries().Where(x => x.State == EntityState.Deleted && x.Entity is BaseClass))
{
entry.State = EntityState.Modified;
entry.CurrentValues["Deleted"] = true;
}
return base.SaveChanges();
}
This is the Team entity:
public class Team: BaseClass
{
public Team()
{
Members = new List<Member>();
Projects = new List<Project>();
}
public string Name { get; set; }
public string Description { get; set; }
public bool StatusActive { get; set; }
public virtual IList<Member> Members { get; set; }
public virtual IList<Project> Projects { get; set; }
}
public class Member: BaseClass
{
public virtual Team Team { get; set; }
public virtual Employee Employee { get; set; }
public virtual Role Role { get; set; }
public virtual MemberStatus Status { get; set; }
public decimal HoursWeekly { get; set; }
}
public class BaseClass
{
public BaseClass()
{
Created = DateTime.Now;
Creator = 0;
Deleted = false;
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public int Creator { get; set; }
public bool Deleted { get; set; }
}
The generic Repository and the specific TeamsRepository with overriden methods. The soft deleted projects appear in old.Projects.Count, so I am never able to delete a Team this way (even though they aren't retrieved when
public class Repository<Entity>: IRepository<Entity> where Entity : class
{
protected AppContext _context;
protected DbSet<Entity> _dbSet;
public Repository(AppContext context)
{
_context = context;
_dbSet = _context.Set<Entity>();
}
public void ValidateUpdate(Entity newEntity, int id)
{
if (id != (newEntity as BaseClass).Id)
throw new ArgumentException($"Error! Id of the sent object: {(newEntity as BaseClass).Id} and id in url: {id} do not match");
}
public virtual IQueryable<Entity> Get() => _dbSet;
public virtual IList<Entity> Get(Func<Entity, bool> where) => Get().Where(where).ToList();
public virtual Entity Get(int id)
{
Entity entity = _dbSet.Find(id);
if (entity == null)
throw new ArgumentException($"There is no object with id: {id} in the database");
return entity;
}
public virtual void Insert(Entity entity)
{
entity.Build(_context);
_dbSet.Add(entity);
}
public virtual void Update(Entity entity, int id)
{
entity.Build(_context);
Entity old = Get(id);
ValidateUpdate(entity, id);
_context.Entry(old).CurrentValues.SetValues(entity);
old.Relate(entity);
}
public void Delete(Entity entity) => _dbSet.Remove(entity);
public virtual void Delete(int id)
{
Entity old = Get(id);
Delete(old);
}
}
public class TeamsRepository: Repository<Team>
{
public TeamsRepository(AppContext context) : base(context) { }
public override void Delete(int id)
{
Team old = Get(id);
if (old.Projects.Count != 0 || old.Members.Count != 0)
{
Services.ThrowChildrenPresentException();
}
Delete(old);
}
}

Related

Is there something like OnDelete callback for ASP.NET Core model?

I have the following model:
public class MyFiles
{
public int MyFilesId { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
As you can understand it's a model for uploaded files. Each model keeps path to wwwroot/ ... and file's (image for example) name. But how to delete a file when I delete this model? I mean:
var res = _applicationDbContext.MyFiles.FirstOrDefault(x => x.MyFilesId = 666);
_applicationDbContext.MyFiles.Remove(res);
_applicationDbContext.SaveChanges();
Of course, the above code will not delete file. It would very useful if I can write something like:
public class MyFiles
{
public int MyFilesId { get; set; }
public string Name { get; set; }
public string Path { get; set; }
protected OnDelete() { // here is logic for removing the file from OS }
}
There is no event mechanism to subscribe to in EF. What you could do though would be to override the SaveChanges as described here
What is described is a variation of the following:
public interface IFileSystemEntity
{
string Path { get; set; }
}
public class MyFiles: IFileSystemEntity
{
public int MyFilesId { get; set; }
public string Name { get; set; }
public string Path { get; set; }
protected OnDelete() { // here is logic for removing the file from OS }
}
And then in your DBContext
public override int SaveChanges()
{
this.ChangeTracker.DetectChanges();
var added = this.ChangeTracker.Entries()
.Where(t => t.State == EntityState.Deleted)
.Select(t => t.Entity)
.ToArray();
foreach (var entity in added)
{
if (entity is IFileSystemEntity)
{
var track = entity as IFileSystemEntity;
// Remove logic here
}
}
return base.SaveChanges();
}
There is no direct callback but you can override OnSave() in your DbContext and call the delete method there there.
public class MyFiles
{
public int MyFilesId { get; set; }
public string Name { get; set; }
public string Path { get; set; }
protected OnDelete() { // here is logic for removing the file from OS }
}
public class MyDbContext : DbContext
{
public override int SaveChanges()
{
// iterate through deletions
foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted))
{
// if item being deleted is MyFiles, call OnDelete
if (item.Entity is MyFiles myFile)
myFile.OnDelete();
}
return base.SaveChanges();
}
}
I'm unaware of how long it will take to delete the file from the OS, how often this will happen and how important it is to delete the file immediately. However, if it takes time to delete a file adding this logic could considerably slow down your context.SaveChanges() calls. To avoid this I would consider adding 'files to be deleted' to some sort of queue so that these can be processed independantly of the save operation on the dbcontext.
You can override method SaveChanges() and call in it to check deleted object in ChangeTracker and call your callback
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
foreach (var entityEntry in ChangeTracker.Entries().Where(entry => entry.State == EntityState.Deleted))
{
var oldEntity = entityEntry.Entity;
// my callback
}
return base.SaveChanges(acceptAllChangesOnSuccess);
}

DI Implementation in asp.net project

I am using ASP.Net, Entity Framwork along with Microsoft Unity Framework integration, i have below setup for the data consumption:
DataObjects:
ArticleDo - This contains all the entities needed for the database.
GenericDo - This contains all the entities which are common across project.
Dao. (Concrete classes)
ArticleDao - This contains the implemented functions specific for Articles from IArticleDao. (This inherits GenericDao, IArticleDao
GenericDao - This is an abstract class it contains the implementation of the Generic functions used across the project.
Interfaces.
IArticleDao - This contains the declaration of the Article specific functions. (this inherits IGenericDao)
IGenericDao -This contains the declaration of all the generic functions which are common across projects, like pulling in specific field from database etc. (This inherits IGenericDao
Here's the implementation:
DataObjects.
1. ArticleDo:
public class ArticleDo
{
public virtual int Id { get; set; }
public virtual int WordCount { get; set; }
public virtual string Title { get; set; }
}
2. GenericDo:
public class GenericDo
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
Business Layer (Concrete classes)
1. ArticleDao:
public class ArticleDao : GenericDao<ArticleDo, DataContext>, IArticleDao
{
private readonly Data.DataContext db;
public ArticleDao()
{
db = new Data.DataContext();
}
public override string Add(ArticleDo entity)
{
entity.AddedBy = Common.CurrentUsername;
db.Articles.Add(entity);
var returnVal = db.SaveChanges();
if (returnVal > 0)
return "Success";
return "Fail";
}
public List<ArticleDo> Search(string SearchTerm, int CurrentPage, out int Total)
{
SearchTerm = SearchTerm.ToLower();
var totalRecords = List().Where(x => x.ArticleId.ToString() == SearchTerm ||
(x.Title.ToString().ToLower().Contains(SearchTerm)));
Total = totalRecords.Count();
return totalRecords.Skip(CurrentPage * Common.Page_SIZE).Take(Common.Page_SIZE).ToList();
}
}
2. GenericDao:
public abstract class GenericDao<T, C> : IGenericDao<T, C> where T : class where C : DbContext, new ()
{
private readonly DataContext _db = new DataContext();
private C _entities = new C();
public virtual string Add(T entity)
{
_entities.Set<T>().Add(entity);
return "Success";
}
public virtual bool Delete(T entity)
{
_entities.Set<T>().Remove(entity);
return true;
}
public T Get(T entity)
{
var _get = _entities.Set<T>();
if (_get == null) return null;
return _get.First();
}
public virtual List<T> List()
{
return _entities.Set<T>().ToList();
}
public virtual bool Update(T entity)
{
_entities.Entry(entity).State = EntityState.Modified;
return true;
}
public T FindByProperty(string property, object value)
{
var _find = _db.Database.SqlQuery<IEnumerable<T>>(string.Format("SELECT a.* from {0} a where a.p0 = p1", typeof(T).FullName), new object[] { property, value });
if (_find != null && _find.Any())
{
return (T)_find.First();
}
return null;
}
}
DataInterfaces.
1. IArticleDao
public interface IArticleDao : IGenericDao<ArticleDo, DataContext>
{
List<ArticleDo> Search(string SearchTerm, int CurrentPage, out int Total);
}
2. IGenericDao
public interface IGenericDao : IGenericDao<GenericDo, DataContext> { }
public interface IGenericDao<T, C>
{
string Add(T entity);
bool Update(T entity);
bool Delete(T entity);
List<T> List();
T Get(T entity);
T FindByProperty(string property, object value);
}
Now before i implement the entities which would resolve the `IArticleDao` and i would be able to call my business layer functions, i tried to create concrete object directly in the page behind like:
IArticleDao dao = new ArticleDao();
Now as per my understanding, since ArticleDao is inheriting GenericDao<ArticleDo, DataContext> i should be able to access all the ArticleDo entities from the dao object, but that's not happening, could anyone please help me to achieve this, i don't want to inherit ArticleDao in ArticleDo, because that would defeat the purpose of DI integration. Also how could i use the contructor injection, do i need to create the constructor in my aspx code behind file?

Entity Framework 6 - Dependency Injection with Unity - Repository pattern - Add or Update exception for many to many relationship

I have a problem when adding new values with a many to many mapping in Entity Framework. I know about the unit of work pattern but in our solution we would like to keep a simple repository pattern and not a unit of work class that contains everything. Is this possible or should I just implement Unit of Work right away?
If I don't use iSupplierRepository below a supplier will be added, but it will always add a new one even though there already exists one with that name.
Error:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
Repository example:
public class SupplierRepository : IntEntityRepository<Supplier, DbContext>, ISupplierRepository
{
public SupplierRepository(DbContext context) : base(context, context.Suppliers)
{
}
}
Inherited repositories:
public class IntEntityRepository<TEntity, TContext> : EntityRepository<TEntity, TContext, int>
where TEntity : class, IEntity<int>
where TContext : BaseIdentityDbContext
{
public IntEntityRepository(TContext context, IDbSet<TEntity> set) : base(context, set)
{
}
public override async Task<TEntity> GetAsync(int id)
{
return (await GetAsync(entity => entity.Id == id)).SingleOrDefault();
}
...
public abstract class EntityRepository<TEntity, TContext, TId> : IEntityRepository<TEntity, TId>
where TEntity : class, IEntity<TId>
where TContext : BaseIdentityDbContext
{
protected TContext Context { get; }
protected IDbSet<TEntity> Set { get; }
protected EntityRepository(TContext context, IDbSet<TEntity> set)
{
Context = context;
Set = set;
}
public abstract Task<TEntity> GetAsync(TId id);
...
Unity:
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
Controller:
private readonly IContactRepository iContactRepository;
private readonly ISupplierRepository iSupplierRepository;
public ContactsController(IContactRepository iContactRepository, ISupplierRepository iSupplierRepository)
{
this.iContactRepository = iContactRepository;
this.iSupplierRepository = iSupplierRepository;
}
[HttpPut]
[Route("UpdateContact/{id}")]
public async Task<IHttpActionResult> UpdateContact(ContactViewModel contactVm, int id)
{
try
{
var supplierList = new List<Supplier>();
foreach (var contactVmSupplier in contactVm.Suppliers)
{
var supplier = await iSupplierRepository.GetAsync(contactVmSupplier.Id);
supplierList.Add(supplier);
}
var contactOriginal = await iContactRepository.GetAsync(id);
var updatedContact = Mapper.Map<ContactViewModel, Contact>(contactVm, contactOriginal);
updatedContact.Suppliers = supplierList;
await iContactRepository.UpdateAsync(updatedContact);
return Ok();
}
catch (Exception e)
{
throw new Exception("Could not update a contact", e);
}
}
Viewmodels:
public class ContactViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SupplierViewModel> Suppliers { get; set; }
}
public class SupplierViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Models:
public class Contact : IEntity<int>
{
public Contact()
{
Suppliers = new List<Supplier>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
}
public class Supplier: IEntity<int>
{
public Supplier()
{
Contacts = new List<Contact>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
If you install the Unity bootstrapper for ASP.NET Web API package, a UnityHierarchicalDependencyResolver is available which will use a new child container for each IHttpController resolution effectively making all registrations with a HierarchicalLifetimeManager resolved per request so that all repository instances in a controller will use the same DbContext.
The NuGet package will also install some bootstrapping code in App_Start which uses WebActivatorEx. You can either use this approach or change to align with what you are using right now. Based on your posted code it would look something like:
public static void ConfigureUnity(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterType<DbContext>(new HierarchicalLifetimeManager());
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
}
Solved it like this, dependency injection is from the tutorial Dependency Injection in ASP.NET Web API 2.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
App_Start -> WebApiConfig
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
UnityConfig:
public static void ConfigureUnity(HttpConfiguration config)
{
var context = new DbContext();
var container = new UnityContainer();
container.RegisterType<ISupplierRepository, SupplierRepository>(new InjectionConstructor(context));
container.RegisterType<IContactRepository, ContactRepository>(new InjectionConstructor(context));
config.DependencyResolver = new UnityResolver(container);
}
Update: Use Randy Levy's answer instead.
My recommendation here is not to use Repository or UoW at all. EF already has them implemented. You'll encounter a lot of issues trying to re-implement them.
As to specific issue you encounter with exception: you have to use the same DbContext for your entities. At the same time, you wouldn't like to use DbContext as Singleton and use it per-request instead. A possible solution for it might be found here.
Application_BeginRequest(...)
{
var childContainer = _container.CreateChildContainer();
HttpContext.Items["container"] = childContainer;
childContainer.RegisterType<ObjectContext, MyContext>
(new ContainerControlledLifetimeManager());
}
Application_EndRequest(...)
{
var container = HttpContext.Items["container"] as IUnityContainer
if(container != null)
container.Dispose();
}

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();

How to override save behavior of object in Entity Framework

I have a class course like this:
public class course
{
public int CourseID { get; set; }
public string Name { get; set; }
public Event Schedule {get; set;} //Event is coming from library Dday.iCal
}
Entity framework cannot correctly understand on how to save this property. ( I want to serialize it to string when saving, and keep it as event when worknig with it in my application.) So I have two methods, say, SerializeToString() and DeserializeFromString(). I want those methods to be applied only when saving to database.
What I came up with the following. Basically I'm trying to have a separate property as a string that will be saved in the database and Event will be ignored, but it doesn't save anything to the database now. I'm not even sure if this is a good approach to do things, or there's something better that can be done.:
public class course
{
public int CourseID { get; set; }
public string Name { get; set; }
private Event _Schedule;
[NotMapped]
public Event Schedule {
get
{
if (!String.IsNullOrEmpty(CourseSchedule))
{
return DeserilizeFromString(CourseSchedule);
}
return new Event();
}
set
{
_schedule = value;
}
}
private string _courseSchedule;
public string CourseSchedule {
get
{
return _courseSchedule;
}
private set
{
if (Schedule != null)
{
_courseSchedule = SerializeToString(Schedule);
}
else
{
_courseSchedule = null;
}
}
}
An author on asp.net actually has an implementation of what your trying to do, almost to a tee. You may want to follow a few points in that project to get you started. The link to the project is here.
Some things to note, is it does utilize the DbContext Api that was implemented in Entity Framework. Some of the abstraction mentioned above is like this:
Your Solution:
Model
View
Controller
Data Access Layer (DAL)
The tutorial will actually go through the implementation with a Course Controller, Unit Of Work Class, and Repositories. By the end of the tutorial it will implement those automatic properties with DbContext and looks like this:
// Model:
public abstract class Person
{
[Key]
public int PersonID { get; set; }
[Required(ErrorMessage = "Last name is required.")]
[Display(Name = "Last Name")]
[MaxLength(50)]
public string LastName { get; set; }
[Required(ErrorMessage = "First name is required.")]
[Column("FirstName")]
[Display(Name = "First Name")]
[MaxLength(50)]
public string FirstMidName { get; set; }
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
}
// Repository:
public class StudentRepository : IStudentRepository, IDisposable
{
private SchoolContext context;
public StudentRepository(SchoolContext context)
{
this.context = context;
}
public IEnumerable<Student> GetStudents()
{
return context.Students.ToList();
}
public Student GetStudentByID(int id)
{
return context.Students.Find(id);
}
public void InsertStudent(Student student)
{
context.Students.Add(student);
}
public void DeleteStudent(int studentID)
{
Student student = context.Students.Find(studentID);
context.Students.Remove(student);
}
public void UpdateStudent(Student student)
{
context.Entry(student).State = EntityState.Modified;
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
// Interface for Repository:
public interface IStudentRepository : IDisposable
{
IEnumerable<Student> GetStudents();
Student GetStudentByID(int studentId);
void InsertStudent(Student student);
void DeleteStudent(int studentID);
void UpdateStudent(Student student);
void Save();
}
// Context to Generate Database:
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment).WithRequired(p => p.Instructor);
modelBuilder.Entity<Course>()
.HasMany(c => c.Instructors).WithMany(i => i.Courses)
.Map(t => t.MapLeftKey("CourseID")
.MapRightKey("PersonID")
.ToTable("CourseInstructor"));
modelBuilder.Entity<Department>()
.HasOptional(x => x.Administrator);
}
}
// Unit Of Work
public class UnitOfWork : IDisposable
{
private SchoolContext context = new SchoolContext();
private GenericRepository<Department> departmentRepository;
private CourseRepository courseRepository;
public GenericRepository<Department> DepartmentRepository
{
get
{
if (this.departmentRepository == null)
{
this.departmentRepository = new GenericRepository<Department>(context);
}
return departmentRepository;
}
}
public CourseRepository CourseRepository
{
get
{
if (this.courseRepository == null)
{
this.courseRepository = new CourseRepository(context);
}
return courseRepository;
}
}
public void Save()
{
context.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
That is some of the content in the lesson, I believe it will answer your question pretty explicitly while giving you an understanding of why the abstraction works, since it does implement the Fluent Api.
Hope that helps.
You should keep your models as minimalistic as possible, just the auto-properties and attributes. For more complex business logic it's good to add another layer to your MVC pattern. This one is usually called Repository (hard to find a good tutorial on Repository Pattern though.. :( )and comes between model and controller controller.
This also is very useful for performing unit tests. When properly implemented it allows you to do replace database dependency with collection during tests. This approach will require a bunch of additional work on the project.
One more approach (a simpler one) would be to add a ViewModel layer. Do it this way:
class MyModel
{
public string Text { get; set; }
}
class MyViewModel : MyModel
{
public new string Text
{
get { return base.Text; }
set { base.Text =value.ToUpper(); }
}
}
class Program
{
static void Main(string[] args)
{
MyViewModel mvm = new MyViewModel();
mvm.Text = "hello there";
var s = ((MyModel) mvm).Text; // "HELLO THERE"
}
}
In DataContext use MyModel in controller use MyViewModel.
If you have a model that looks like this
using (LolEntities context = new LolEntities)
{
...
}
Somewhere in your application, this model is defined, usually something like this:
public partial class LolEntities : ObjectContext
(1) Notice that the class is partial, so you could just create another partial class with the same name and override:
public override int SaveChanges(SaveOptions options)
(2) Or you can just capture the event:
using (DemoAZ_8_0Entities context = new DemoAZ_8_0Entities())
{
context.SavingChanges += ...
}
and do your formatting before it gets sent back to the DB.
In your model just make sure to include a property that properly maps to the column in the DB.
Maybe introducing some abstraction over this logic, you could recreate the unit of work and repository pattern and add the desired logic in a more convenient way. For example in the Course repository class you can costumize the add and find method serializing and deserializing the event field.
I am going to focus on the repository pattern, you can find a lot of information about
the design of the whole data access layer on the web.
For example, to manage courses, your application should depends on a ICourseRepository interface like this
interface ICourseRepository
{
void Add(Course newCourse);
Course FindByID(int id);
}
And you provide the folowing implementation:
class CourseRepository
{
// DbContext and maybe other fields
public void Add(Course c)
{
// Serialize the event field before save the object
_courses.Add(c); // calling entity framework functions, note
// that '_courses' variable could be an DBSet from EF
}
public Course FindById(int id)
{
var course = /// utilize EF functions here to retrieve the object
// In course variable deserialize the event field before to return it ...
}
}
Note that the ObjectContext in EF is an implementation of this pattern, if you are not intresting in change the ORM in the future you can just override the Save method on EF.
If you want to know more about this kind of pattern you can visit the Martin Fowler site:
Unit Of Work pattern
Repository pattern

Categories

Resources