【问题标题】:URF with WPF MVVM Caliburn MicroURF 与 WPF MVVM Caliburn Micro
【发布时间】:2016-04-14 10:49:19
【问题描述】:

我正在使用这个框架:URFCaliburn 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 中的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。所以删除的审计不是该上下文的一部分?!

【问题讨论】:

    标签: c# wpf mvvm caliburn.micro unit-of-work


    【解决方案1】:

    我不能代表您的 DI 设置,因为我从未使用过 SimpleContainer(我正在使用带有 Autofac 的 URF)。您不确定的 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。 SelectedAudit 现在由实体框架跟踪。然后将 SelectedAudit 的状态设置为已删除。到目前为止,一切都很好。您现在所要做的就是调用 Savechanges。 SelectedAudit 已附加到您的实体框架上下文,并将其状态标记为已删除足以让实体框架知道将其删除。从您的服务中调用 Delete 将尝试再次将 SelectedAudit 附加到上下文。这将引发异常(很可能)或导致不良行为。如果您删除该行

    _auditService.Delete(SelectedAudit);
    

    它应该工作。请注意,对于实体的更新也是如此。获取实体,对其进行更改,然后调用 SaveChanges 而不调用您的服务更新方法。

    如果您没有在同一上下文中首先获取实体,则应仅使用更新/删除方法。

    对于生命周期管理,默认 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);
        }
    }
    

    【讨论】:

    • 您好,garethb,感谢您的回答。我会在大约 1 小时后上班时试试这个。但是如果我不能传递现有实体,我有点不确定服务上的更新方法。你是怎么做的?
    • 我通常将视图模型传递给我的服务。从我的服务中,我获取实体并将视图模型映射到实体,就像在我的服务中一样。 var entity = _repository.Find(viewmodel.Id); entity.FirstName = viewmodel.FirstName;实体.ObjectState = ObjectState.Modified; _unitOfWork.SaveChanges();
    • 查看我修改后的服务的更新答案。请注意,我使用 Automapper 在模型和实体之间进行映射,但您也可以手动映射。
    • 嗨,gartehb,感谢您提供额外的代码! :) 但我编辑了我的问题。删除仍然没有被持久化到数据库中。 :( 感谢您的宝贵时间!
    • 尝试不使用异步。另外,出于某种原因,我将更新视为问题,而不是删除!对于那个很抱歉。将 ObjectState 设置为已删除后,您应该调用 _service.Delete(entity)。如果这不起作用,请尝试 _service.Delete(entity.Id)。
    猜你喜欢
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多