Domanda

Sto usando il "tecnica di stub" a aggiornare Il mio POCO (usato in un contesto indipendente, ASP.NET MVC).

Questo è il codice che ho attualmente nel mio controller (che funziona):

[HttpPost]
public ActionResult Edit(Review review)
{
   Review originalReview = _userContentService.FindById(review.PostId) as Review;
   var ctx = _unitOfWork as MySqlServerObjectContext;
   ctx.ApplyCurrentValues("MyEntities.Posts", review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

Come puoi vedere, c'è un odore di codice ovunque. :)

Alcuni punti:

  1. Uso l'iniezione di dipendenza (basata sull'interfaccia) per praticamente tutto
  2. Uso il modello di unità di lavoro per astrarre ObjectContext e fornire persistenza in più repository
  3. Attualmente il mio Iunitofwork L'interfaccia ha solo 1 metodo: void Commit();
  4. Controller IUserContentService e IUnitOfWork iniettare per di
  5. IUserContentService chiamate Find nei repository, che usano il ObjectContext.

Queste sono due cose che non mi piacciono con il mio codice sopra:

  1. Non voglio lanciare il Iunitofwork come MySqlServerObjectContext.
  2. Non voglio che il controller debba interessare ApplyCurrentValues

Fondamentalmente voglio che il mio codice sembri così:

[HttpPost]
public ActionResult Edit(Review review)
{
   _userContentService.Update(review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

Qualche idea su come posso farlo? (o qualcosa di simile).

Ho già intelligenza per elaborare il nome del set di entità in base al tipo (combinazione di generici, pluralizzazione), quindi non preoccuparti troppo.

Ma Mi chiedo dove il posto migliore da mettere ApplyCurrentValues è? Non sembra appropriato dirlo nel IUnitOfWork interfaccia, poiché questa è una preoccupazione di persistenza (EF). Per lo stesso motivo non appartiene al servizio. Se lo metto nel mio MySqlServerObjectContext Classe (ha senso), da dove chiamerei questo, poiché nulla ha direttamente l'accesso a questa classe: viene iniettato tramite DI quando qualcosa richiede IUnitOfWork.

qualche idea?

MODIFICARE

Di seguito ho una soluzione che utilizza la tecnica stub, ma il problema è che se avessi recuperato l'entità che sto aggiornando in anticipo, lancia un'eccezione, affermando che esiste già un'entità con quella chiave.

Il che ha senso, anche se non sono sicuro di come può risolverlo?

Devo "verificare se l'entità è già allegata, in caso contrario, allegala?"

Qualche esperto EF4 può aiutare?

MODIFICARE

Nevermind - Ho trovato la soluzione, vedi risposta di seguito.

È stato utile?

Soluzione

Ho capito - non è stato facile, quindi cercherò di spiegare meglio che posso. (per coloro a cui importa)

Codice pertinente del controller:

// _userContentService is IUserContentService
_userContentService.Update(review);

Quindi, il mio controller chiama un metodo chiamato Update Su IUserContentService, passando attraverso il titole fortemente Review oggetto.

Codice pertinente del servizio di contenuto utente

public void Update(Post post)
{
   // _userContentRepository is IPostRepository
   _userContentRepository.UpdateModel(post);
}

Quindi, il mio servizio chiama un metodo chiamato UpdateModel Su IPostRepository, passando attraverso il titole fortemente Review oggetto.

Ora, ecco la parte difficile.

In realtà l'ho fatto nessun repository specifici. Ho un repository generico chiamato GenericRepository<T> : IRepository<T>, che gestisce Tutti i diversi repository.

Quindi quando qualcosa richiede a IPostRepository (cosa che stava facendo il mio servizio), DE lo avrebbe dato a GenericRepository<Post>.

Ma ora lo do un PostRepository:

public class PostRepository : GenericRepository<Post>, IPostRepository
{
   public void UpdateModel(Post post)
   {
      var originalPost = CurrentEntitySet.SingleOrDefault(p => p.PostId == post.PostId);
      Context.ApplyCurrentValues(GetEntityName<Post>(), post);
   }
}

E perché la classe deriva da GenericRepository, eredita tutta la logica del repository core (trova, aggiungi, ecc.).

All'inizio ho provato a metterlo UpdateModel codice nel GenericRepository classe stessa (e quindi non avrei avuto bisogno di questo repository specifico), ma il problema è la logica per recuperare l'entità esistente si basa su una chiave di entità specifica, che la GenericRepository<T> non saprei.

Ma il risultato finale è il Cucitura è nascosto in profondità nelle profondità del livello dati e finisco con un controller davvero pulito.

MODIFICARE

Questa "tecnica stub" funziona anche:

public void UpdateModel(Post post)
{
   var stub = new Review {PostId = post.PostId};
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}

Ma il problema è perché Inviare è astratto, non posso istanziare e quindi dovrei controllare il tipo di post e creare stub per ogni tipo derivato. Non davvero un'opzione.

EDIT 2 (l'ultima volta)

Ok, ho ottenuto la "tecnica stub" che lavora con le classi astratte, quindi ora il problema della concorrenza è risolto.

Ho aggiunto un parametro di tipo generico al mio UpdateModel metodo e lo speciale nuovo () vincolo.

Implementazione:

public void UpdateModel<T>(T post) where T : Post, new()
{
   var stub = new T { PostId = post.PostId };
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>, post);
}

Interfaccia:

void UpdateModel<T>(T post) where T : Post, new();

Questo mi impedisce di dover capire manualmente il tipo di T, impedisce i problemi di concorrenza e impedisce anche un viaggio extra sul DB.

Pretty Groovy.

EDIT 3 (ho pensato che l'ultima volta fosse l'ultima volta)

La "tecnica di stub" sopra funziona, ma se recupero prima l'oggetto, lancia un'eccezione indicando un'entità con quella chiave già esiste nell'OSM.

Qualcuno può consigliare come gestirlo?

EDIT 4 (OK - Eccolo!)

Ho trovato la soluzione, grazie a questa risposta: È possibile verificare se un oggetto è già allegato a un contesto di dati in Entity Framework?

Avevo provato a "verificare se l'entità è allegata" usando il seguente codice:

ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);

Ma è sempre tornato nullo, anche quando ho esplorato l'OSM ho potuto vedere la mia entità lì con la stessa chiave.

Ma questo codice funziona:

CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)

Forse perché sto usando pure POCO, l'OSM ha avuto problemi a capire la chiave di entità, chissà.

Oh e un'altra cosa che ho aggiunto - in modo da non dover aggiungere un repository specifico per ogni entità, ho creato un attributo chiamato "Entitykey"(Attributo di proprietà pubblica).

Tutti i POCO devono avere 1 proprietà pubblica decorata con quell'attributo, o lancio un'eccezione nel mio modulo repository.

Quindi il mio repository generico cerca quindi questa proprietà per creare/configurare lo stub.

Sì, usa la riflessione, ma è una riflessione intelligente (basata su attributi) e sto già usando la riflessione per la plularizzazione dei nomi di set di entità da T.

Comunque, problema risolto: ora funziona tutto bene!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top