Riutilizzo della logica in un altro repository nel modello di repository ASP.NET MVC

4

Ho un'applicazione web ASP.NET MVC4, con EntityFramework per l'accesso ai dati. Nell'applicazione ho due repository come segue (questo è solo un esempio, non il mio vero codice):

public class CustomerRepository
{
     ICustomerContext _db;

     public CustomerRepository(ICustomerContext db)
     {
          _db = db;
     }

     ...

     public void DeactivateCustomer(int customerId)
     {
         var customer = _db.Customers.Single(x => x.CustomerId == customerId);
         customer.IsActive = false;
         _db.SaveChanges();
     }
}

E

public class ItemRepository
{
     IItemContext _db;

     public ItemRepository(IItemContext db)
     {
         _db = db;
     }

     ...

     public void DeactivateItem(int itemId)
     {
         var item = _db.Items.Single(x => x.ItemId == itemId);
         item.IsActive = false;

         // The problem is here
         new CustomerRepository().DeactivateCustomer(item.CustomerId);

         _db.SaveChanges();
     }
}

Il ItemRepository vuole utilizzare la logica di disattivazione del Cliente da CustomerRepository senza copiare il codice, o accoppiare entrambi i repository come nell'esempio sopra.

Voglio anche eseguire entrambe le azioni (disattivando l'elemento e disattivando il cliente) in una transazione (forse una chiamata a SaveChanges() )

Esiste uno schema di progettazione in grado di soddisfare questi requisiti?

    
posta KeyBored 23.10.2015 - 14:35
fonte

4 risposte

1

Probabilmente il modo migliore per risolvere questo problema è utilizzare un'unità di modello di lavoro e utilizzare un livello di logica aziendale per gestirli.

public class UnitOfWork : IDisposable
{
    private ICustomerContext customerContext;
    private IItemContext itemContext;

    private CustomerRepository customerRepository;
    private ItemRepository itemRepository;

    public CustomerRepository CustomerRepository
    {
        get
        {

            if (this.CustomerRepository == null)
            {
                this.CustomerRepository = new CustomerRepository (customerContext);
            }
            return CustomerRepository;
        }
    }

    public ItemRepository ItemRepository
    {
        get
        {

            if (this.ItemRepository == null)
            {
                this.ItemRepository = new ItemRepositoryitemContext);
            }
            return ItemRepository;
        }
    }

    public void Save()
    {
        customerContext.SaveChanges();
        itemContext.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Ora, nel tuo livello aziendale, potresti semplicemente

public class ItemWorker 
{
     public void DeactivateItem(int itemId)
     {
         using (var unitOfWork = new UnitOfWork)
         {
             var item = unitOfWork.ItemRepository.GetItem(itemId); 


             unitOfWork.ItemRepository.DeactivateItem(itemId);
             unitOfWork.CustomerRepository.DeactivateCustomer(item.CustomerId);
             unitOfWork.Save();
         }
     } 
}

Riferimento

    
risposta data 22.03.2016 - 04:12
fonte
1

In base alla mia esperienza, provare a far funzionare un framework come EF in un pattern di repository non è il massimo se si desidera davvero raggruppare le transazioni e separare le funzionalità in Repo in questo modo. Vorrei che il controller MVC istanzasse un contesto di dati EF e fosse responsabile della chiamata di "SaveChanges ()" dopo aver eseguito tutte le manipolazioni dei dati del record attivo. Per l'amor di DRY, puoi spostare tutte quelle funzioni di manipolazione in una libreria comune che prende IItemContext come metodo o parametro di costruzione. Quella lib potrebbe sembrare qualcosa di simile:

public class IItemDataManipulator
        {
            private readonly IItemContext _db;

            public IItemDataManipulator(IItemContext db)
            {
                _db = db;
            }

            public void DeactivateCustomer(int custId)
            {
                var customer = _db.Customers.Single(x => x.CustomerId == customerId);
                customer.IsActive = false;
            }

            public void DeactivateItem(int itemId)
            {
                var item = _db.Items.Single(x => x.ItemId == itemId);
                item.IsActive = false;
                DeactivateCustomer(item.customerId);
            }
        }

E non mi preoccuperei di aggiungere query EF su una sola riga a quella libreria per il retreival di oggetti di base. Se vuoi bloccare di più, metti questa classe nella stessa libreria del datacontext EF e segna tutti i setter INTERNAL per le cose che vuoi SOLO essere in grado di cambiare all'interno di questa libreria comune.

    
risposta data 21.01.2016 - 22:06
fonte
0

La logica di disattivazione di un cliente deve esistere in una classe Customer . Solo dopo aver estratto il cliente dal database e averne modificato il valore, è necessario salvarlo nuovamente nel database. Inoltre, la logica di disattivazione di un elemento dovrebbe andare sulla classe Item . Per fare in modo che ciò accada, è necessaria una relazione più descrittiva tra articolo e cliente:

public class Customer
{
    public bool IsActive { get; set; }
    public long CustomerId { get; set; }

    public void Deactivate()
    {
        IsActive = false;
    }
}

public class Item
{
    public bool IsActive { get; set; }
    public long CustomerId { get; set; }
    public Customer Customer { get; set; }

    public void Deactivate()
    {
        IsActive = false;

        if (Customer != null)
        {
            Customer.Deactivate();
        }
    }
}

Ora le tue classi di repository non hanno bisogno di questa logica aziendale - in effetti gli archivi non dovrebbero avere nessuna logica di business in essi per lo stesso identico motivo per cui stai postando questa domanda. È difficile riutilizzare la logica aziendale. Il vero cambiamento avviene nel codice utilizzando le classi di repository:

public class ItemsController : Controller
{
    public ActionResult Deactivate(long id)
    {
        using (var db = new DbContext())
        {
            var item = db.Items.Find(id);

            item.Deactivate();

            db.SaveChanges();
        }
    }
}

(utilizzando Entity Framework come esempio)

  1. Crea l'oggetto contesto del database all'interno di un blocco using in modo che venga sistemato correttamente se viene generata un'eccezione
  2. Recupera Item dal contesto del database
  3. Chiama Deactivate() sull'elemento
  4. L'elemento si disattiva, quindi chiama Deactivate sul cliente.
  5. Termina la transazione chiamando db.SaveChanges() per mantenere qualsiasi modifica dell'oggetto dominio al database

Se non si utilizza Entity Framework, potrebbe essere necessario salvare esplicitamente gli oggetti Item e Customer separatamente nel controller, ma il modello di progettazione principale è ancora valido.

    
risposta data 23.10.2015 - 19:30
fonte
0

Non farlo direttamente con il contesto DB, che porterà a un riutilizzo del codice insufficiente. Inoltre, non istanziare un repository direttamente all'interno di un altro repository, fidati, ti morderà nel sedere più tardi.

Il tuo repository dovrebbe essere più simile a questo, con il concreto figlio di IRepoFactory definito nell'applicazione chiamante:

public class MyRepo
{
     IRepoFactory _factory;
     public MyRepo(IRepoFactory factory)
     {
          _factory = factory;
     }

     public void DoThing()
     {
          _factory.GetMyOtherRepo().DoOtherThing();
     }
}
    
risposta data 22.03.2016 - 14:08
fonte

Leggi altre domande sui tag