Database context life time vs Realtime communications - c#

According to Microsoft
the database context lifetime should be very short and any instantiated
objects should be disabled and removed from the ram as soon as they are no longer needed.
While I was developing an POS WPF application ,which based on caliburn.Micro framework and IOC container was implemented, I separated the project as following:
the entities classes & the context were putted in separated Class
Library project.
I implemented the repository and unit of work
design patterns in the main wpf project and i injected the context in
the unit of work class constructor.
I registered the context and the
unit of work into the ioc container "Autofac Container" with
InstancePerDependency life time for both of them which means a new instance will be created whenever an object of each on of them is
requested.
Finally ,i injected the unit of work interface into the ViewModel classes so i can load the proper entities for each VM in its constructor.
and the problems came here.
When I was trying to add a Product' Category and then I navigate to the Products VM to add a new product under this category ,but the VM didn't recognize that I had added new category.
when I traced the code I found that it happens with any VM. The VMs weren't feeling the changes except when I restart the program. I tried to fix it in many ways but no thing worked with me ,wo i decided to change the lifetime of the context to be "SingleInstance" which means one instance of the context will be created for whole the project.
I know it's a bad practice but there weren't any other choices.
The Questions are:
why did it happen?
what was the proper solution at this situation ?
does it also happen in the other workloads like mvc ,web apis or the Scoped lifetime handles it in the dot net core services container ?
was my separation structure correct and does it work fine for other projects or not?
Update:
This is my product entity:
public class Product
{
public Product()
{
ProductBarcodes = new HashSet<ProductBarcode>();
InventoriesProducts = new HashSet<InventoriesProducts>();
ProductUnits = new HashSet<ProductUnits>();
ProductsVariants = new HashSet<ProductsVariants>();
}
[Key]
public string Productkey { get; set; }
[Required,MaxLength(150), MinLength(3)]
public string ProductName { get; set; }
[MaxLength(500)]
public string Description { get; set; }
public string ImagePath { get; set; }
public decimal? SalesPrice { get; set; }
public decimal? Cost { get; set; }
public bool CanBeSold { get; set; }
public bool CanBePurchased { get; set; }
public ProductType ProductType { get; set; }
public long CategoryId { get; set; }
public virtual ProductCategory Category { get; set; }
[InverseProperty(nameof(ProductBarcode.Product))]
public virtual ICollection<ProductBarcode> ProductBarcodes { get; set; }
public virtual ICollection<InventoriesProducts> InventoriesProducts { get; set; }
public virtual ICollection<ProductUnits> ProductUnits { get; set; }
public virtual ICollection<ProductsVariants> ProductsVariants { get; set; }
}
This is my Category entity :
public class ProductCategory
{
[Key]
public long CategoryId { get; set; }
[Required, MaxLength(150), MinLength(3)]
public string Title { get; set; }
public IList<ProductCategory> SubCategories { get; set; }
public ProductCategory Parent { get; set; }
public IList<Product> Products { get; set; }
}
This project supports english and arabic languages so,i implemented the results messages as following:
I added the ITransactionResult generic interface that describe how any transaction result should be and it's generic to give me the ability to pass the entity type that i'm going to give transaction results about.
The interface has a UserMessageResource property of type Type this property usually was setted to Properties.Resources to search for a registered resource which contains the arabic or english message that will be displayed after the transaction.
Then i added two classes to implement this interface to describe two cases , when the Transaction ends successfully which was presented in SucessTransaction class and other wises were presented in FailedTransaction class.
ITransactionResult :
public interface ITransactionResult<T> where T : class
{
Exception ExceptionHappend { get; }
string UserMessage { get; }
string SourceType { get; }
bool Success { get; }
Type UserMessageResource { get; }
string UserMessageName { get; }
string[] Notes { get; }
}
SucessTransaction:
public class SuccessTransaction<T> : ITransactionResult<T> where T : class
{
public SuccessTransaction(string message,params string[] notes)
{
Success = true;
ExceptionHappend = null;
Notes = notes;
UserMessage = message;
}
public SuccessTransaction(Type UserMessageResource,string UserMessageName, params string[] notes):this(string.Empty,notes)
{
this.UserMessageResource = UserMessageResource;
this.UserMessageName = UserMessageName;
UserMessage = UserMessageResource.GetProperty(UserMessageName).GetValue(UserMessageResource).ToString();
}
public Exception ExceptionHappend { get; }
public string UserMessage { get; }
public bool Success {get;}
public string SourceType { get => nameof(T); }
public Type UserMessageResource { get; }
public string UserMessageName { get; }
public string[] Notes { get; }
}
FailedTransaction :
public class FailedTransaction<T> : ITransactionResult<T> where T : class
{
public FailedTransaction(string message,Exception exception,params string[] notes)
{
Success = false;
UserMessage = message;
ExceptionHappend = exception;
Notes = notes;
}
public FailedTransaction(Type UserMessageResource, string UserMessageName,Exception exception, params string[] notes)
:this(string.Empty,exception,notes)
{
this.UserMessageResource = UserMessageResource;
this.UserMessageName = UserMessageName;
UserMessage = UserMessageResource.GetProperty(UserMessageName).GetValue(UserMessageResource).ToString();
}
public Exception ExceptionHappend { get; }
public string UserMessage { get; }
public bool Success { get; }
public string SourceType { get => nameof(T); }
public Type UserMessageResource { get; }
public string UserMessageName { get; }
public string[] Notes { get; }
}
My Product repo :
public class ProductRepo : IProductRepo
{
private DbContext dbContext;
public Type DisplayResourceType { get; set; }
public ProductRepo(DbContext dbContext)
{
this.dbContext = dbContext;
}
public Product Add(Product entity, out ITransactionResult<Product> transactionResult)
{
try
{
var res = dbContext.Add(entity);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "Adding_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "Adding_F", ex);
return entity;
}
}
public IEnumerable<Product> AddRange(IEnumerable<Product> entites, out ITransactionResult<Product> transactionResult)
{
try
{
dbContext.AddRange(entites);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "AddingRang_S");
return entites;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "AddingRange_F", ex);
return entites;
}
}
public IEnumerable<Product> GetAll() => GetAllAsQueryable().ToList();
public IQueryable<Product> GetAllAsQueryable() => dbContext.Set<Product>();
public Product GetById(string id) => dbContext.Find<Product>(id);
public bool IsExist(string id, out Product entity)
{
entity = GetById(id);
return entity != null ? true : false;
}
public void Remove(Product entity, out ITransactionResult<Product> transactionResult)
{
try
{
var res = dbContext.Remove(entity);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "Removing_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "Removing_F", ex);
}
}
public void RemoveRange(IEnumerable<Product> entities, out ITransactionResult<Product> transactionResult)
{
try
{
dbContext.AddRange(entities);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "RemovingRange_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "RemovingRange_F", ex);
}
}
public Product Update(Product entity, out ITransactionResult<Product> transactionResult)
{
try
{
var res = dbContext.Update(entity);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "Updating_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "Updating_F", ex);
return entity;
}
}
public IEnumerable<Product> UpdateRange(IEnumerable<Product> entities, out ITransactionResult<Product> transactionResult)
{
try
{
dbContext.UpdateRange(entities);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "UpdatingRange_S");
return entities;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "UpdatingRange_F", ex);
return entities;
}
}
}
Category repo :
public class CategoryRepo : ICategoryRepo
{
private DbContext dbContext;
public Type DisplayResourceType { get; set; }
public CategoryRepo(DbContext dbContext)
{
this.dbContext = dbContext;
}
public IEnumerable<ProductCategory> GetAll() => GetAllAsQueryable().ToList();
public IQueryable<ProductCategory> GetAllAsQueryable() => dbContext.Set<ProductCategory>();
public ProductCategory GetById(long id) => dbContext.Find<ProductCategory>(id);
public ProductCategory Add(ProductCategory entity, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
var res = dbContext.Add(entity);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "Adding_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "Adding_F", ex);
return entity;
}
}
public IEnumerable<ProductCategory> AddRange(IEnumerable<ProductCategory> entites, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
dbContext.AddRange(entites);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "AddingRang_S");
return entites;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "AddingRange_F", ex);
return entites;
}
}
public ProductCategory Update(ProductCategory entity, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
var res = dbContext.Update(entity);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "Updating_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "Updating_F", ex);
return entity;
}
}
public IEnumerable<ProductCategory> UpdateRange(IEnumerable<ProductCategory> entities, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
dbContext.UpdateRange(entities);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "UpdatingRange_S");
return entities;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "UpdatingRange_F", ex);
return entities;
}
}
public void Remove(ProductCategory entity, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
var res = dbContext.Remove(entity);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "Removing_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "Removing_F", ex);
}
}
public void RemoveRange(IEnumerable<ProductCategory> entities, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
dbContext.AddRange(entities);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "RemovingRange_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "RemovingRange_F", ex);
}
}
public bool IsExist(long id, out ProductCategory entity)
{
entity = GetById(id);
return entity != null ? true : false;
}
}

