Custom RoleProvider and Custom Membership provider - entity-framework

I am working on an MVC4 application that has authentication completed by ADFS before the application is launched and will only be launched by successfully authenticated users. Within the application itself I have a list of users held within the following class:
public class MyAppUser : IPrincipal
{
[Key]
public int UserID { get; set; }
[StringLength(128)]
public string Username { get; set; }
public int ParticipationPoints { get; set; }
public DateTime JoinDate { get; set; }
public DateTime LastLogin { get; set; }
//1-to-1 Mappings
public int? CompanyID { get; set; }
public virtual Company Company { get; set; }
public int? ProfileID { get; set; }
public virtual Profile Profile { get; set; }
public int? SocialID { get; set; }
public virtual SocialMedia Social { get; set; }
public int? RoleID { get; set; }
public virtual Role Role { get; set; }
//1-to-many Mappings
public virtual ICollection<Block> Blocks { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<CategoryMap> CategoryMaps { get; set; }
public virtual ICollection<Feedback> Feedbacks { get; set; }
public virtual ICollection<Survey> Surveys { get; set; }
public virtual ICollection<Friend> Friends { get; set; }
public virtual ICollection<Participation> Participations { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<Target> Targets { get; set; }
public virtual ICollection<CourseStatus> CourseStatuses { get; set; }
public virtual ICollection<Objective> Objectives { get; set; }
public virtual ICollection<UserCourseInfo> UserCourseInfos { get; set; }
public IIdentity Identity { get; set; }
public bool IsInRole(string role)
{
if (this.Role.Name == role)
{
return true;
}
return false;
}
}
I have created a custom role provider that is tied to my dbcontext as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration.Provider;
using MyApp.Data;
using MyApp.ViewModels;
namespace MyApp.Providers
{
public class MyAppProvider : System.Web.Security.SqlRoleProvider
{
private MyAppContext dbcontext = new MyAppContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyAppContext"].ConnectionString);
private Repository<MyAppUser> userRepository;
private Repository<Role> roleRepository;
public MyAppProvider()
{
this.userRepository = new Repository<MyAppUser>(dbcontext);
this.roleRepository = new Repository<Role>(dbcontext);
}
public override string[] GetAllRoles()
{
IEnumerable<Role> dbRoles = roleRepository.GetAll();
int dbRolesCount = roleRepository.GetAll().Count();
string[] roles = new string[dbRolesCount];
int i = 0;
foreach(var role in dbRoles)
{
roles[i] = role.Name;
i++;
}
return roles;
}
public string[] GetAllUsers()
{
IEnumerable<MyAppUser> dbUsers = userRepository.GetAll();
int dbUsersCount = userRepository.GetAll().Count();
string[] users = new string[dbUsersCount];
int i = 0;
foreach (var user in dbUsers)
{
users[i] = user.Username;
i++;
}
return users;
}
public override bool RoleExists(string roleName)
{
string[] roles = { "Admin", "User", "BobsBusiness" };
if(roles.Contains(roleName))
return true;
else
return false;
}
public override void CreateRole(string roleName)
{
Role newRole = new Role();
newRole.Name = roleName;
roleRepository.Add(newRole);
roleRepository.SaveChanges();
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
foreach (var username in usernames)
{
MyAppUser user = userRepository.Get(u => u.Username == username).FirstOrDefault();
foreach (var rolename in roleNames)
{
Role role = roleRepository.Get(r => r.Name == rolename).FirstOrDefault();
user.RoleID = role.RoleID;
userRepository.Add(user);
userRepository.SaveChanges();
}
}
}
public override string[] GetRolesForUser(string username)
{
MyAppUser user = userRepository.Get(u => u.Username == username).FirstOrDefault();
if (user == null)
{
string[] roles = new string[1];
roles[0] = "Fail";
return roles;
}
else
{
Role role = roleRepository.Get(r => r.RoleID == user.RoleID).FirstOrDefault();
if (role == null)
{
string[] roles = new string[1];
roles[0] = "Fail";
return roles;
}
else
{
string[] roles = new string[1];
roles[0] = role.Name;
return roles;
}
}
}
public override bool IsUserInRole(string userName, string roleName)
{
MyAppUser user = userRepository.Get(u => u.Username == userName).FirstOrDefault();
if (user == null)
throw new ProviderException("Username cannot be empty or null.");
Role role = roleRepository.Get(r => r.Name == roleName).FirstOrDefault();
if (role == null)
throw new ProviderException("Role name cannot be empty or null.");
if (user.RoleID == role.RoleID)
return true;
else
return false;
}
}
}
Now I have tried to create a CustomMembershipProvider but I am unsure how to tie this to the dbcontext and the MyAppUser that I have created. Due to the authentication type we do not have an AccountController and so simpleMembership is not an option. I have created the abstract class for the custom membership provider as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyApp.Data;
namespace MyApp.Providers
{
public class MyAppMembershipProvider : System.Web.Security.MembershipProvider
{
private MyAppContext dbcontext = new MyAppContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyAppContext"].ConnectionString);
private Repository<MyAppUser> userRepository;
public MyAppUser User { get; private set; }
public MyAppMembershipProvider()
{
this.userRepository = new Repository<MyAppUser>(dbcontext);
User = userRepository.Get(u => u.Username == "jpmcfeely").FirstOrDefault();
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
}
public override System.Web.Security.MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out System.Web.Security.MembershipCreateStatus status)
{
throw new NotImplementedException();
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
throw new NotImplementedException();
}
public override bool EnablePasswordReset
{
get { throw new NotImplementedException(); }
}
public override bool EnablePasswordRetrieval
{
get { throw new NotImplementedException(); }
}
public override System.Web.Security.MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override System.Web.Security.MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override System.Web.Security.MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override System.Web.Security.MembershipUser GetUser(string username, bool userIsOnline)
{
//throw new NotImplementedException();
MyAppUser user = userRepository.Get(u => u.Username == username).FirstOrDefault();
return GetUser(username, true);
}
public override System.Web.Security.MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
throw new NotImplementedException();
}
public override string GetUserNameByEmail(string email)
{
throw new NotImplementedException();
}
public override int MaxInvalidPasswordAttempts
{
get { throw new NotImplementedException(); }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { throw new NotImplementedException(); }
}
public override int MinRequiredPasswordLength
{
get { throw new NotImplementedException(); }
}
public override int PasswordAttemptWindow
{
get { throw new NotImplementedException(); }
}
public override System.Web.Security.MembershipPasswordFormat PasswordFormat
{
get { throw new NotImplementedException(); }
}
public override string PasswordStrengthRegularExpression
{
get { throw new NotImplementedException(); }
}
public override bool RequiresQuestionAndAnswer
{
get { throw new NotImplementedException(); }
}
public override bool RequiresUniqueEmail
{
get { throw new NotImplementedException(); }
}
public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override bool UnlockUser(string userName)
{
throw new NotImplementedException();
}
public override void UpdateUser(System.Web.Security.MembershipUser user)
{
throw new NotImplementedException();
}
public override bool ValidateUser(string username, string password)
{
throw new NotImplementedException();
}
}
}
I would be very grateful for any tips anyone has in connecting the MyAppUser to the custom membership provider.

