When user update the property Curso (type Curso), this update does not work in Entity Framework. All data in object of type Turma are updated, but property Curso. Follow bellow my code:
This is the data received by PUT method in MVC controller:
{
"Id":1,
"DataVencimento":"2017-11-24T00:00:00",
"Nome":".Net MVCss",
"Turno":"Tarde",
"Curso":
{
"Id":1,
"Nome":"teste",
"Duracao":2,
"ValorAnuidade":5888.88,
"QtdParcelas":2,
"ValorParcela":22.22,
"ValorMatricula":22.22,
"Disciplinas":null,
"CorpoDocente":null,
"Documentos":null,
"Turmas":null
}
}
This is PUT method in MVC Controller:
[HttpPut]
public HttpResponseMessage Update(TurmaDto dto)
{
var response = new HttpResponseMessage();
IKernel ninjectKernel = new StandardKernel();
ninjectKernel.Bind<ITurmaBLO>().To<TurmaBLO>();
ITurmaBLO blo = ninjectKernel.Get<ITurmaBLO>();
Turma t = Mapper.Map<TurmaDto, Turma>(dto);
if (!blo.Update(t))
{
response = Request.CreateResponse(HttpStatusCode.NotFound, "Turma não encontrada.");
}
else
{
response = Request.CreateResponse(HttpStatusCode.OK, t);
}
return response;
}
This is Class TurmaBLO:
public class TurmaBLO : GenericaBLO<Turma>, ITurmaBLO
{
private IKernel ninjectKernel = new StandardKernel();
private ITurmaDAO _dao;
public TurmaBLO()
{
ninjectKernel.Bind<ITurmaDAO>().To<TurmaDAO>();
_dao = ninjectKernel.Get<ITurmaDAO>();
}
public override bool Add(Turma e)
{
return _dao.Add(e);
}
public override bool Update(Turma e)
{
return _dao.Update(e);
}
public override List<Turma> GetAll()
{
return _dao.GetAll();
}
public override Turma Get(int id)
{
return _dao.Get(id);
}
}
This is Class GenericaBLO:
public class GenericaBLO<T> : IGenericaBLO<T> where T : class
{
public GenericaDAO<T> dao;
public virtual bool Add(T e)
{
dao = new GenericaDAO<T>();
return dao.Add(e);
}
public virtual bool Update(T e)
{
dao = new GenericaDAO<T>();
return dao.Update(e);
}
public virtual bool Delete(T e)
{
dao = new GenericaDAO<T>();
return dao.Delete(e);
}
public virtual List<T> GetAll()
{
dao = new GenericaDAO<T>();
return dao.GetAll();
}
public virtual T Get(int id)
{
dao = new GenericaDAO<T>();
return dao.Get(id);
}
public void ValidateForAdd()
{
throw new NotImplementedException();
}
public void ValidateForUpdate()
{
throw new NotImplementedException();
}
public void ValidateForDelete()
{
throw new NotImplementedException();
}
}
This is Class TurmaDAO:
internal class TurmaDAO : GenericaDAO<Turma>, ITurmaDAO
{
public override bool Add(Turma e)
{
base.Context.Curso.Attach(e.Curso);
return base.Add(e);
}
public override bool Update(Turma e)
{
base.Context.Curso.Attach(e.Curso);
return base.Update(e);
}
public override List<Turma> GetAll()
{
return base.Context.Turma.Include(c => c.Curso).Include(dt => dt.Descontos).ToList();
}
public override Turma Get(int id)
{
return base.Context.Turma.Include(c => c.Curso).Include(dt => dt.Descontos).SingleOrDefault(c => c.Id == id);
}
}
This is Class GenericaDAO:
public class GenericaDAO<T> : IGenericaDAO<T> where T : class
{
internal ApplicationDbContext Context { get; set; }
protected DbSet<T> DbSet { get; set; }
public GenericaDAO()
{
Context = new ApplicationDbContext();
DbSet = Context.Set<T>();
}
public virtual bool Add(T e)
{
try
{
Context.Entry(e).State = EntityState.Added;
Context.SaveChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public virtual bool Update(T e)
{
try
{
Context.Entry(e).State = EntityState.Modified;
Context.SaveChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public virtual bool Delete(T e)
{
try
{
Context.Entry(e).State = EntityState.Deleted;
Context.SaveChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public virtual List<T> GetAll()
{
return DbSet.ToList();
}
public virtual T Get(int id)
{
return DbSet.Find(id);
}
}
This is Class TurmaDto:
public class TurmaDto
{
public int Id { get; set; }
public DateTime DataVencimento { get; set; }
public string Nome { get; set; }
public string Turno { get; set; }
public CursoDto Curso { get; set; }
}
This is Class Turma:
public class Turma
{
public int Id { get; set; }
public DateTime DataVencimento { get; set; }
public string Nome { get; set; }
public string Turno { get; set; }
public Curso Curso { get; set; }
}
You also need to call Update on the child entities, in your case Curso. So:
ITurmaBLO bloT = ninjectKernel.Get<ITurmaBLO>();
ICursoBLO blo = ninjectKernel.Get<ICursoBLO>();
Turma t = Mapper.Map<TurmaDto, Turma>(dto);
if(!(bloT.Update(t) && bloC.Update(t.Curso))){
response = Request.CreateResponse(HttpStatusCode.NotFound, "Turma não encontrada.");
}
If you need to only update the relation from Turma to Curso without changing the Curso, it's enough to set Turma.CursoId to the Id of the new Curso, assuming the relationship is mapped correctly in EF.
Related
When a new InvoiceItem is created, its InvoiceItemId is generated. For some reason, whenever I call OnPost page handler in EditInvoiceModel, the ids of my items in InputModel get generated again.
InvoiceItem
public class InvoiceItem
{
public string InvoiceItemId { get; set; }
public InvoiceItem()
{
InvoiceItemId = GenerateID.GenerateItemID();
}
}
}
EditInvoiceModel
public class EditInvoiceModel : PageModel
{
public readonly InvoiceService _service;
[BindProperty]
public InputModel Input { get; set; }
public string InvoiceId { get; set; }
public EditInvoiceModel(InvoiceService service)
{
_service = service;
}
public async void OnGet(string id)
{
Invoice invoice = await _service.GetInvoice(id);
Input = new InputModel();
Input.Items = invoice.Items;
}
public async Task<IActionResult> OnPost(string id)
{
if(ModelState.IsValid)
{
_service.EditInvoice(Input, id);
return RedirectToPage("/ViewInvoice", new { id = id });
}
return Page();
}
}
CreateInvoiceModel
public class CreateInvoiceModel : PageModel
{
public readonly InvoiceService _service;
[BindProperty]
public InputModel Input { get; set; }
public CreateInvoiceModel(InvoiceService service)
{
_service = service;
}
public void OnGet()
{
Input = new InputModel();
Input.PopulateItems();
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
_service.AddInvoice(Input);
return RedirectToPage("/Index");
}
return Page();
}
}
My Input Model
public class InputModel
{
public List<InvoiceItem> Items { get; set; } = new List<InvoiceItem>(16);
public void PopulateItems()
{
for (int i = 0; i < Items.Capacity; i++)
{
Items.Add(new InvoiceItem());
}
}
}
EditInvoice Service
public async void EditInvoice(InputModel input, string id)
{
var invoice = await _context.Invoices.Include(x => x.Client).Include(x => x.Items).FirstAsync(x => x.InvoiceId == id);
if (invoice == null) { throw new Exception("Unable to find the invoice"); }
invoice.Items = input.Items;
invoice.Description = input.Description;
invoice.InvoiceDate = input.InvoiceDate;
invoice.PaymentTerms = input.PaymentTerms;
invoice.Client = input.Client;
invoice.BillFromAddress = input.BillFromAddress;
invoice.BillFromCity = input.BillFromCity;
invoice.BillFromCountry = input.BillFromCountry;
invoice.BillFromPostal = input.BillFromPostal;
_context.Entry(invoice).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
I am getting the below error. I am using .Net Core web API.
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type 'CustomerManager.Db.DataAccessContext' while attempting to activate 'CustomerManager.Repository.UnitOfWork'.
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, bool throwIfCallSiteNotFound)
Api Controller
[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
{
private ICustomersManager _customersManager = null;
public CustomerController(ICustomersManager customersManager)
{
_customersManager = customersManager;
}
[HttpGet]
public async Task<IActionResult> Get()
{
var customers = await _customersManager.GetAll();
return Ok(customers);
}
}
Customer Model
public class Customers
{
public Customers()
{
Customers customer = this;
customer.CustomerBankDetails = new List<CustomerBankDetail>();
customer.CustomerContactDetails = new List<CustomerContactDetail>();
customer.CustomerFamilyDetails = new List<CustomerFamilyDetail>();
customer.CustomerPhotos = new List<CustomerPhoto>();
}
public int Id { get; set; }
public string CustomerNo { get; set; }
public string CustomerName { get; set; }
public string Gender { get; set; }
public DateTime? CustomerEntryDate { get; set; }
public DateTime? DateOfBirth { get; set; }
public string Nationality { get; set; }
public bool? IsApproved { get; set; }
public bool IsActive { get; set; }
public bool? IsDeleted { get; set; }
public byte? SortedBy { get; set; }
public string Remarks { get; set; }
public virtual IEnumerable<CustomerBankDetail> CustomerBankDetails { get; set; }
public virtual IEnumerable<CustomerContactDetail> CustomerContactDetails { get; set; }
public virtual IEnumerable<CustomerFamilyDetail> CustomerFamilyDetails { get; set; }
public virtual IEnumerable<CustomerPhoto> CustomerPhotos { get; set; }
}
Customer Business Layer Code
public interface ICustomersManager
{
Task<List<Customers>> GetAll();
}
BLL Implementation
public class CustomersManager : ICustomersManager
{
private IUnitOfWork _unitOfWork = null;
public CustomersManager(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<List<Customers>> GetAll()
{
return await _unitOfWork.CustomerRepository.GetAllAsync();
}
}
Unit Of Work Interface
public interface IUnitOfWork : IDisposable
{
ICustomerRepository CustomerRepository { get; }
}
Unit of work implementation
public class UnitOfWork : IUnitOfWork
{
#region properties
private readonly DataAccessContext _context;
private ICustomerRepository _customerRepository;
public UnitOfWork(DataAccessContext context)
{
_context = context;
}
#endregion
public ICustomerRepository CustomerRepository =>
_customerRepository ?? (_customerRepository = new CustomerRepository(_context));
public void Dispose()
{
_customerRepository = null;
}
}
Customer Repository Interface
public interface ICustomerRepository : IRepository<Customers>
{
}
Customer Repository Implementation
public class CustomerRepository : BaseRepository, ICustomerRepository
{
public CustomerRepository(DataAccessContext objDataAccessContext) : base(objDataAccessContext)
{
}
public Task<List<Customers>> GetAllAsync()
{
return Task.Run(() =>
{
var objCustomerList = new List<Customers>();
ObjDbCommand.Parameters.Clear();
ObjDbCommand.AddInParameter("#Id", null);
try
{
ObjDbDataReader = ObjDataAccessContext.ExecuteReader(ObjDbCommand, "dbo.prGetAllCustomers", CommandType.StoredProcedure);
if (ObjDbDataReader.HasRows)
while (ObjDbDataReader.Read())
{
var objCustomer = new Customers();
BuildModel(ObjDbDataReader, objCustomer);
objCustomerList.Add(objCustomer);
}
}
catch (Exception ex)
{
throw new Exception("Error : " + ex.Message);
}
finally
{
if (ObjDbDataReader != null) ObjDbDataReader.Close();
ObjDataAccessContext.Dispose(ObjDbCommand);
}
return objCustomerList;
});
}
}
Generic Repository Interface
public interface IRepository<TEntity> where TEntity : class
{
Task<List<TEntity>> GetAllAsync();
}
Generic Repository Implementation
public class BaseRepository
{
protected readonly DataAccessContext ObjDataAccessContext;
protected readonly DbCommand ObjDbCommand;
protected DbDataReader ObjDbDataReader;
protected BaseRepository(DataAccessContext objDataAccessContext)
{
ObjDataAccessContext = objDataAccessContext;
ObjDbCommand = ObjDataAccessContext.GetCommand(true, IsolationLevel.ReadCommitted);
}
protected void BuildModel<T>(DbDataReader objDataReader, T item) where T : class
{
for (var inc = 0; inc < objDataReader.FieldCount; inc++)
{
var type = item.GetType();
var prop = type.GetProperty(objDataReader.GetName(inc));
var val = objDataReader.GetValue(inc) is DBNull || objDataReader.GetValue(inc).Equals(null) ||
string.IsNullOrEmpty(Convert.ToString(objDataReader.GetValue(inc)))
? null
: objDataReader.GetValue(inc);
prop?.SetValue(item, val, null);
}
}
}
Database Access Context using ADO.NET
public abstract class DataAccessContext
{
public DbCommand GetCommand(bool isTransaction, IsolationLevel isolationLevel)
{
var connectionString = DbConfiguration.ConnectionString;
return GetDbCommand(isTransaction, isolationLevel, connectionString);
}
public int ExecuteNonQuery(DbCommand objDbCommand, string textOrSpName, CommandType commandType)
{
try
{
objDbCommand.CommandType = commandType;
objDbCommand.CommandText = textOrSpName;
return objDbCommand.ExecuteNonQuery();
}
catch (DbException sqlEx)
{
throw new Exception("ExecuteNonQuery " + textOrSpName, sqlEx);
}
}
public int ExecuteNonQuery(DbCommand objDbCommand)
{
try
{
return objDbCommand.ExecuteNonQuery();
}
catch (DbException sqlEx)
{
throw new Exception("ExecuteNonQuery " + objDbCommand.CommandText, sqlEx);
}
}
public DbDataReader ExecuteReader(DbCommand objDbCommand, string textOrSpName, CommandType commandType)
{
try
{
objDbCommand.CommandType = commandType;
objDbCommand.CommandText = textOrSpName;
return objDbCommand.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (DbException sqlEx)
{
throw new Exception("ExecuteReader " + textOrSpName, sqlEx);
}
}
public DbDataReader ExecuteReader(DbCommand objDbCommand)
{
try
{
return objDbCommand.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (DbException sqlEx)
{
throw new Exception("ExecuteReader " + objDbCommand.CommandText, sqlEx);
}
}
public void Dispose(DbCommand objDbCommand)
{
if (objDbCommand.Connection != null)
{
objDbCommand.Connection.Dispose();
objDbCommand.Connection = null;
}
if (objDbCommand.Transaction != null)
{
objDbCommand.Transaction.Dispose();
objDbCommand.Transaction = null;
}
objDbCommand.Dispose();
objDbCommand = null;
}
private DbCommand GetDbCommand(bool bIsTransaction, IsolationLevel isolationLevel, string connectionString)
{
// retrieve provider invariant name from web.config
var providerInvariantName = string.Empty;
if (string.IsNullOrEmpty(providerInvariantName))
providerInvariantName = "System.Data.SqlClient";
// create the specific invariant provider
//DbProviderFactories.RegisterFactory("System.Data.SqlClient", SqlClientFactory.Instance);
var objDbProviderFactory = DbProviderFactories.GetFactory(providerInvariantName);
var objDbConnection = objDbProviderFactory.CreateConnection();
if (objDbConnection == null) return null;
objDbConnection.ConnectionString = connectionString;
var objDbCommand = objDbProviderFactory.CreateCommand();
if (objDbCommand == null) return null;
objDbCommand.Connection = objDbConnection;
objDbConnection.Open();
if (bIsTransaction)
{
var objDbTransaction = objDbConnection.BeginTransaction(isolationLevel);
objDbCommand.Transaction = objDbTransaction;
return objDbCommand;
}
return objDbCommand;
}
}
Here is the Startup.cs file
public void ConfigureServices(IServiceCollection services)
{
DbConfiguration.ServerName = Configuration["DbConnection:ServerName"];
DbConfiguration.DatabaseName = Configuration["DbConnection:DatabaseName"];
DbConfiguration.UserId = Configuration["DbConnection:UserId"];
DbConfiguration.Password = Configuration["DbConnection:Password"];
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//services.AddSingleton(typeof(DataAccessContext));
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
//Dependency Injection
//services.AddScoped(sp => sp.GetService(typeof(DataAccessContext)));
services.AddScoped<ICustomerRepository, CustomerRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<ICustomersManager, CustomersManager>();
}
How to solve this issue?
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type
'CustomerManager.Db.DataAccessContext' while attempting to activate
'CustomerManager.Repository.UnitOfWork'.
That is because you do not register DataAccessContext in Startup.cs.
For your DataAccessContext is abstract,note that you generally cannot register abstract classes because they cannot be instantiated.
Change like below:
public class DataAccessContext {}
Register like below:
services.AddScoped(typeof(DataAccessContext));
Nothing in the shown code snippets demonstrates why DataAccessContext should be an abstract class.
Make it a concrete class
public class DataAccessContext {
//...code omitted for brevity
}
Second, classes should depend on abstractions of services and not concretions. DataAccessContext should have a backing abstraction.
public interface IDataAccessContext {
DbCommand GetCommand(bool isTransaction, IsolationLevel isolationLevel);
int ExecuteNonQuery(DbCommand objDbCommand, string textOrSpName, CommandType commandType);
int ExecuteNonQuery(DbCommand objDbCommand);
DbDataReader ExecuteReader(DbCommand objDbCommand, string textOrSpName, CommandType commandType);
DbDataReader ExecuteReader(DbCommand objDbCommand);
Dispose(DbCommand objDbCommand);
//...
}
and
public class DataAccessContext: IDataAccessContext {
//...code omitted for brevity
}
Dependent classes should then explicitly depend on that abstraction
For example
public class UnitOfWork : IUnitOfWork {
private readonly IDataAccessContext _context;
private ICustomerRepository _customerRepository;
public UnitOfWork(IDataAccessContext context) {
_context = context;
}
//...omitted for brevity
And the abstraction and implementation registered with the DI container
services.AddSingleton<IDataAccessContext, DataAccessContext>();
so that it is aware of how to resolve the service while activating its dependents
services.AddScoped is bether than AddSingleton for this solve
Introduction to the Problem
First of all I tried everything to figure out this problem by no result. I am currently trying to design a repository pattern on my own for the first time.
My application is just a Blog Website and it has some components. I will directly point to the problem for you.
Problem
When I want to update a post through repository system throws an exception (Concurrency Exception) but I am sure that this type of exception occurs when you define a "[TimeStamp]" column type in Post Table. I know how to handle this exception exactly and I am sure nobody is updating the post which I am updating currently because it works on local system. I think no reason to occur this exception except a reason which I don't know and maybe you can help me at this point.
I defined the problem explicitly for you then let's go code blocks;
Components
I have this AdminController
public class AdminController : Controller
{
private IDbFactory _dbFactory;
private IUnitOfWork _unitOfWork;
private ICategoryRepository _categoryRepository;
private IPostRepository _postRepository;
private ITagRepository _tagRepository;
public ICategoryRepository categoryRepository
{
get
{
return _categoryRepository ?? (_categoryRepository = new CategoryRepository(HttpContext.GetOwinContext().Get<DbFactory>()));
}
set
{
_categoryRepository = value;
}
}
public IPostRepository postRepository
{
get
{
return _postRepository ?? (_postRepository = new PostRepository(HttpContext.GetOwinContext().Get<DbFactory>()));
}
set
{
_postRepository = value;
}
}
public ITagRepository tagRepository
{
get
{
return _tagRepository ?? (_tagRepository = new TagRepository(HttpContext.GetOwinContext().Get<DbFactory>()));
}
set
{
_tagRepository = value;
}
}
public IDbFactory dbFactory
{
get
{
return _dbFactory ?? (_dbFactory =
HttpContext.GetOwinContext().Get<DbFactory>());
}
}
public IUnitOfWork unitOfWork
{
get
{
return _unitOfWork ?? (_unitOfWork =
HttpContext.GetOwinContext().Get<UnitOfWork>());
}
}
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult UpdatePost([Bind(Include = "IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
{
if (ModelState.IsValid)
{
Post post = new Post();
post = Mapper.Map<ViewPostModel, Post>(model);
if (model.CodeText != null)
post.PostText = GetCodedPostText(model.PostText, model.CodeText);
post.CreatedDate = DateTime.Now.ToShortDateString();
post.CategoryID = model.CategoryID;
postRepository.Update(post);
unitOfWork.SaveChanges(); // Throws and exception (Concurrency Exception)
}
ViewBag.Categories = FillCategoriesForDropdownList();
return RedirectToAction("DetailPost");
}
}
I have this RepositoryBase generic class;
private IDbFactory dbFactory;
private AppDbContext context;
private ICategoryRepository _categoryRepository;
private IPostRepository _postRepository;
private ITagRepository _tagRepository;
//public ICategoryRepository categoryRepository
//{
// get
// {
// return _categoryRepository ?? (_categoryRepository = new CategoryRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>()));
// }
// set
// {
// _categoryRepository = value;
// }
//}
//public IPostRepository postRepository
//{
// get
// {
// return _postRepository ?? (_postRepository = new PostRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>()));
// }
// set
// {
// _postRepository = value;
// }
//}
//public ITagRepository tagRepository
//{
// get
// {
// return _tagRepository ?? (_tagRepository = new TagRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>()));
// }
// set
// {
// _tagRepository = value;
// }
//}
AppDbContext db
{
get
{
return context ?? (context = dbFactory.Init());
}
}
public ICategoryRepository categoryRepository
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public IPostRepository postRepository
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public ITagRepository tagRepository
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public static UnitOfWork Create()
{
return new UnitOfWork(HttpContext.Current.GetOwinContext().Get<DbFactory>());
}
public UnitOfWork(IDbFactory _dbFactory)
{
dbFactory = _dbFactory;
}
public void SaveChanges()
{
db.SaveChanges();
}
public void Dispose()
{
}
}
I have this Post Repository;
public class PostRepository : RepositoryBase<Post>, IPostRepository, IDisposable
{
public PostRepository(IDbFactory dbFactory) : base(dbFactory) { }
public static PostRepository Create()
{
return new PostRepository(HttpContext.Current.GetOwinContext().Get<DbFactory>());
}
public void Dispose()
{
}
}
I have this Database Initializer;
public class AppDbInitializer : DropCreateDatabaseAlways<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
SeedIdentity(context);
SeedTables(context);
base.Seed(context);
}
private void SeedIdentity(AppDbContext context)
{
//var userManager = HttpContext.Current.GetOwinContext().GetUserManager<AppUserManager>();
//var roleManager = HttpContext.Current.GetOwinContext().Get<AppRoleManager>();
const string name = "admin#example.com";
const string password = "SelcuK99.";
const string roleName = "Admin";
#region Old
//var role = roleManager.FindByName(roleName);
//AppRole role = null;
//if (role == null)
//{
// role = new AppRole(roleName);
// var roleresult = roleManager.Create(role);
//}
//AppUser user = null;
////var user = userManager.FindByName(name);
//if (user == null)
//{
// user = new AppUser { UserName = name, Email = name };
// var result = userManager.Create(user, password);
// result = userManager.SetLockoutEnabled(user.Id, false);
//}
//var rolesForUser = userManager.GetRoles(user.Id);
//if (!rolesForUser.Contains(role.Name))
//{
// var result = userManager.AddToRole(user.Id, role.Name);
//}
#endregion
RoleStore<AppRole> roleStore = new RoleStore<AppRole>(context);
RoleManager<AppRole> roleManager = new RoleManager<AppRole>(roleStore);
AppRole role = new AppRole
{
Name = roleName
};
roleManager.Create(role);
UserStore<AppUser> userStore = new UserStore<AppUser>(context);
AppUserManager userManager = new AppUserManager(userStore);
AppUser user = new AppUser { Email = name, UserName = name};
userManager.Create(user, password);
userManager.AddToRole(user.Id, roleName);
}
}
I have this OwinContext startup class;
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext(AppDbContext.Create);
app.CreatePerOwinContext(DbFactory.Create);
app.CreatePerOwinContext(TagRepository.Create);
app.CreatePerOwinContext(CategoryRepository.Create);
app.CreatePerOwinContext(PostRepository.Create);
app.CreatePerOwinContext(UnitOfWork.Create);
app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create);
app.CreatePerOwinContext<AppRoleManager>(AppRoleManager.Create);
app.CreatePerOwinContext<AppSignInManager>(AppSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType =
DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity =
SecurityStampValidator.OnValidateIdentity<AppUserManager, AppUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) =>
user.GenerateUserIdentityAsync(manager))
}
});
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
}
Here is AppDbContext
public class AppDbContext : IdentityDbContext<AppUser>
{
public AppDbContext() : base("AppDbContext", throwIfV1Schema: false) { }
public static AppDbContext Create()
{
return new AppDbContext();
}
public DbSet<Category> Categories { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
static AppDbContext()
{
Database.SetInitializer(new AppDbInitializer());
}
//protected override void OnModelCreating(DbModelBuilder modelBuilder)
//{
// modelBuilder.Configurations.Add(new CategoryConfiguration());
// modelBuilder.Configurations.Add(new PostConfiguration());
//}
}
I have this Post class
public class Post : BaseEntity, IAudit
{
public int CategoryID { get; set; }
public string IntroText { get; set; }
public string PostText { get; set; }
public string CodeText { get; set; }
public string Header { get; set; }
public string Author { get; set; }
public string ImagePath { get; set; }
public string CreatedDate { get; set; }
public string UpdatedDate { get; set; }
public virtual ICollection<PostTagMapping> PostTags { get; set; }
public Category Category { get; set; }
}
and finaly I have this ViewPostModel;
public class ViewPostModel : BaseEntity
{
public string PostText { get; set; }
public string IntroText { get; set; }
public string CodeText { get; set; }
public string Author { get; set; }
public string Header { get; set; }
public string ImagePath { get; set; }
public DateTime? CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
public int CategoryID { get; set; }
}
I forget to give you the DbFactory;
public class DbFactory : Disposable, IDbFactory
{
private AppDbContext context;
public static DbFactory Create()
{
return new DbFactory();
}
public AppDbContext Init()
{
int a;
if (context == null)
a = 5;
return (HttpContext.Current.GetOwinContext().Get<AppDbContext>());
}
protected override void DisposeCore()
{
if (context != null)
context.Dispose();
}
}
I give you everything to solve this issue.
Here is my assumptions and questions ##
Maybe somewhere there could be a race condition but how is that possible I am using static DbContext ?
Maybe there are two running DbContext instances but how is that possible again I am using static DbContext ?
Here is the details of the Exception
InnerException Message:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
StackTrace:
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at HybridBlog.Model.RepositoryBase`1.Update(TEntity entity) in D:\MVC_Projects\TrialProjects\HybridBlog\HybridBlog.Model\RepositoryBase.cs:line 71
at HybridBlog.Web.Controllers.AdminController.UpdatePost(ViewPostModel model) in D:\MVC_Projects\TrialProjects\HybridBlog\HybridBlog.Web\Controllers\AdminController.cs:line 153
at lambda_method(Closure , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()
BaseEntity.cs
public class BaseEntity
{
public int ID { get; set; }
}
I strongly suspect you aren't setting post.ID in your update method. You can verify this by checking the value of post.ID prior to the postRepository.Update(post); call.
I suspect you need to change:
public ActionResult UpdatePost([Bind(Include = "IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
{
to:
public ActionResult UpdatePost([Bind(Include = "ID, IntroText, PostText, CodeText, Header, Author, ImagePath, CategoryID")] ViewPostModel model)
{
I have an object A that has another object B inside. I am loading the using EntityFramework and modifying the object B but when I set the State to EntityState.Modified I have this error{"An entity object cannot be referenced by multiple instances of IEntityChangeTracker."}
This are my objects
public class Patient : IEntity, IObjectWithState
{
public virtual int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsMobileActived { get; set; }
public virtual MobilePatient MobilePatient { get; set; }
[NotMapped]
public State State { get; set; }
}
public class MobilePatient : IObjectWithState
{
public virtual int Id { get; set; }
public virtual int PatientPin { get; set; }
public virtual string SecurityAnswer { get; set; }
public virtual bool IsPinRemembered { get; set; }
public virtual PhysiologicalData PhysiologicalData { get; set; }
[NotMapped]
public State State { get; set; }
}
I have a repository and a unit of work and bascally using it I am loading the Patient object in this way.
... Patient patient = context.Patients.Find(id); ...
Then with the patient I am updating the MobilePatient
... patient.MobilePatient = NEWmobilePatient; ...
and then updating it with
PatientContext patientContext = (PatientContext)context;
patientContext.Entry(patient).State = EntityState.Modified;
My context only has the Patient dbset
public class PatientContext:BaseContext<PatientContext>, IPatientContext
{
public IDbSet<Patient> Patients { get; set; }
public void SetModified(object entity)
{
Entry(entity).State = EntityState.Modified;
}
public void SetAdd(object entity)
{
Entry(entity).State = EntityState.Added;
}
}
So I dont know what I am missing to update. When I am loading the Patient I am using the default lazy-load but how I have data in MobilePatient I am getting that object too.
Something that I think could be a good information is that I am using unit of work and repository and my application a disconnect application.
This my repository:
public class PatientRepository : IRepository<Patient>
{
private IPatientContext context=new PatientContext();
public PatientRepository(PatientContext context)
{
this.context = context;
}
public void Add(Patient patient)
{
context.SetAdd(patient);
}
public void Update(Patient patient)
{
context.SetModified(patient);
}
public void Delete(Patient entity)
{
throw new NotImplementedException();
}
public Patient FindById(int id)
{
Patient patient = context.Patients.Find(id);
return patient;
}
public IQueryable<Patient> Find(Expression<Func<Patient, bool>> predicate)
{
PatientContext patientContext = (PatientContext)context;
return patientContext.Set<Patient>().Where(predicate).AsQueryable<Patient>();
}
public IQueryable<Patient> FindAll()
{
return context.Patients;
}
}
I an this how I am using it in my services :
Patient patient = new Patient();
using (IPatientLoaderService patientLoaderService = AppManager.Instance.Database.CreatePatientLoaderService())
{
patient = patientLoaderService.LoadPatientById(patientId);
}
patient.MobilePatient =New_mobilePatient;
patient.State = State.Modified;
patient.Age = 40;
using (IPatientUpdaterService patientUpdaterService = AppManager.Instance.Database.CreatePatientUpdaterService())
{
patientUpdaterService.UpdatePatient(patient);
}
In my services I use the unit of work and the repositories
this is one of my services used in the code:
public class EntityFrameworkPatientUpdaterService: IPatientUpdaterService
{
private PatientRepository patientsRepository;
private EntityFrameworkUnitOfWork<PatientContext> unitOfWork;
public EntityFrameworkPatientUpdaterService()
{
unitOfWork = new EntityFrameworkUnitOfWork<PatientContext>();
PatientContext patientContent = new PatientContext();
patientsRepository = new PatientRepository(patientContent);
}
public void UpdatePatient(Patient patient)
{ try
{
patientsRepository.Update(patient);
unitOfWork.Commit();
}
catch (Exception e)
{
//TODO: Log the error and evoid to throw another exception-DOR
unitOfWork.Dispose();
throw new Exception("Error on EntityFrameworkPatientUpdaterService.UpdatePatient. " +
e.Message);
}
finally
{
unitOfWork.Dispose();
unitOfWork = new EntityFrameworkUnitOfWork<PatientContext>();
PatientContext patientContent = new PatientContext();
patientsRepository = new PatientRepository(patientContent);
}
}
public void Dispose()
{
unitOfWork.Dispose();
}
}
Thank you for read this post
I am going to be adding more detail on how I am currently using my service. I think the problem is that I am trying to use tghe Onion Architecture and I am missing something.
public class PatientContext:BaseContext<PatientContext>, IPatientContext
{
public IDbSet<Patient> Patients { get; set; }
public void SetModified(object entity)
{
Entry(entity).State = EntityState.Modified;
}
}
public class PatientRepository : IRepository<Patient>
{
private readonly IPatientContext context;
public PatientRepository(PatientContext context)
{
this.context = context;
}
public void Update(Patient patient)
{
context.SetModified(_patient);
}
public Patient FindById(int id)
{
Patient patient = context.Patients.Find(id);
return patient;
}
}
public class EntityFrameworkPatientUpdaterService
{
private PatientRepository patientsRepository;
private EntityFrameworkUnitOfWork<PatientContext> unitOfWork;
public EntityFrameworkPatientUpdaterService()
{
unitOfWork = new EntityFrameworkUnitOfWork<PatientContext>();
PatientContext patientContent = new PatientContext();
patientsRepository = new PatientRepository(patientContent);
}
public void UpdatePatient(Patient patient)
{ try
{
patientsRepository.Update(patient);
unitOfWork.Commit();
}
catch (Exception e)
{
//TODO: Log the error and evoid to throw another exception-DOR
unitOfWork.Dispose();
throw new Exception("Error on EntityFrameworkPatientUpdaterService.UpdatePatient. " +
e.Message);
}
finally
{
unitOfWork.Dispose();
}
}
public void Dispose()
{
unitOfWork.Dispose();
}
}
//GEtting the patient and dispose everything,
Patient patient = new Patient();
using (IPatientLoaderService patientLoaderService = AppManager.Instance.Database.CreatePatientLoaderService())
{
patient = patientLoaderService.LoadPatientById(patientId);
}
//THEN Calling my services to update
using (IPatientUpdaterService patientUpdaterService = AppManager.Instance.Database.CreatePatientUpdaterService())
{
patientUpdaterService.UpdatePatient(patient);
}
If I interpret the exception message correctly, it means that you have two contexts, and both want the entry of your entity. This is not possible, only one context at the time can deal with a data row at a time.
I'm trying to test the that the GetSystem(int id) method in SystemService.cs returns the correct value, but can't seem to figure out how to get everything to play well together. It seems that no matter what I do, GetSystem() always returns null. This is using Entity Framework 6. If I change the body of GetSystem to read _context.Systems.SingleOrDefault(s => s.Id = id), then everything works properly, but I'd really like to use Find().
What is the proper way to test this? I'm using xUnit and Moq in this example. SystemServiceTests.cs shows the code that I'm currently using that isn't working.
SystemService.cs
namespace MyProject.Services
{
public class SystemService
{
private readonly MyContext _context;
public SystemService(MyContext context)
{
_context = context;
}
public Models.System GetSystem(int id)
{
return _context.Systems.Find(id);
}
}
}
SystemServiceTests.cs
namespace MyProject.Tests.Unit
{
public class SystemServiceTests
{
[Fact]
public void GetSystemReturnsFromContext()
{
var data = new List<Models.System> {
new Models.System { Id = 1, Name = "test 1" },
new Models.System { Id = 2, Name = "test 2" }
}.AsQueryable();
var mockContext = new Mock<MyContext>();
var mockSet = new Mock<MockableDbSetWithIQueryable<Models.System>>();
mockContext.Setup(c => c.Systems).Returns(mockSet.Object);
mockSet.Setup(m => m.Provider).Returns(data.Provider);
mockSet.Setup(m => m.Expression).Returns(data.Expression);
mockSet.Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var service = new SystemService(mockContext.Object);
var system = service.GetSystem(1);
Assert.NotNull(system); // This is always null
}
}
}
MyContext.cs
namespace MyProject.Models
{
public class MyContext : DbContext
{
public MyContext()
: base("DefaultConnection")
{
}
public virtual DbSet<Models.System> Systems { get; set; }
}
}
System.cs
namespace MyProject.Models
{
public class System
{
public int Id { get; set; }
public string Name { get; set; }
}
}
MockableDbSetWithIQueryable.cs
namespace MyProject.Tests.Helpers
{
public abstract class MockableDbSetWithIQueryable<T> : DbSet<T>, IQueryable
where T : class
{
public abstract IEnumerator<T> GetEnumerator();
public abstract Expression Expression { get; }
public abstract Type ElementType { get; }
public abstract IQueryProvider Provider { get; }
}
}
PS. Some of the code for this, specifically MockableDbSetWithIQueryable was found at http://msdn.microsoft.com/en-US/data/dn314429
I was able to find the recommended way to test everything using Entity Framework 6. The resource for this recommendation is available at http://msdn.microsoft.com/en-US/data/dn314431.
In a nutshell, test classes need to be created for each bit that needs to be tested. What I ended up doing is the following:
TestDbSet.cs
public class TestDbSet<TEntity> : DbSet<TEntity>, IQueryable, IEnumerable<TEntity>
where TEntity : class
{
ObservableCollection<TEntity> _data;
IQueryable _query;
public TestDbSet()
{
_data = new ObservableCollection<TEntity>();
_query = _data.AsQueryable();
}
public override TEntity Add(TEntity item)
{
_data.Add(item);
return item;
}
public override TEntity Remove(TEntity item)
{
_data.Remove(item);
return item;
}
public override TEntity Attach(TEntity item)
{
_data.Add(item);
return item;
}
public override TEntity Create()
{
return Activator.CreateInstance<TEntity>();
}
public override TDerivedEntity Create<TDerivedEntity>()
{
return Activator.CreateInstance<TDerivedEntity>();
}
public override ObservableCollection<TEntity> Local
{
get
{
return _data;
}
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return _query.Provider; }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
return _data.GetEnumerator();
}
}
TestSystemDbSet.cs
class TestSystemDbSet : TestDbSet<Models.System>
{
public override Models.System Find(params object[] keyValues)
{
var id = (int)keyValues.Single();
return this.SingleOrDefault(s => s.Id == id);
}
}
TestContext.cs
public class TestContext: IContext
{
public TestContext()
{
this.Systems = new TestSystemDbSet();
}
public DbSet<Models.System> Systems { get; set; }
public int SaveChangesCount { get; private set; }
public int SaveChanges()
{
this.SaveChangesCount++;
return 1;
}
}
SystemServiceTests.cs
public class SystemServiceTests
{
[Fact]
public void GetSystemReturnsFromContext()
{
var context = new TestContext();
context.Systems.Add(new Models.System { Id = 1, Name = "System 1" });
context.Systems.Add(new Models.System { Id = 2, Name = "System 2" });
context.Systems.Add(new Models.System { Id = 3, Name = "System 3" });
var service = new SystemService(context);
var system = service.GetSystem(2);
Assert.NotNull(system);
Assert.Equal(2, system.Id);
Assert.Equal("System 2", system.Name);
}
}
SystemService.cs
public class SystemService : ISystemService
{
private readonly IContext _context;
public SystemService(IContext context)
{
_context = context;
}
public Models.System AddSystem(Models.System system)
{
var s = _context.Systems.Add(system);
_context.SaveChanges();
return s;
}
public Models.System GetSystem(int id)
{
return _context.Systems.Find(id);
}
}
ISystemService.cs
public interface ISystemService
{
Models.System AddSystem(Models.System system);
Models.System GetSystem(int id);
}
.Find() is returning null because that is the default value for System. The collection does not contain an item with id id.
.Find() is a method of List.
I suggest you use LINQ's FirstOrDefault()
The reason being, you can use lazy loading by returning an IQueryable