Einheit von Arbeit + repository + service layer mit dependency injection

Ich bin der Gestaltung einer web-Anwendung und windows-Dienst nutzen möchten und die Einheit von Arbeit + repository-Schicht in Verbindung mit einer service-Schicht, und ich bin mit einigen Schwierigkeiten setzen Sie alle zusammen, so dass die client-apps Steuern, die Transaktion von Daten, die mit dem Gerät arbeiten.

Die Einheit der Arbeit ist eine Sammlung aller repositories eingeschrieben in die Transaktion zusammen mit commit-und rollback-Operationen

public interface IUnitOfWork : IDisposable
{
    IRepository<T> Repository<T>() where T : class;
    void Commit();
    void Rollback();
}

Den generischen repository-Operationen, die durchgeführt werden, auf dem Daten-layer für ein bestimmtes Modell (Tabelle)

public interface IRepository<T> where T : class 
{
    IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, IList<Expression<Func<T, object>>> includedProperties = null, IList<ISortCriteria<T>> sortCriterias = null);
    PaginatedList<T> GetPaged(Expression<Func<T, bool>> filter = null, IList<Expression<Func<T, object>>> includedProperties = null, PagingOptions<T> pagingOptions = null);
    T Find(Expression<Func<T, bool>> filter, IList<Expression<Func<T, object>>> includedProperties = null);
    void Add(T t);
    void Remove(T t);
    void Remove(Expression<Func<T, bool>> filter);
}

Die konkrete Umsetzung der Einheit von Arbeit verwendet entity framework unter der Haube (DbContext) zum speichern der änderungen an der Datenbank, und eine neue Instanz der DbContext-Klasse erstellt wird pro Einheit der Arbeit

public class UnitOfWork : IUnitOfWork
{
    private IDictionary<Type, object> _repositories;
    private DataContext _dbContext;
    private bool _disposed;

    public UnitOfWork()
    {
        _repositories = new Dictionary<Type, object>();
        _dbContext = new DataContext();
        _disposed = false;
    }

Den repositories in der Einheit von arbeiten erstellt werden, nach Zugang, wenn Sie nicht existiert in der aktuellen Arbeitseinheit Instanz. Das repository übernimmt die DbContext als Konstruktor-parameter, damit es effektiv arbeiten können in der aktuellen Arbeitseinheit

public class Repository<T> : IRepository<T> where T : class
{
    private readonly DataContext _dbContext;
    private readonly DbSet<T> _dbSet;

    #region Ctor
    public Repository(DataContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = _dbContext.Set<T>();
    }
    #endregion

Ich habe auch ein service-Klassen Kapseln-business-workflow-Logik und Ihre Abhängigkeiten im Konstruktor.

public class PortfolioRequestService : IPortfolioRequestService
{
    private IUnitOfWork _unitOfWork;
    private IPortfolioRequestFileParser _fileParser;
    private IConfigurationService _configurationService;
    private IDocumentStorageService _documentStorageService;

    #region Private Constants
    private const string PORTFOLIO_REQUEST_VALID_FILE_TYPES = "PortfolioRequestValidFileTypes";
    #endregion

    #region Ctors
    public PortfolioRequestService(IUnitOfWork unitOfWork, IPortfolioRequestFileParser fileParser, IConfigurationService configurationService, IDocumentStorageService documentStorageService)
    {
        if (unitOfWork == null)
        {
            throw new ArgumentNullException("unitOfWork");
        }

        if (fileParser == null)
        {
            throw new ArgumentNullException("fileParser");
        }

        if (configurationService == null)
        {
            throw new ArgumentNullException("configurationService");
        }

        if (documentStorageService == null)
        {
            throw new ArgumentNullException("configurationService");
        }

        _unitOfWork = unitOfWork;
        _fileParser = fileParser;
        _configurationService = configurationService;
        _documentStorageService = documentStorageService;
    }
    #endregion

Die web-Anwendung ist eine ASP.NET MVC-app, der controller bekommt seine Abhängigkeiten injiziert
im Konstruktor als gut. In diesem Fall ist die Einheit der Arbeit und der service Klasse injiziert werden. Die Aktion führt eine operation durch den Dienst verfügbar gemacht, wie das erstellen eines Datensatzes in der repository und der Speicherung einer Datei auf einem Datei-server mit einem DocumentStorageService, und dann die Einheit der Arbeit ist verpflichtet, bei der controller-Aktion.

public class PortfolioRequestCollectionController : BaseController
{
    IUnitOfWork _unitOfWork;
    IPortfolioRequestService _portfolioRequestService;
    IUserService _userService;