Related

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.

Insert object EF not working

i want insert object to sql server data base, i using EF 6.x. But when i execute Create method, this success and not show any error but in a database is empty :/.
public class ProductoEntity
{
public int ID { get; set; }
public string Descripcion { get; set; }
public string Categoria { get; set; }
public int Precio { get; set; }
public Producto toSql()
{
var sqlEntity = new Producto();
sqlEntity.Descripcion = this.Descripcion;
sqlEntity.Categoria = this.Categoria;
sqlEntity.Precio = this.Precio;
return sqlEntity;
}
public bool Create()
{
bool result = false;
try
{
StoreEntities context = new StoreEntities();
context.Producto.Add(toSql()); ;
result = true;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return result;
}
}
You need to call SaveChanges
StoreEntities context = new StoreEntities();
context.Producto.Add(toSql());
context.SaveChanges();

Entity Framework not updating entity when using Task.Factory

I have Entity Framework code which is supposed to be updating an entity called Asset. For some reason, it's not updating as it should. Debugging to see the root cause of this issue is difficult as I don't see any exceptions.
The problem is in the repository class where it always returns an
Asset could not be updated
message even if I pass in a valid Book or Asset object.
Model classes
public class Asset
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Book : Asset
{
public string Isbn { get; set; }
public string Edition { get; set; }
public string Publisher { get; set; }
public virtual User User { get; set; }
}
Controller class
[System.Web.Http.Authorize]
public async Task<IHttpActionResult> Put([FromBody] BookViewModel model)
{
string result = null;
try
{
if (model.IsEmpty)
return BadRequest(ModelState);
result = await _assetAssetRepository.Update(Mapper.Map<Book>(model));
return Content(HttpStatusCode.Created, result);
}
catch (Exception ex)
{
return Content(HttpStatusCode.InternalServerError, result);
}
}
Repository class:
public class AssetRepository
{
private readonly GenAppContext _context = new GenAppContext();
public Task<string> Update(Asset asset)
{
try
{
var updateTask = Task<string>.Factory.StartNew(() =>
{
if (!(asset is Book))
return "Please return correct type of asset";
var _asset = _context.Assets.SingleOrDefault(x => x.Id == asset.Id);
if (_asset == null)
throw new ArgumentNullException(nameof(_asset));
_asset.Name = asset.Name;
((Book) _asset).Edition = ((Book) asset).Edition;
((Book) _asset).Publisher = ((Book) asset).Publisher;
((Book) _asset).Isbn = ((Book) asset).Isbn;
_context.Assets.AddOrUpdate(_asset);
return _context.SaveChanges() > 0
? "Asset has been updated successfully"
: "Asset could not be updated";
});
return updateTask;
}
catch (Exception ex)
{
return Task<string>.Factory.StartNew(() => ex.Message + " " + ex.StackTrace);
}
}
}

Entity framwork 6 Code First and optional foreign key issue

I'm currently working on a small application with WPF, EF6 and SqlServer 2012. I have two entities "Region" and "BctGouvernorats" associated with an optional one to many relationship.
My problem is : When I remove a child (BctGouvernorat) from the relationship , it still appears in the collection related to the parent (Region). here's the code:
//Entities
public partial class BctGouvernorat
{
public long GovId { get; set; }
public string Libelle { get; set; }
public long UserId { get; set; }
public Nullable<long> RegionId { get; set; }
public virtual Region Region { get; set; }
}
public partial class Region
{
public long RegionId { get; set; }
public string Libelle { get; set; }
public long GroupeNumber { get; set; }
public byte Bonus { get; set; }
public long UserId { get; set; }
public virtual RegionsGroupes GroupeRegions { get; set; }
public virtual ICollection<BctGouvernorat> Gouvernorats { get; set; }
public Region()
{
Libelle = "New region";
GroupeNumber = 0;
this. Gouvernorats = new HashSet<BctGouvernorat>() ;
}
//Mapping of BctGouvernorat entity
public BctGouvernoratMapping()
{
this.ToTable("BctGouvernorat");
this.HasKey(t => t.GovId);
this.Property(t => t.GovId);
this.HasOptional(t => t.Region)
.WithMany(t => t.Gouvernorats)
.HasForeignKey(d => d.RegionId)
.WillCascadeOnDelete(false);
}
//Mapping of Region entity
public RegionMapping()
{
this.ToTable("Region");
this.HasKey(t => t.RegionId);
this.Property(t => t.RegionId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
//C# code for "Modify" Method
public void Modify(Region r, List<BctGouvernorat> _ToUnlink, List<BctGouvernorat> _ToLink)
{
//Below the code for unlink child from parent
if (_ToUnlink.Count > 0)
{
r.Gouvernorats.ToList().All(xx =>
{
if (_ToUnlink.Contains(xx))
{
xx.RegionId = null;
xx.Region = null;
}
return true;
}
);
}
//Here the code for link child to the parent
_ToLink.All(xx =>
{
xx.RegionId = r.RegionId;
xx.Region = r;
r.Gouvernorats.Add(xx);
return true;
});
//Mark Childs collection as modified
r.Gouvernorats.All(xx =>
{
_uow.GetEntry<BctGouvernorat>(xx).State = EntityState.Modified;
return true;
});
base.Modify(r);
}
actually the previous method is included in a «RegionRepository» which inherits from a base class Repository. The code of base.Modify() is as follows :
//Method Modify from RegionRepository
public void Modify(T item)
{
_uow.RegisterChanged(item);
_uow.Commit();
}
And Modify Method uses services of a unit of work "_uow" that save data to sqlserver database. Here the code :
//***************************
//_uow is a unit of work
//*****************************
public void RegisterChanged<T>(T item) where T : class
{
base.Entry<T>(item).State = System.Data.Entity.EntityState.Modified;
}
public void Commit()
{
try
{
base.SaveChanges();
}
catch (DbUpdateException e)
{
var innerEx = e.InnerException;
while (innerEx.InnerException != null)
innerEx = innerEx.InnerException;
throw new Exception(innerEx.Message);
}
catch (DbEntityValidationException e)
{
var sb = new StringBuilder();
foreach (var entry in e.EntityValidationErrors)
{
foreach (var error in entry.ValidationErrors)
{
sb.AppendLine(string.Format("{0}-{1}-{2}",
entry.Entry.Entity,
error.PropertyName,
error.ErrorMessage
));
}
}
throw new Exception(sb.ToString());
}
}
Sorry, I should have put the ViewModel code that calls the previous code :
private void SaveRegion()
{
List<BctGouvernorat> _GovToLink = null;
//The following method checks and returns (Added, Deleted, Modified BctGouvernorat)
List<BctGouvernorat> _GovToUnlink = CheckGouvernoratsListStatus(out _GovToLink);
ILogger _currentLog = (Application.Current as App).GetCurrentLogger();
using (UnitOfWork cx = new UnitOfWork(_currentLog))
{
RegionRepository _regionRepository = new RegionRepository(cx, _currentLog);
IRegionManagementService rms = new RegionManagementService(_currentLog, _regionRepository);
if (CurrentRegion.RegionId == 0)
{
CurrentRegion.UserId = Session.GetConnectedUser().UserId;
rms.AddRegion(CurrentRegion);
}
else
rms.ModifyRegion(CurrentRegion, _GovToUnlink,_GovToLink);
}
}
private List<BctGouvernorat> CheckGouvernoratsListStatus(out List<BctGouvernorat> _ToLink)
{
List<BctGouvernorat> AddedGouvernorats = GouvernoratsRegion.Except<BctGouvernorat>(CurrentRegion.Gouvernorats,
new GouvernoratComparer()).ToList();
_ToLink = AddedGouvernorats;
List<BctGouvernorat> DeletedGouvernorats = CurrentRegion.Gouvernorats.Except<BctGouvernorat>(GouvernoratsRegion,
new GouvernoratComparer()).ToList();
return DeletedGouvernorats;
}
The "GouvernoratsRegion" is an observablecollection bound to a datagrid that i edit to add or remove BCTgouvernorat Rows to the region
public void ModifyRegion(Region r, List<BctGouvernorat> _ToUnlik, List<BctGouvernorat> _ToLink)
{
_regionRepository.Modify(r, _ToUnlik, _ToLink);
}

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