2016-04-14 6 views
1

Ich verwende dieses Framework: URF und Caliburn Micro, um eine Business-WPF-Anwendung zu erstellen. URF mit WPF MVVM Caliburn Micro

Dies ist der Code für das CM Bootstrap:

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

Der ShellViewModel.cs Code:

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

Die ShellView.xaml Markup:

<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> 

Dies ist einige 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(); 
    } 
} 

Und sein Service:

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

Das ist mein Viewmodel Konstruktor:

[ImportingConstructor] 
public AdminViewModel(
    IWindowManager windowManager, 
    IEventAggregator eventAggregator, 
    IUnitOfWorkAsync unitOfWorkAsync, 
    IAuditService auditService) 
{ 
    _windowManager = windowManager; 
    _eventAggregator = eventAggregator; 
    _unitOfWorkAsync = unitOfWorkAsync; 
    _auditService = auditService 
} 

Und die Umsetzung in diesem Ansichtsmodell, die mir Fragen ist zu geben:

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

Etwas, was ich bin nicht sicher in der URFUnitOfWork.cs Datei:

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

Das Problem ist, dass der einzige Weg CRUD-Operationen auf die Datenbank persistieren mit dem _unitOfWorkAsync.Repository() Objekt ist und den Dienst nicht verwenden. Das scheitert aber keine Änderungen in der DB .. Ich bin ein wenig unsicher über die ServiceLocator in URF und die SimpleContainer von Caliburn Micro und wie sie (sollten) zusammen arbeiten. Ich bin auch unsicher über die LifeTime der Container-Objekte in der Datei Bootstrapper.cs.

Ich fange gerade das DI-Muster zu verstehen, aber ich denke, das ist es, was mir die Frage ist was ich habe ..

Wenn jemand schon so etwas wie dies getan oder sieht, was das Problem ist, bitte Gib mir Bescheid. Wenn Sie mehr Code sehen möchten, kommentieren Sie bitte unten.

EDIT:

Ich habe versucht, die folgenden aber die Daten nicht gelöscht werden ..

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

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

Wenn ich Schritt in die UnitOfWork.cs SaveChanges und schauen in die IDataContextAsync _dataContext Audits dbSet Das Audit hat ObjectState Unchanged. Also ist das gelöschte Audit nicht Teil dieses Kontexts ?!

Antwort

1

Ich kann nicht für Ihr DI-Setup sprechen, da ich noch nie SimpleContainer verwendet habe (ich benutze URF mit Autofac). Der Code in URF, von dem Sie sich nicht sicher sind, ist in Ordnung. Funktioniert großartig mit Autofac und Ninject, also denke ich, SimpleContainer wird ähnlich sein.Aber ein Problem sehe ich in den folgenden Codezeilen:

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

Zuerst erhalten Sie das SelectedAudit. SelectedAudit wird jetzt vom Entitätsframework verfolgt. Sie legen dann den Status von SelectedAudit auf gelöscht fest. So weit, ist es gut. Alles, was Sie jetzt tun müssen, ist Save Changes aufzurufen. SelectedAudit ist bereits an Ihren Entity-Framework-Kontext angehängt und markiert seinen Status, da das gelöschte Objekt ausreicht, damit das Entity-Framework es löschen kann. Wenn Sie Löschen von Ihrem Dienst aufrufen, versuchen Sie, die SelectedAudit erneut an den Kontext anzuhängen. Dies wird entweder eine Ausnahme auslösen (höchstwahrscheinlich) oder ein unerwünschtes Verhalten verursachen. Wenn Sie die Zeile

_auditService.Delete(SelectedAudit); 

entfernen, sollte es funktionieren. Beachten Sie, dass dies für Aktualisierungen einer Entität gleich ist. Rufen Sie die Entität ab, nehmen Sie Änderungen daran vor, und rufen Sie dann SaveChanges auf, ohne Ihre Service-Update-Methode aufzurufen.

Sie sollten nur die update/delete-Methoden verwenden, wenn Sie die Entität nicht zuerst im selben Kontext erhalten.

Wie für die Lebensdauerverwaltung verwendet Standard-URF ​​PerRequest für IDataContextAsync, IUnitOfWorkAsync und INorthwindStoredProcedures. Alle anderen verwenden TransientLifetime. Bleiben Sie dabei und ändern Sie, wenn Sie müssen.

EXTRA INFO Ich habe meinen Service geändert, um Viewmodels anstelle von Entitäten zu akzeptieren. Dies ist der Dienst, den ich verwende. Ich mag das besser, da es eine bessere Trennung (IMO) zwischen den Ebenen DAL und Web hält.

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

Hallo Garethb, danke für deine Antwort. Ich werde das versuchen, wenn ich in etwa 1 Stunde bei der Arbeit bin. Aber ich bin ein wenig unsicher über eine Update-Methode für den Dienst, wenn ich eine bestehende Entität nicht übergeben kann. Wie gehst du da vor? – grmbl

+0

Ich gebe das Viewmodel normalerweise an meinen Service weiter. Von meinem Dienst bekomme ich die Entity und kartiere das Viewmodel auf die Entity, so etwas in meinem Service. var entity = _repository.Find (viewmodel.Id); entity.FirstName = viewmodel.FirstName; entity.ObjectState = ObjectState.Modified; _unitOfWork.SaveChanges(); – garethb

+0

Siehe aktualisierte Antwort für meinen geänderten Dienst. Hinweis: Ich verwende Automapper, um zwischen meinen Modellen und Entitäten zu mappen, aber Sie können auch manuell zuordnen. – garethb