ConcurrencyExeption on ASP.NET MVC Application - c#

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)
{

Related

Why a new value of this property get assigned after the POST request?

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

InvalidOperationException: Unable to resolve service for type 'DataAccessContext' while attempting to activate 'UnitOfWork'

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

EF .Net Core reach class from Controller

I have .net core project and wanted to connect db with ef. Below code is my db context.
public class YTContext:DbContext
{
public YTContext(DbContextOptions<YTContext> options) : base(options) { }
//protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//{
// optionsBuilder.UseMySql(#"Server=localhost;Database=kariyer_portal;Uid=root;Pwd=1234;");
//}
public DbSet<Student> Students { get; set; }
public DbSet<University> Universities { get; set; }
public DbSet<Bolum> Bolums { get; set; }
public DbSet<Admin> Admins { get; set; }
public DbSet<Announcement> Announcements { get; set; }
}
In Startup.class I wrote conntection.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ITest, MyConfig>();
services.AddMvc();
services.AddMvc().AddJsonOptions( options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore );
services.AddDbContext<YTContext>(options => options.UseMySql(Configuration.GetConnectionString("MySqlCon")));
}
In my repository class I wrote below .
public class StudentRepository
{
private readonly YTContext _db;
public StudentRepository(YTContext context)
{
_db = context;
}
public List<Student> GetStudents(int page, int limit = 8, string query = null)
{
try
{
var skip = (page - 1) * limit;
var students = _db.Students.Where(x => query == null || x.Name.Contains(query) || x.Surname.Contains(query) || x.University.Name.Contains(query) || x.Bolum.Name.Contains(query))
.Include(x=>x.Bolum).Include(x=>x.University)
.Skip(skip).Take(limit).ToList();
return students;
}
catch (Exception ex)
{
return null;
}
}
}
My problem is that I cant reach this class from controller below code.
public class StudentController : Controller
{
public StudentRepository repo;
[HttpGet]
public IActionResult List()
{
var students = repo.GetStudents(1, 6,null);
return View(students);
}
[HttpPost]
public IActionResult Paginate(Paginate paginate)
{
var students = repo.GetStudents(paginate.page, paginate.limit,paginate.query);
return Json(new {status = 200, student = students});
}
}
public StudentRepository repo;
The code above return null in controller. How can I reach this class from controller?
You need to register it in the DI-Container in Startup:
services.AddScoped<StudentRepository, StudentRepository>();
And then inject it to your controller's constructor:
public StudentController(StudentRepository studentRepo) {
repo = studentRepo;
}

Relationship doesn't updated in Entity Framework

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.

Cannot update a complex property inside my object with Entity Framework

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.

Categories