Question
We are currently having a WPF application architecture issue. This applies to managing the EntityFramework context, its instance is once and is used throughout the life of the application. Thus, we ended the cache problem, entities do not update when they are loaded once. When using the application, our objects are outdated.
Data sheet
- Wpf Project
- . Net Framework 4 Client Profile
- MEF (Enable in Framework 4.0 System.ComponentModel.Composition)
- MVVM Design Pattern
- Multi-user application
Architecture
This is a diagram of the current architecture.

Service level
- Business rule call management (business layer)
- Save context (via UnitOfWork) after executing business rules.
- Only ViewModel can be called.
Business level
- Definition of business rules
- It can only be called by the level of service.
Repository level
- Execute methods that change context data (insert, update, delete)
- Inherit ReadOnlyRepository
- Can only be called up by the business layer.
ReadOnlyRepository Level
- Execute method returning data (select)
- It can be called everywhere (ViewModel, Service level, Business level).
UnitOfWork
- Manage contextual settings
- Save context
- Context is available only to repositories.
The code
ViewModel
[Export(typeof(OrderViewModel))] [PartCreationPolicy(CreationPolicy.NonShared)] public class OrderViewModel : ViewModelBase { private readonly IOrderManagementService _orderManagementService; private readonly IOrderReadOnlyRepository _orderReadOnlyRepository; [ImportingConstructor] public OrderViewModel(IOrderManagementService orderManagementService, IOrderReadOnlyRepository orderReadOnlyRepository) { _orderManagementService = orderManagementService; _orderReadOnlyRepository = orderReadOnlyRepository; } }
Service level
public class OrderManagementService : IOrderManagementService { private readonly IUnitOfWork _unitOfWork; private readonly IOrderManagementBusiness _orderManagementBusiness; [ImportingConstructor] public OrderManagementService (IUnitOfWork unitOfWork, IOrderManagementBusiness orderManagementBusiness) { _unitOfWork= unitOfWork; _orderManagementBusiness = orderManagementBusiness; } }
Business level
public class OrderManagementBusiness : IOrderManagementBusiness { private readonly IOrderReadOnlyRepository _orderReadOnlyRepository; [ImportingConstructor] public OrderManagementBusiness (IOrderReadOnlyRepository orderReadOnlyRepository) { _orderReadOnlyRepository = orderReadOnlyRepository; } }
ReadOnlyRepository Level
public class OrderReadOnlyRepository : ReadOnlyRepositoryBase<DataModelContainer, Order>, IOrderReadOnlyRepository { [ImportingConstructor] public OrderReadOnlyRepository (IUnitOfWork uow) : base(uow) { } }
ReadOnlyRepositoryBase
public abstract class ReadOnlyRepositoryBase<TContext, TEntity> : IReadOnlyRepository<TEntity> where TEntity : class, IEntity where TContext : DbContext { protected readonly TContext _context; protected ReadOnlyRepositoryBase(IUnitOfWork uow) { _context = uow.Context; } protected DbSet<TEntity> DbSet { get { return _context.Set<TEntity>(); } public virtual IEnumerable<TEntity> GetAll(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "") { IQueryable<TEntity> query = DbSet.AsNoTracking(); if (filter != null) { query = query.Where(filter); } foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { query = query.Include(includeProperty); } if (orderBy != null) { return orderBy(query).ToList(); } return query.ToList(); } public virtual IQueryable<TEntity> All() { return DbSet.AsNoTracking(); } public virtual IQueryable<TEntity> AllWhere(Expression<Func<TEntity, bool>> predicate) { return DbSet.Where(predicate).AsNoTracking(); } public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate) { return DbSet.Where(predicate).AsNoTracking().FirstOrDefault(); } public virtual TEntity GetById(int id) { TEntity find = DbSet.Find(id); _context.Entry(find).State = System.Data.EntityState.Detached; return DbSet.Find(id); }
We see that the context is provided to the repository in the constructor. Selection methods use the "AsNoTracking ()" method to not cache objects. This is a temporary solution that is obviously not practical in the long run.
UnitOfWork
public class UnitOfWork : IUnitOfWork { private DataModelContainer _context; public UnitOfWork() : this(new DataModelContainer()) { } public UnitOfWork(DataModelContainer context) { _context = context; } public DataModelContainer Context { get { return _context; } } public int Save() { return _context.SaveChanges(); } }
During the first composition of the service with MEF, UnitOfWork will be created using the default constructor, which creates an instance of the context.
Notes
Some code fragments were omitted for readability.
Goal of achievement
Context lifetime is clearly a problem. Knowing that all calls to the same service method must have the same context.
How can we consider changing the architecture to avoid having a single context?
Feel free to ask questions! If necessary, I can attach a test project that emphasizes the problem.