    #region Ctors
    public PortfolioRequestCollectionController(IUnitOfWork unitOfWork, IPortfolioRequestService portfolioRequestService, IUserService userService)
    {
        _unitOfWork = unitOfWork;
        _portfolioRequestService = portfolioRequestService;
        _userService = userService;
    }
    #endregion
[HttpPost]
    [ValidateAntiForgeryToken]
    [HasPermissionAttribute(PermissionId.ManagePortfolioRequest)]
    public ActionResult Create(CreateViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            //validate file exists
            if (viewModel.File != null && viewModel.File.ContentLength > 0)
            {
                //TODO: ggomez - also add to CreatePortfolioRequestCollection method
                //see if file upload input control can be restricted to excel and csv
                //add additional info below control
                if (_portfolioRequestService.ValidatePortfolioRequestFileType(viewModel.File.FileName))
                {
                    try
                    {
                        //create new PortfolioRequestCollection instance
                        _portfolioRequestService.CreatePortfolioRequestCollection(viewModel.File.FileName, viewModel.File.InputStream, viewModel.ReasonId, PortfolioRequestCollectionSourceId.InternalWebsiteUpload, viewModel.ReviewAllRequestsBeforeRelease, _userService.GetUserName());
                        _unitOfWork.Commit();                            
                    }
                    catch (Exception ex)
                    {
                        ModelState.AddModelError(string.Empty, ex.Message);
                        return View(viewModel);
                    }

                    return RedirectToAction("Index", null, null, "The portfolio construction request was successfully submitted!", null);
                }
                else
                {
                    ModelState.AddModelError("File", "Only Excel and CSV formats are allowed");
                }
            }
            else
            {
                ModelState.AddModelError("File", "A file with portfolio construction requests is required");
            }
        }


        IEnumerable<PortfolioRequestCollectionReason> portfolioRequestCollectionReasons = _unitOfWork.Repository<PortfolioRequestCollectionReason>().Get();
        viewModel.Init(portfolioRequestCollectionReasons);
        return View(viewModel);
    }

Auf die web-Anwendung bin ich mit Unity DI container injizieren die gleiche Instanz der Einheit der Arbeit, die pro http-Anforderung an alle Anrufer, so dass die controller-Klasse wird eine neue Instanz und dann die service-Klasse verwendet, die die Einheit der Arbeit wird die gleiche Instanz wie der controller. Diese Weise der Dienst fügt einige Einträge in das repository, die ist eingeschrieben in eine Einheit der Arbeit und kann begangen werden durch den client-code in den controller.

Eine Frage zu den code-und Architektur-wie oben beschrieben. Wie kann ich loswerden der Arbeitseinheit in Abhängigkeit der service-Klassen? Im Idealfall möchte ich nicht in der service-Klasse eine Instanz der Einheit der Arbeit, weil ich nicht wollen, dass die service-commit für die Transaktion aus, ich möchte nur der service eine Referenz auf das repository, die es braucht, um mit zu arbeiten, und lassen Sie den controller (mit dem client-code) verpflichtet den Betrieb, wenn Sie es sehen, passt.

Auf der windows-Dienst-Anwendung, ich möchte in der Lage sein, um eine Reihe von Datensätzen mit einer einzigen Einheit der Arbeit, sagen alle Datensätze mit dem status ausstehend. Dann würde ich gerne eine Schleife durch alle Datensätze und die Abfrage der Datenbank zu bekommen, jedes einzeln, und überprüfen Sie dann den status für jeden eine während jeder Schleife, denn der status kann sich geändert haben von der Zeit, die ich abgefragt all der Zeit die ich bedienen möchte, auf ein einziges. Ich habe das problem jetzt ist, dass meine aktuellen Architektur nicht erlauben Sie mir mehrere die Einheit von arbeiten, für die gleiche Instanz des service.

public class ProcessPortfolioRequestsJob : JobBase
{
    IPortfolioRequestService _portfolioRequestService;
    public ProcessPortfolioRequestsJob(IPortfolioRequestService portfolioRequestService)
    {
        _portfolioRequestService = portfolioRequestService;
    }

Die Job-Klasse, über die ein Dienst benötigt, die im Konstruktor als Abhängigkeit-und wieder wird gelöst, indem die Einheit. Die service-Instanz wird aufgelöst und injiziert werden, hängt von einer Einheit der Arbeit. Ich möchte Sie zwei get-Operationen auf der service-Klasse, aber weil ich die bin, die unter der gleichen Instanz der Einheit der Arbeit, ich kann nicht erreichen, dass.

Für alle von Euch gurus da draußen, haben Sie irgendwelche Vorschläge auf, wie ich kann zu überarbeiten meinen Antrag, unit of work + repository + service-Klassen zum erreichen der genannten Ziele?

Ich soll mit der Einheit von Arbeit + repository-Muster zu ermöglichen, Testbarkeit auf meine service-Klassen, aber ich bin offen für andere design-patterns, wird mein code wartbar und testbar, zur gleichen Zeit, während die Trennung von Bedenken.

Update 1
Hinzufügen DataContext-Klasse, inheris von EF-s DbContext, wo ich erklärte mein EF DbSets und Konfigurationen.

public class DataContext : DbContext
{
    public DataContext()
        : base("name=ArchSample")
    {
        Database.SetInitializer<DataContext>(new MigrateDatabaseToLatestVersion<DataContext, Configuration>());
        base.Configuration.ProxyCreationEnabled = false;
    }

    public DbSet<PortfolioRequestCollection> PortfolioRequestCollections { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Configurations.Add(new PortfolioRequestCollectionConfiguration());

        base.OnModelCreating(modelBuilder);
    }
}
Schreibe einen Kommentar