带有 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 中使用的 ServiceLocatorCaliburn 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);
    }
}