带有 WPF MVVM Caliburn Micro 的 URF
URF with WPF MVVM Caliburn Micro
我正在使用此框架:URF and Caliburn Micro 创建业务 WPF 应用程序。
这是 CM 引导程序的代码:
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer container;
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
container = new SimpleContainer();
container.Singleton<IWindowManager, WindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ShellViewModel>();
container.AllTypesOf<ITabItem>(Assembly.GetExecutingAssembly());
container.PerRequest<IDataContextAsync, AuraContext>();
container.PerRequest<IUnitOfWorkAsync, UnitOfWork>();
container.PerRequest<IRepositoryAsync<Audit>, Repository<Audit>>();
container.PerRequest<IAuditService, AuditService>();
}
protected override object GetInstance(Type service, string key)
{
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException(String.Format("Could not locate any instances of type {0}", service.Name));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
}
ShellViewModel.cs代码:
public class ShellViewModel: Conductor<ITabItem>.Collection.OneActive, IShell
{
private readonly IWindowManager _windowManager;
[ImportingConstructor]
public ShellViewModel(IWindowManager windowManager, IEnumerable<ITabItem> tabItems)
{
DisplayName = "Aura";
_windowManager = windowManager;
Items.AddRange(tabItems.Where(t => t.IsEnabled).OrderBy(t => t.DisplayOrder));
}
}
ShellView.xaml 标记:
<UserControl x:Class="Aura.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Aura"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600" MinWidth="800" MinHeight="600">
<TabControl x:Name="Items" Margin="3">
</UserControl>
这是一些存储库:
public static class AuditRepository
{
public static async Task<Audit> GetCurrentAudit(this IRepositoryAsync<Audit> repository)
{
var audits = await repository
.Query(a => a.BeginDate.Year == DateTime.Now.Year)
.Include()
.SelectAsync(); ;
return audits.FirstOrDefault();
}
public static IEnumerable<Reminder> GetRemindersForAudit(this IRepositoryAsync<Audit> repository, int auditId)
{
var audits = repository.GetRepository<Audit>().Queryable();
var phases = repository.GetRepository<Phase>().Queryable();
var reminders = repository.GetRepository<Reminder>().Queryable();
var query = from audit in audits
where audit.Id == auditId
join phase in phases on audit.Id equals phase.AuditId
join reminder in reminders on phase.Id equals reminder.PhaseId
select reminder;
return query.AsEnumerable();
}
}
及其服务:
public interface IAuditService: IService<Audit>
{
Task<Audit> GetCurrentAudit();
}
public class AuditService: Service<Audit>, IAuditService
{
private readonly IRepositoryAsync<Audit> _repository;
public AuditService(IRepositoryAsync<Audit> repository)
:base(repository)
{
_repository = repository;
}
public async Task<Audit> GetCurrentAudit()
{
return await _repository.GetCurrentAudit();
}
public override void Delete(Audit entity)
{
// business logic here
base.Delete(entity);
}
public override void Update(Audit entity)
{
// business logic here
base.Update(entity);
}
public override void Insert(Audit entity)
{
// business logic here
base.Insert(entity);
}
}
这是我的 ViewModels 构造函数:
[ImportingConstructor]
public AdminViewModel(
IWindowManager windowManager,
IEventAggregator eventAggregator,
IUnitOfWorkAsync unitOfWorkAsync,
IAuditService auditService)
{
_windowManager = windowManager;
_eventAggregator = eventAggregator;
_unitOfWorkAsync = unitOfWorkAsync;
_auditService = auditService
}
那个 ViewModel 中的实现给我带来了问题:
try
{
//var audits = await _unitOfWorkAsync.RepositoryAsync<Audit>().Query().SelectAsync();
//Audits = new ObservableCollection<Audit>(audits);
SelectedAudit = await _auditService.GetCurrentAudit();
//AuditHistoryHeader = String.Format(Constants.ADMINVIEW_AUDITHISTORYHEADER, Audits.Count);
SelectedAudit.ObjectState = ObjectState.Deleted;
_auditService.Delete(SelectedAudit);
_unitOfWorkAsync.SaveChanges();
var audit = _unitOfWorkAsync.Repository<Audit>().Query().Select().FirstOrDefault();
_unitOfWorkAsync.Repository<Audit>().Delete(audit);
_unitOfWorkAsync.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
我在 URF UnitOfWork.cs 文件中不确定的内容:
public IRepository<TEntity> Repository<TEntity>() where TEntity : Entity, IEntity
{
try
{
if (ServiceLocator.IsLocationProviderSet)
//if (ServiceLocator.Current != null)
//{
return ServiceLocator.Current.GetInstance<IRepository<TEntity>>();
//}
}
catch (Exception)
{
}
return RepositoryAsync<TEntity>();
//return IoC.Get<IRepositoryAsync<TEntity>>();
}
问题是将 CRUD 操作持久保存到数据库的唯一方法是使用 _unitOfWorkAsync.Repository() 对象而不是使用该服务。
这并没有失败,但数据库没有变化。我有点不确定 URF 中使用的 ServiceLocator
和 Caliburn Micro[=63 中的 SimpleContainer
=] 以及他们(应该)如何一起工作。我也不确定 Bootstrapper.cs
文件中容器对象的 LifeTime。
我才刚刚开始了解 DI 模式,但我认为这是我遇到的问题的原因..
如果有人已经做过类似的事情或发现问题所在,请告诉我。如果你想看更多代码,请在下面评论。
编辑:
我试过以下方法,但数据没有被删除..
try
{
SelectedAudit = await _auditService.GetCurrentAudit();
SelectedAudit.ObjectState = ObjectState.Deleted;
_unitOfWorkAsync.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
如果我进入 UnitOfWork.cs SaveChanges
并查看 IDataContextAsync _dataContext
Audits dbSet,Audit 有 ObjectState Unchanged
。所以删除的审计不属于该上下文的一部分?!
我不能代表你的 DI 设置,因为我从未使用过 SimpleContainer(我使用的是 URF 和 Autofac)。您不确定的 URF 中的代码没问题。与 Autofac 和 Ninject 配合使用效果很好,所以我猜 SimpleContainer 会很相似。但我看到的一个问题是在以下代码行中:
SelectedAudit = await _auditService.GetCurrentAudit();
//AuditHistoryHeader = String.Format(Constants.ADMINVIEW_AUDITHISTORYHEADER, Audits.Count);
SelectedAudit.ObjectState = ObjectState.Deleted;
_auditService.Delete(SelectedAudit); // <-- This is a problem
_unitOfWorkAsync.SaveChanges();
首先,您将获得 SelectedAudit。 entity framework 现在正在跟踪 SelectedAudit。然后将 SelectedAudit 的状态设置为已删除。到目前为止,一切都很好。您现在要做的就是调用 Savechanges。 SelectedAudit 已附加到您 entity framework 上下文并将其状态标记为已删除足以让 entity framework 知道删除它。从您的服务中调用 Delete 将尝试再次将 SelectedAudit 附加到上下文。这将引发异常(最有可能)或导致意外行为。如果删除行
_auditService.Delete(SelectedAudit);
应该可以。请注意,这对于实体的更新是相同的。获取实体,对其进行更改,然后在不调用您的服务更新方法的情况下调用 SaveChanges。
如果您不首先在同一上下文中获取实体,则应仅使用 update/delete 方法。
至于生命周期管理,默认 URF 对 IDataContextAsync、IUnitOfWorkAsync 和 INorthwindStoredProcedures 使用 PerRequest。其余的都使用 TransientLifetime。坚持下去,并在需要时进行更改。
额外信息
我修改了我的服务以接受视图模型而不是实体。这是我使用的服务。我更喜欢这个,因为它在 DAL 和 Web 层之间保持更好的分离 (IMO)。
public interface IService<TModel>
{
TModel Find(params object[] keyValues);
Task<TModel> Insert(TModel model);
IEnumerable<TModel> InsertRange(IEnumerable<TModel> models);
Task<TModel> Update(TModel model);
void Delete(object id);
void Delete(TModel model);
Task<TModel> FindAsync(params object[] keyValues);
Task<TModel> FindAsync(CancellationToken cancellationToken, params object[] keyValues);
Task<bool> DeleteAsync(params object[] keyValues);
Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues);
}
public abstract class Service<TModel, TEntity> : IService<TModel> where TEntity : class, IObjectState
{
#region Private Fields
private readonly IRepositoryAsync<TEntity> _repository;
private readonly IUnitOfWorkAsync _unitOfWork;
private readonly IMapper _mapper;
#endregion Private Fields
#region Constructor
protected Service(IRepositoryAsync<TEntity> repository, IUnitOfWorkAsync unitOfWork, IMapper mapper)
{
_repository = repository;
_unitOfWork = unitOfWork;
_mapper = mapper;
}
#endregion Constructor
public void Delete(TModel model)
{
_unitOfWork.RepositoryAsync<TEntity>().Delete(_mapper.Map<TEntity>(model));
_unitOfWork.SaveChanges();
}
public void Delete(object id)
{
_unitOfWork.RepositoryAsync<TEntity>().Delete(id);
_unitOfWork.SaveChanges();
}
public async Task<bool> DeleteAsync(params object[] keyValues)
{
return await DeleteAsync(CancellationToken.None, keyValues);
}
public async Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues)
{
var result = await _unitOfWork.RepositoryAsync<TEntity>().DeleteAsync(cancellationToken, keyValues);
_unitOfWork.SaveChanges();
return result;
}
public TModel Find(params object[] keyValues)
{
return _mapper.Map<TModel>(_repository.Find(keyValues));
}
public async Task<TModel> FindAsync(params object[] keyValues)
{
var entity = await _repository.FindAsync(keyValues);
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
{
var entity = await _repository.FindAsync(cancellationToken, keyValues);
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> Insert(TModel model)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().Insert(_mapper.Map<TEntity>(model));
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
public IEnumerable<TModel> InsertRange(IEnumerable<TModel> models)
{
var entities = _unitOfWork.RepositoryAsync<TEntity>().InsertRange(_mapper.Map<IEnumerable<TEntity>>(models));
_unitOfWork.SaveChanges();
return _mapper.Map<IEnumerable<TModel>>(entities);
}
public async Task<TModel> Update(TModel model)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().Update(_mapper.Map<TEntity>(model));
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> UpdateFieldsOnly(TModel model, params string[] fields)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().UpdateFieldsOnly(_mapper.Map<TEntity>(model), fields);
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
}
我正在使用此框架:URF and Caliburn Micro 创建业务 WPF 应用程序。
这是 CM 引导程序的代码:
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer container;
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
container = new SimpleContainer();
container.Singleton<IWindowManager, WindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ShellViewModel>();
container.AllTypesOf<ITabItem>(Assembly.GetExecutingAssembly());
container.PerRequest<IDataContextAsync, AuraContext>();
container.PerRequest<IUnitOfWorkAsync, UnitOfWork>();
container.PerRequest<IRepositoryAsync<Audit>, Repository<Audit>>();
container.PerRequest<IAuditService, AuditService>();
}
protected override object GetInstance(Type service, string key)
{
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException(String.Format("Could not locate any instances of type {0}", service.Name));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
}
ShellViewModel.cs代码:
public class ShellViewModel: Conductor<ITabItem>.Collection.OneActive, IShell
{
private readonly IWindowManager _windowManager;
[ImportingConstructor]
public ShellViewModel(IWindowManager windowManager, IEnumerable<ITabItem> tabItems)
{
DisplayName = "Aura";
_windowManager = windowManager;
Items.AddRange(tabItems.Where(t => t.IsEnabled).OrderBy(t => t.DisplayOrder));
}
}
ShellView.xaml 标记:
<UserControl x:Class="Aura.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Aura"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600" MinWidth="800" MinHeight="600">
<TabControl x:Name="Items" Margin="3">
</UserControl>
这是一些存储库:
public static class AuditRepository
{
public static async Task<Audit> GetCurrentAudit(this IRepositoryAsync<Audit> repository)
{
var audits = await repository
.Query(a => a.BeginDate.Year == DateTime.Now.Year)
.Include()
.SelectAsync(); ;
return audits.FirstOrDefault();
}
public static IEnumerable<Reminder> GetRemindersForAudit(this IRepositoryAsync<Audit> repository, int auditId)
{
var audits = repository.GetRepository<Audit>().Queryable();
var phases = repository.GetRepository<Phase>().Queryable();
var reminders = repository.GetRepository<Reminder>().Queryable();
var query = from audit in audits
where audit.Id == auditId
join phase in phases on audit.Id equals phase.AuditId
join reminder in reminders on phase.Id equals reminder.PhaseId
select reminder;
return query.AsEnumerable();
}
}
及其服务:
public interface IAuditService: IService<Audit>
{
Task<Audit> GetCurrentAudit();
}
public class AuditService: Service<Audit>, IAuditService
{
private readonly IRepositoryAsync<Audit> _repository;
public AuditService(IRepositoryAsync<Audit> repository)
:base(repository)
{
_repository = repository;
}
public async Task<Audit> GetCurrentAudit()
{
return await _repository.GetCurrentAudit();
}
public override void Delete(Audit entity)
{
// business logic here
base.Delete(entity);
}
public override void Update(Audit entity)
{
// business logic here
base.Update(entity);
}
public override void Insert(Audit entity)
{
// business logic here
base.Insert(entity);
}
}
这是我的 ViewModels 构造函数:
[ImportingConstructor]
public AdminViewModel(
IWindowManager windowManager,
IEventAggregator eventAggregator,
IUnitOfWorkAsync unitOfWorkAsync,
IAuditService auditService)
{
_windowManager = windowManager;
_eventAggregator = eventAggregator;
_unitOfWorkAsync = unitOfWorkAsync;
_auditService = auditService
}
那个 ViewModel 中的实现给我带来了问题:
try
{
//var audits = await _unitOfWorkAsync.RepositoryAsync<Audit>().Query().SelectAsync();
//Audits = new ObservableCollection<Audit>(audits);
SelectedAudit = await _auditService.GetCurrentAudit();
//AuditHistoryHeader = String.Format(Constants.ADMINVIEW_AUDITHISTORYHEADER, Audits.Count);
SelectedAudit.ObjectState = ObjectState.Deleted;
_auditService.Delete(SelectedAudit);
_unitOfWorkAsync.SaveChanges();
var audit = _unitOfWorkAsync.Repository<Audit>().Query().Select().FirstOrDefault();
_unitOfWorkAsync.Repository<Audit>().Delete(audit);
_unitOfWorkAsync.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
我在 URF UnitOfWork.cs 文件中不确定的内容:
public IRepository<TEntity> Repository<TEntity>() where TEntity : Entity, IEntity
{
try
{
if (ServiceLocator.IsLocationProviderSet)
//if (ServiceLocator.Current != null)
//{
return ServiceLocator.Current.GetInstance<IRepository<TEntity>>();
//}
}
catch (Exception)
{
}
return RepositoryAsync<TEntity>();
//return IoC.Get<IRepositoryAsync<TEntity>>();
}
问题是将 CRUD 操作持久保存到数据库的唯一方法是使用 _unitOfWorkAsync.Repository() 对象而不是使用该服务。
这并没有失败,但数据库没有变化。我有点不确定 URF 中使用的 ServiceLocator
和 Caliburn Micro[=63 中的 SimpleContainer
=] 以及他们(应该)如何一起工作。我也不确定 Bootstrapper.cs
文件中容器对象的 LifeTime。
我才刚刚开始了解 DI 模式,但我认为这是我遇到的问题的原因..
如果有人已经做过类似的事情或发现问题所在,请告诉我。如果你想看更多代码,请在下面评论。
编辑:
我试过以下方法,但数据没有被删除..
try
{
SelectedAudit = await _auditService.GetCurrentAudit();
SelectedAudit.ObjectState = ObjectState.Deleted;
_unitOfWorkAsync.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
如果我进入 UnitOfWork.cs SaveChanges
并查看 IDataContextAsync _dataContext
Audits dbSet,Audit 有 ObjectState Unchanged
。所以删除的审计不属于该上下文的一部分?!
我不能代表你的 DI 设置,因为我从未使用过 SimpleContainer(我使用的是 URF 和 Autofac)。您不确定的 URF 中的代码没问题。与 Autofac 和 Ninject 配合使用效果很好,所以我猜 SimpleContainer 会很相似。但我看到的一个问题是在以下代码行中:
SelectedAudit = await _auditService.GetCurrentAudit();
//AuditHistoryHeader = String.Format(Constants.ADMINVIEW_AUDITHISTORYHEADER, Audits.Count);
SelectedAudit.ObjectState = ObjectState.Deleted;
_auditService.Delete(SelectedAudit); // <-- This is a problem
_unitOfWorkAsync.SaveChanges();
首先,您将获得 SelectedAudit。 entity framework 现在正在跟踪 SelectedAudit。然后将 SelectedAudit 的状态设置为已删除。到目前为止,一切都很好。您现在要做的就是调用 Savechanges。 SelectedAudit 已附加到您 entity framework 上下文并将其状态标记为已删除足以让 entity framework 知道删除它。从您的服务中调用 Delete 将尝试再次将 SelectedAudit 附加到上下文。这将引发异常(最有可能)或导致意外行为。如果删除行
_auditService.Delete(SelectedAudit);
应该可以。请注意,这对于实体的更新是相同的。获取实体,对其进行更改,然后在不调用您的服务更新方法的情况下调用 SaveChanges。
如果您不首先在同一上下文中获取实体,则应仅使用 update/delete 方法。
至于生命周期管理,默认 URF 对 IDataContextAsync、IUnitOfWorkAsync 和 INorthwindStoredProcedures 使用 PerRequest。其余的都使用 TransientLifetime。坚持下去,并在需要时进行更改。
额外信息 我修改了我的服务以接受视图模型而不是实体。这是我使用的服务。我更喜欢这个,因为它在 DAL 和 Web 层之间保持更好的分离 (IMO)。
public interface IService<TModel>
{
TModel Find(params object[] keyValues);
Task<TModel> Insert(TModel model);
IEnumerable<TModel> InsertRange(IEnumerable<TModel> models);
Task<TModel> Update(TModel model);
void Delete(object id);
void Delete(TModel model);
Task<TModel> FindAsync(params object[] keyValues);
Task<TModel> FindAsync(CancellationToken cancellationToken, params object[] keyValues);
Task<bool> DeleteAsync(params object[] keyValues);
Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues);
}
public abstract class Service<TModel, TEntity> : IService<TModel> where TEntity : class, IObjectState
{
#region Private Fields
private readonly IRepositoryAsync<TEntity> _repository;
private readonly IUnitOfWorkAsync _unitOfWork;
private readonly IMapper _mapper;
#endregion Private Fields
#region Constructor
protected Service(IRepositoryAsync<TEntity> repository, IUnitOfWorkAsync unitOfWork, IMapper mapper)
{
_repository = repository;
_unitOfWork = unitOfWork;
_mapper = mapper;
}
#endregion Constructor
public void Delete(TModel model)
{
_unitOfWork.RepositoryAsync<TEntity>().Delete(_mapper.Map<TEntity>(model));
_unitOfWork.SaveChanges();
}
public void Delete(object id)
{
_unitOfWork.RepositoryAsync<TEntity>().Delete(id);
_unitOfWork.SaveChanges();
}
public async Task<bool> DeleteAsync(params object[] keyValues)
{
return await DeleteAsync(CancellationToken.None, keyValues);
}
public async Task<bool> DeleteAsync(CancellationToken cancellationToken, params object[] keyValues)
{
var result = await _unitOfWork.RepositoryAsync<TEntity>().DeleteAsync(cancellationToken, keyValues);
_unitOfWork.SaveChanges();
return result;
}
public TModel Find(params object[] keyValues)
{
return _mapper.Map<TModel>(_repository.Find(keyValues));
}
public async Task<TModel> FindAsync(params object[] keyValues)
{
var entity = await _repository.FindAsync(keyValues);
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
{
var entity = await _repository.FindAsync(cancellationToken, keyValues);
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> Insert(TModel model)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().Insert(_mapper.Map<TEntity>(model));
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
public IEnumerable<TModel> InsertRange(IEnumerable<TModel> models)
{
var entities = _unitOfWork.RepositoryAsync<TEntity>().InsertRange(_mapper.Map<IEnumerable<TEntity>>(models));
_unitOfWork.SaveChanges();
return _mapper.Map<IEnumerable<TModel>>(entities);
}
public async Task<TModel> Update(TModel model)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().Update(_mapper.Map<TEntity>(model));
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
public async Task<TModel> UpdateFieldsOnly(TModel model, params string[] fields)
{
var entity = _unitOfWork.RepositoryAsync<TEntity>().UpdateFieldsOnly(_mapper.Map<TEntity>(model), fields);
await _unitOfWork.SaveChangesAsync();
return _mapper.Map<TModel>(entity);
}
}