In my wpf app I need to be able to read/write data from specific table/tables, so, as I'm using SimpleInjector
to use all services and stuff.
So, basically I want to be able to use repository in some of my services.
First I found this How to use Simple injector, Repository and Context - code first
And decided to do something like that:
Entry point:
public partial class App : Application
{
public static Container appContainer;
private void AppStartup(object sender, StartupEventArgs e)
{
}
private void ConfigureContainer()
{
appContainer = new Container();
appContainer.Options.DefaultScopedLifestyle = new ThreadScopedLifestyle();
appContainer.RegisterSingleton<INLogger, LoggerNlog>();
//some of services registration
appContainer.Register<DbContext>(() =>
{
string connectionString = System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"].ToString();
return new ExcelSlicerContext(connectionString);
}, Lifestyle.Scoped);
appContainer.Register(typeof(IRepository<>), typeof(BaseRepository<>), Lifestyle.Scoped);
appContainer.Register<IMainAppSerivce, SlicerMainService>();
appContainer.Register<MainViewModel>();
appContainer.Verify();
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ConfigureContainer();
using (ThreadScopedLifestyle.BeginScope(appContainer))
{
var viewModel = appContainer.GetInstance<MainViewModel>();
var mainWindow = new MainWindow();
mainWindow.DataContext = viewModel;
mainWindow.Show();
}
}
}
Example of service:
public class SlicerMainService : IMainAppSerivce
{
private INLogger _nLogger;
// ...
private IRepository<FactModel> _dataFactRepository;
public SlicerMainService(INLogger nLogger, IRepository<FactModel> dataFactRepository)
{
_nLogger = nLogger;
//...
_dataFactRepository = dataFactRepository;
}
//...
public void TestWorkWithDb(int[] sheetIndexList)
{
var t = _dataFactRepository.Get(x => x.Row > 6).ToList();
}
}
So, in this example, when it tries to get some data it simple says that DbContext is disposed
.
I know it is because of in need to use scope like using (ThreadScopedLifestyle.BeginScope(myContainer))
but is it good practice to pass container to some services? I doubt it.
Whats the way of properly inject IRepository<T>
here?
Code for repositories & dbcontext:
public class ExcelSlicerContext: DbContext
{
public DbSet<FactModel> FactData { get; set; }
public ExcelSlicerContext(string connectionString) : base(connectionString)
{
Database.SetInitializer(
new CreateDatabaseIfNotExists<ExcelSlicerContext>()
);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<FactModel>().ToTable("fact");
}
}
Repository:
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
DbContext _context;
DbSet<TEntity> _dbSet;
public BaseRepository(DbContext context)
{
_context = context;
_dbSet = context.Set<TEntity>();
}
public IEnumerable<TEntity> Get()
{
return _dbSet.AsNoTracking().ToList();
}
public IEnumerable<TEntity> Get(Func<TEntity, bool> predicate)
{
return _dbSet.AsNoTracking().Where(predicate).ToList();
}
public TEntity GetFirst(Func<TEntity, bool> predicate)
{
return _dbSet.AsNoTracking().FirstOrDefault(predicate);
}
public TEntity FindById(int id)
{
return _dbSet.Find(id);
}
public void Create(TEntity item)
{
_dbSet.Add(item);
_context.SaveChanges();
}
public void CreateFromRange(IEnumerable<TEntity> itemList)
{
_dbSet.AddRange(itemList);
_context.SaveChanges();
}
public void Update(TEntity item)
{
_context.Entry(item).State = EntityState.Modified;
_context.SaveChanges();
}
public void Remove(TEntity item)
{
_dbSet.Remove(item);
_context.SaveChanges();
}
public void RemoveWhere(Func<TEntity, bool> predicate)
{
_dbSet.RemoveRange(_dbSet.Where(predicate).ToList());
_context.SaveChanges();
}
public void Add(TEntity item)
{
_dbSet.Add(item);
}
public void AddRange(IEnumerable<TEntity> itemList)
{
_dbSet.AddRange(itemList);
}
public void Save(bool recreateContext)
{
if (recreateContext)
{
RecreateContext();
}
_context.SaveChanges();
}
private void RecreateContext()
{
var constring = _context.Database.Connection.ConnectionString;
_context.Dispose();
_context = new ExcelSlicerContext(constring);
_context.Configuration.AutoDetectChangesEnabled = false;
}
}
Interface for repository:
public interface IRepository<TEntity> where TEntity : class
{
void Create(TEntity item);
void CreateFromRange(IEnumerable<TEntity> itemList);
void Add(TEntity item);
void AddRange(IEnumerable<TEntity> itemList);
void Save(bool recreateContext);
TEntity FindById(int id);
IEnumerable<TEntity> Get();
IEnumerable<TEntity> Get(Func<TEntity, bool> predicate);
TEntity GetFirst(Func<TEntity, bool> predicate);
void Remove(TEntity item);
void RemoveWhere(Func<TEntity, bool> predicate);
void Update(TEntity item);
}
UPD
After I read the answer, which linked as duplicate I implemented things as it was implemeneted in the example, e.g.:
public class ThreadScopedCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly Func<ICommandHandler<TCommand>> decorateeFactory;
private readonly Container container;
public ThreadScopedCommandHandlerDecorator(Func<ICommandHandler<TCommand>> decorateeFactory, Container container)
{
this.decorateeFactory = decorateeFactory;
this.container = container;
}
public void Handle(TCommand command)
{
using (ThreadScopedLifestyle.BeginScope(container))
{
var handler = decorateeFactory.Invoke();
handler.Handle(command);
}
}
}
Saving changes handler:
public class SaveChangesCommandHandlerDecorator<TCommand>: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> decoratee;
private DbContext db;
public SaveChangesCommandHandlerDecorator(ICommandHandler<TCommand> decoratee, DbContext db)
{
this.decoratee = decoratee;
this.db = db;
}
public void Handle(TCommand command)
{
this.decoratee.Handle(command);
this.db.SaveChanges();
}
}
as decorators and, as example, such a command:
public class GetFactModelCommandHandler : ICommandHandler<GetFactModelCommand>
{
private readonly IRepository<FactModel> _factRepository;
public GetFactModelCommandHandler(IRepository<FactModel> factRepository)
{
_factRepository = factRepository;
}
public void Handle(GetFactModelCommand command)
{
var data = _factRepository.Get(x => x.DocGuid == command.DocGuid);
}
}
But then I realized, that on every new action for every different entity I need to implement commands and command handlers!
It dozens and dozens of classes!
And also, how I could get results from command (I know I can make something like ICommandHandler<Command, TResult>
and have filed there for store the result but... its not like Command Pattern
should be implemented AFAIK
Is there any other way?
Just implement some IService
for particular entity?