Assuming that the application authenticates users via ADFS, your role provider and membersh provider have nothing to do. Completely. The identity together with roles is passed to the application by the ADFS.
What you possibly could do is to have a custom ClaimsAuthenticationmanager. I've blogged on that some time ago:
http://netpl.blogspot.com/2012/09/sessionauthenticationmodule-and-dynamic.html

As I was already authenticated by ADFS I had gotten the username in the claims and held it in session variable. I then used this session variable to call my database user with the GetRolesForUser method that I had overriden. Works great not too much effort.

Related

Asp .net core web api Entity Framework 5.0 Lazy loading

i have the following client model that has a relation with orders and BloodType tables:
public partial class Client
{
public Client()
{
ClientOrders = new HashSet<ClientOrder>();
}
public long ClientId { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public long BloodTypeId { get; set; }
public virtual BloodType BloodType { get; set; }
public virtual ICollection<ClientOrder> ClientOrders { get; set; }
}
when i try the following code to lazy load the orders and the BloodType it's not rendering them and it return the as empty list and null value.
here's my code:
[HttpGet]
[Route("clients-orders")]
public async Task<IActionResult> GetClientsOrders()
{
try
{
var result = await _trainingContext.Clients.FromSqlRaw("Exec getClients").ToListAsync();
var clientsOrders = new List<Client>();
foreach (var client in result)
{
Client currentClient = new Client()
{
ClientId = client.ClientId,
BloodTypeId = client.BloodTypeId,
PhoneNumber = client.PhoneNumber,
ClientOrders = client.ClientOrders,
BloodType = client.BloodType
};
clientsOrders.Add(currentClient);
}
return Ok(clientsOrders);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Any solution would be very appreciated,
Thank you!

not catch DbUpdateConcurrencyException?

i set a break point at var c = context.SaveChanges() , then update sql update [Student] set Name ='456' where PriKey =1 in Sqlserver Management Studio, continue, somstimes the program can not catch DbUpdateConcurrencyException, why happen this situation?
public class OfficeContext : DbContext
{
public DbSet<Student> Students { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
#"Data Source=192.168.31.215;User ID=hj;Password=hj123;Database=office;Integrated Security=false");
optionsBuilder.LogTo(Console.WriteLine);
}
[Table("Student")]
public class Student
{
[Column("PriKey")]
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Timestamp]
[Column("VerCol")]
public virtual byte[] RowVersion { get; set; }
[Column("Name")]
public string Name { get; set; }
}
}
class Program
{
static void Main(string[] args)
{
using (var context = new OfficeContext()){
try
{
var a = context.Students.FirstOrDefault(x => x.Id == 1);
a.Name = "123";
var c = context.SaveChanges();
}
catch(DbUpdateConcurrencyException e)
{
Console.WriteLine(e.Message);
}
}
}
}

WebAPI: How to do attribute routing for Post, Put and Delete verbs

I am new in WebAPI. Somehow I manage to do attribute routing for get type request but do not know how to do it for Post, Put and Delete verbs
Please see my sample code and come with modified code where some one would show me how to do attribute routing for Post, Put and Delete verbs.
[RoutePrefix("api/customer")]
public class CustomerController : ApiController
{
static readonly ICustomerRepository repository = new CustomerRepository();
[HttpGet, Route("GetAll")]
public IEnumerable<Customer> GetAllCustomers()
{
return repository.GetAll();
}
[HttpGet, Route("GetByID/{customerID}")]
public Customer GetCustomer(string customerID)
{
Customer customer = repository.Get(customerID);
if (customer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return customer;
}
[HttpGet, Route("GetByCountryName/{country}")]
public IEnumerable<Customer> GetCustomersByCountry(string country)
{
return repository.GetAll().Where(
c => string.Equals(c.Country, country, StringComparison.OrdinalIgnoreCase));
}
public HttpResponseMessage PostCustomer(Customer customer)
{
customer = repository.Add(customer);
var response = Request.CreateResponse<Customer>(HttpStatusCode.Created, customer);
string uri = Url.Link("DefaultApi", new { customerID = customer.CustomerID });
response.Headers.Location = new Uri(uri);
return response;
}
public void PutProduct(string customerID, Customer customer)
{
customer.CustomerID = customerID;
if (!repository.Update(customer))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
public void DeleteProduct(string customerID)
{
Customer customer = repository.Get(customerID);
if (customer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.Remove(customerID);
}
}
Customer class
public class Customer
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string Address { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
}
Not testet but I think it should look like this:
[RoutePrefix("api/customer")]
public class CustomerController : ApiController
{
static readonly ICustomerRepository repository = new CustomerRepository();
[HttpGet]
[Route("")]
public IEnumerable<Customer> GetAllCustomers()
{
return repository.GetAll();
}
[HttpGet]
[Route("{customerID}")]
public Customer GetCustomer(string customerID)
{
Customer customer = repository.Get(customerID);
if (customer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return customer;
}
[HttpGet]
[Route("/GetByCountryName/{country}")]
public IEnumerable<Customer> GetCustomersByCountry(string country)
{
return repository.GetAll().Where(
c => string.Equals(c.Country, country, StringComparison.OrdinalIgnoreCase));
}
[HttpPost]
[Route("")]
public HttpResponseMessage PostCustomer([FromBody]Customer customer)
{
customer = repository.Add(customer);
var response = Request.CreateResponse<Customer>(HttpStatusCode.Created, customer);
string uri = Url.Link("DefaultApi", new { customerID = customer.CustomerID });
response.Headers.Location = new Uri(uri);
return response;
}
[HttpPut]
[Route("{customerID}")]
public void PutProduct(string customerID, [FromBody]Customer customer)
{
customer.CustomerID = customerID;
if (!repository.Update(customer))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
[HttpDelete]
[Route("{customerID}")]
public void DeleteProduct(string customerID)
{
Customer customer = repository.Get(customerID);
if (customer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.Remove(customerID);
}
}

Entity Framework one to one relation, disconnected entities

I want to work with disconnected objects. In my model a user can have or not an address.
First my model:
public class ApplicationUser : IdentityUser, IUser
{
public ApplicationUser() : base()
{
}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual DateTime BornDate { get; set; }
public virtual SubscriptionSetting SubscriptionSetting { get; set; }
public virtual Address Address { get; private set; }
public void AddAddress(Address address)
{
this.Address = address;
}
}
public class Address
{
public Address(string id, string number, string type, string nom, string postalCode, string city, GPSCoordinates gps)
{
this.Id = id;
this.Number = number;
this.Type = type;
this.Nom = nom;
this.PostalCode = postalCode;
this.City = city;
this.GPSCoordinates = gps;
}
public virtual string Id { get; set; }
public virtual string Number { get; set; }
public virtual string Type { get; set; }
public virtual string Nom { get; set; }
public virtual string PostalCode { get; set; }
public virtual string City { get; set; }
public virtual GPSCoordinates GPSCoordinates { get; set; }
}
My context:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection")
{
this.Database.Log = Console.Write;
}
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations.Add(new SubscriptionSettingConfiguration());
modelBuilder.Configurations.Add(new ApplicationUserConfiguration());
modelBuilder.Configurations.Add(new AddressConfiguration());
base.OnModelCreating(modelBuilder);
}
}
My configuration:
public class ApplicationUserConfiguration : EntityTypeConfiguration<ApplicationUser>
{
public ApplicationUserConfiguration() : base()
{
HasRequired(u => u.Address).WithRequiredPrincipal();
Property(u => u.BornDate).HasColumnType("datetime2");
}
}
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration()
{
Property(m => m.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
HasKey(m => m.Id);
}
}
And my repo:
public virtual async Task<TEntity> InsertAsync(TEntity entity)
{
DbSet.Add(entity);
try
{
await _dbContext.SaveChangesAsync();
}
catch (DbEntityValidationException ex)
{
var sb = new StringBuilder();
foreach (var failure in ex.EntityValidationErrors)
{
sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
foreach (var error in failure.ValidationErrors)
{
sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
sb.AppendLine();
}
}
throw new DbEntityValidationException(
"Entity Validation Failed - errors follow:\n" +
sb.ToString(), ex
);
}
return entity;
}
Finally my unit test :
public class adress_repository_test
{
IRepository<Address> _addressRepository;
DbContext _context;
public adress_repository_test()
{
_context = new ApplicationDbContext();
_addressRepository = new Repository<Address>(_context);
}
[Fact]
public async Task insert_new_address_success()
{
GPSCoordinates gps = new GPSCoordinates(44.5, 41.2);
Address address = new Address("d0ead995-ca31-4950-bfd3-93e0ca82e37e", "BATC", "Allée", "beethoven", "60100", "creil", gps);
var result = await _addressRepository.InsertAsync(address);
Assert.NotEmpty(address.Id);
}
When the test run it throws an exception :
Violation of PRIMARY KEY constraint 'PK_dbo.Address'. Cannot insert
duplicate key in object 'dbo.Address'. The duplicate key value is
(d0ead995-ca31-4950-bfd3-93e0ca82e37e)
Sorry for this question but i haven't becareful about the config of my test, the connexion string is not present, and it's create a new database .
Steve is right the row is present in the BDD

Generic Repository EF 5 - Update Entity And It's Complex/Scalar/Navigation Properties

I'm trying to find an easy solution for updating an entity + the included properties in my solution. I've created an Generic Repository for my DBContext (database). It does update the parent entity, but not handling changes on the child properties. Is there a way to handle or track those changes?
Example code for updating child propery: (look at comment - example code)
[HttpPut]
public HttpResponseMessage PutBrand(Brand brand)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
try
{
// example code
brand.BrandSizes.FirstOrDefault().Name = "I'm a Test";
// add values
brand.State = State.Changed;
brand.DateChanged = DateTime.Now;
// update
brand = _brandService.UpdateBrand(brand);
// save
_brandService.SaveBrandChanges();
// signalR
Hub.Clients.All.UpdateBrand(brand);
return Request.CreateResponse<Brand>(HttpStatusCode.OK, brand);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
Context:
public class ERPContext : DbContext
{
#region Catalog
public DbSet<Brand> Brands { get; set; }
public DbSet<BrandSize> BrandSizes { get; set; }
public DbSet<BrandSizeOption> BrandSizeOptions { get; set; }
public DbSet<BrandTierPrice> BrandTierPrices { get; set; }
#endregion Catalog
public ERPContext()
: base("db-erp")
{
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Generic Repository:
public class ERPRepository<T> : IRepository<T> where T : class
{
#region Fields
private DbSet<T> _dbSet;
private DbContext _dataContext;
#endregion Fields
#region Ctor
public ERPRepository(DbContext dataContext)
{
if (dataContext == null)
{
throw new ArgumentNullException("dataContext", "dataContext cannot be null");
}
_dataContext = dataContext;
_dbSet = _dataContext.Set<T>();
}
#endregion Ctor
#region Methods
public T Add(T item)
{
return _dbSet.Add(item);
}
public T Delete(T item)
{
return _dbSet.Remove(item);
}
public T Update(T item)
{
var updated = _dbSet.Attach(item);
_dataContext.Entry(item).State = EntityState.Modified;
return updated;
}
public IQueryable<T> Query(params Expression<Func<T, object>>[] includes)
{
var query = _dbSet;
if (includes != null)
{
includes.ToList().ForEach(x => query.Include(x).Load());
}
return query;
}
public void SaveChanges()
{
_dataContext.SaveChanges();
}
#endregion Methods
}
Model:
public class Brand
{
#region Ctr
public Brand()
{
BrandSizes = new List<BrandSize>();
BrandTierPrices = new List<BrandTierPrice>();
}
#endregion Ctr
#region Properties
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int? LogoId { get; set; }
public int DisplayOrder { get; set; }
public bool Deleted { get; set; }
public bool Locked { get; set; }
public State State { get; set; }
public DateTime DateChanged { get; set; }
public DateTime DateCreated { get; set; }
#endregion Properties
#region Mapping
public virtual Picture Logo { get; set; }
public virtual List<BrandSize> BrandSizes { get; set; }
public virtual List<BrandTierPrice> BrandTierPrices { get; set; }
#endregion Mapping
}
BrandService:
public partial class BrandService : IBrandService
{
#region Fields
private readonly IRepository<Brand> _brandRepository;
private readonly IRepository<BrandSize> _brandSizeRepository;
private readonly IRepository<BrandSizeOption> _brandSizeOptionRepository;
#endregion Fields
#region Ctor
public BrandService(IRepository<Brand> brandRepository, IRepository<BrandSize> brandSizeRepository, IRepository<BrandSizeOption> brandSizeOptionRepository)
{
_brandRepository = brandRepository;
_brandSizeRepository = brandSizeRepository;
_brandSizeOptionRepository = brandSizeOptionRepository;
}
#endregion Ctor
#region Methods
public virtual IEnumerable<Brand> GetAllBrands()
{
return _brandRepository.Query(x => x.BrandSizes);
//return _brandRepository.Query();
}
public virtual Brand GetBrandById(int id)
{
return _brandRepository.Query().Where(x => x.Id == id).FirstOrDefault();
}
public virtual Brand InsertBrand(Brand brand)
{
return _brandRepository.Add(brand);
}
public virtual Brand UpdateBrand(Brand brand)
{
return _brandRepository.Update(brand);
}
public virtual Brand DeleteBrand(Brand brand)
{
return _brandRepository.Delete(brand);
}
public virtual void SaveBrandChanges()
{
_brandRepository.SaveChanges();
}
#endregion Methods
}
Create IObjectWithState interface and State enum to track changes manually:
public interface IObjectWithState
{
State State { get; set; }
}
public enum State
{
Added,
Unchanged,
Modified,
Deleted
}
and implement the interface in every mapped entity
public class Brand:IObjectWithState
{ ....
[NotMapped]
public State State { get; set; }}
and add these two helper methods to convert the state and to apply the changes in the entire graph:
public static EntityState ConvertState(State state)
{
switch (state)
{
case State.Added :
return EntityState.Added;
case State.Deleted:
return EntityState.Deleted;
case State.Modified:
return EntityState.Modified;
case State.Unchanged:
return EntityState.Unchanged;
default:
return EntityState.Unchanged;
}
}
public static void ApplyStateChanges(this DbContext context)
{
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = StateHelpers.ConvertState(stateInfo.State);
}
}
and when update or insert any object edit the state of it like this object.State = State.Modified;
and then modify your insert or update method to be like this:
public void InsertOrUpdate(T entity, bool IsGraph)
{
if (((IObjectWithState)entity).State == State.Added)
{
dataContext.Entry(entity).State = System.Data.Entity.EntityState.Added;
}
else
{
dbset.Add(entity);
dataContext.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
//This method change the state of every changed object
if (IsGraph)
ApplyStateChanges(dataContext);
dataContext.Commit();
}