2016-04-14 2 views
1

Я использую эту структуру: URF и Caliburn Micro для создания бизнес-приложения WPF.URF с WPF MVVM Caliburn Micro

Это код 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> 

Это некоторые Repository :

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); 
} 

Что-то я не уверены в URFUnitOfWork.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(), а не с помощью службы. Это не подводит, но никаких изменений в БД. Я немного не уверен в том, что ServiceLocator используется в URF и SimpleContainer от Caliburn Micro и как они (должны) работать вместе. Я также не уверен в объектах контейнеров LifeTime в файле Bootstrapper.cs.

Я только начинаю понимать картину DI, но я думаю, что это то, что дает мне меня вопрос ..

Если кто-то уже сделал что-то вроде этого или видит, что проблема есть, пожалуйста, дай мне знать. Если вам нравится больше кода, прокомментируйте это ниже.

EDIT:

Я попытался следующие, но данные не удаляются ..

try 
{ 
    SelectedAudit = await _auditService.GetCurrentAudit(); 
    SelectedAudit.ObjectState = ObjectState.Deleted; 

    _unitOfWorkAsync.SaveChanges(); 
} 
catch (Exception ex) 
{ 
    Console.WriteLine(ex.Message); 
} 

Если я шаг в UnitOfWork.cs SaveChanges и посмотреть в IDataContextAsync _dataContext Аудиты dbSet в Аудит имеет ObjectState Unchanged. Итак, удаленный аудит не является частью этого контекста ?!

ответ

1

Я не могу говорить для установки 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. SelectedAudit теперь отслеживается инфраструктурой entity. Затем вы удаляете состояние SelectedAudit. Все идет нормально. Все, что вам нужно сделать, это вызов Savechanges. SelectedAudit уже привязан к контексту фреймворка сущности и помечает его состояние как удаленное, достаточно для того, чтобы сущность-инфраструктура могла знать, чтобы удалить его. Вызов «Удалить из вашей службы» попробует снова подключить SelectedAudit к контексту. Это либо вызовет исключение (скорее всего), либо вызовет нежелательное поведение. Если вы удалите линию

_auditService.Delete(SelectedAudit); 

он должен работать. Обратите внимание, что это одно и то же для обновлений для объекта. Получите сущность, внесите в нее изменения, затем вызовите SaveChanges БЕЗ вызова метода обновления службы.

Вы должны использовать методы обновления/удаления, если вы не получите объект сначала в том же контексте.

Что касается управления жизненным циклом, то по умолчанию URF использует PerRequest для IDataContextAsync, IUnitOfWorkAsync и INorthwindStoredProcedures. Остальные все используют TransientLifetime. Придерживайтесь этого и измените, если вам нужно.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ Я изменил мое обслуживание, чтобы принять viewmodels вместо сущностей. Это сервис, который я использую. Мне нравится это лучше, поскольку он улучшает разделение (IMO) между уровнями DAL и Web.

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); 
    } 
} 
+0

Привет, garethb, спасибо за ваш ответ. Я попробую это, когда я заработаю около часа. Но я немного не уверен в методе обновления службы, если я не могу передать существующий объект. Как вы это делаете? – grmbl

+0

Я обычно передаю viewmodel к моей службе. Из моей службы я получаю объект и сопоставляю viewmodel с сущностью, что-то вроде этого в моем сервисе. var entity = _repository.Find (viewmodel.Id); entity.FirstName = viewmodel.FirstName; entity.ObjectState = ObjectState.Modified; _unitOfWork.SaveChanges(); – garethb

+0

См. Обновленный ответ для моей измененной службы. Примечание. Я использую Automapper для сопоставления между моими моделями и сущностями, но вы также можете отображать их вручную. – garethb

Смежные вопросы