Se qualcuno potesse mettermi su questo, molto apprezzato !!!
Mi scuso per qualsiasi re-battistrada, ho visto molti esempi, ma non tutti quelli che affrontano un pattern MVC.
Il mio dilemma è un'implementazione di un controller MVC in grado di elaborare più richieste e operazioni Db, ma non commettere mai nulla finché l'operatore non ha terminato i processi richiesti.
Ad esempio, aggiungi un nuovo record padre e restituisci informazioni sui record, con Id Key primaria id generata. Ora l'operatore può aggiungere i record dei figli passando la richiesta ForiegnKey. Alcuni di questi registri figlio possono avere figli propri, ecc. L'operatore deve superare l'intero processo prima che venga eseguito QUALSIASI COSA.
A meno che non sia completamente fuori, non riesco a capirlo. Mi sono imbattuto in questi:
Come fare una transazione in asp.net-core 2.0?
Alternativa a TransactionScope di System.Transaction assembly nel framework .net core
Da quanto ho capito, System.Transactions è attualmente disponibile in .Net Core 2.0.x. come indicato in 3568202:
https://docs.microsoft.com/en-us/dotnet/api/system.transactions.transactionscope?view=netcore-2.0
https://docs.microsoft.com/en-us/dotnet/framework/data/transactions/managing-concurrency-with-dependenttransaction che ha un esempio di WorkerThread che ho giocato con un po '.
Ma l'esempio non mostra esattamente da dove proviene Transactions.Current . Qui è dove ho colpito un muro nel mio Controller.
Non riuscivo a capire la transizione da un IDbContextTransaction derivato da MyDbContext.Database.BeginTransaction () ; e le cose di System.Transactions .
Poiché non esiste .DependentClone () per le transazioni create con IDbContextTransaction .
Mi mancava qualcosa, un'interfaccia? Non potrei metterlo insieme ...
Poi mi sono imbattuto in questo, che sembrava un po 'più semplice:
Niente a che vedere con TransactionScope , e la prima linea sembrava essere la salvezza:
"Puoi anche condividere una transazione tra più istanze di contesto"
Dopo aver tentato di implementare l'esempio in un controller, ottengo continuamente il temuto:
â € œInvalidOperationException: la transazione specificata non è associata alla connessione corrente. Possono essere utilizzate solo le transazioni associate alla connessione corrente.â €
Inoltre, cosa più importante anche , in tutta la mia lettura sembra indicare che non molto, se tutto ciò è possibile usando un contesto iniettato ??? !!! Huh, cosa, perché?
E come si impiegherebbe il corretto modello di "apprendimento" ???
Sto usando .Net Core 2.0.5 e VS 2017 15.5.3
BTW - Non sto usando un repository qui, ma se una soluzione richiede un repository, sono del tutto d'accordo.
Ecco la configurazione di base che sto guardando
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{ }
public DbSet<Source> Sources { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<DwsFileInfo> DwsFileInfo { get; set; }
}
Quindi in Startup.ConfigureServices
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConnection")));
Quindi il controller:
public class MyController : Controller
{
private MyDbContext MyDbContext { get; set; }
private static IDbContextTransaction RootTransaction { get; set; }
//private DependentTransaction DependentTransaction { get; set; }
//private TransactionScope ScopedTransaction { get; set; }
public CommentsController(MyDbContext context)
{
MyDbContext = context;
if (RootTransaction == null)
{
RootTransaction = MyDbContext.Database.BeginTransaction();
}
else
{
MyDbContext.Database.UseTransaction(RootTransaction.GetDbTransaction());
}
}
/// <summary>
/// Not sure I really need this?!?
/// </summary>
~CommentsController()
{
if (RootTransaction != null && RootTransaction.GetDbTransaction() != null)
{
RootTransaction.Rollback();
}
RootTransaction.Dispose();
MyDbContext.Dispose();
}
public IActionResult Add(object recordinfo)
{
// DB operations
}
public IActionResult Add2(object recordinfo)
{
// DB operations
}
public IActionResult Edit(object recordinfo)
{
// Db Operations
}
/// <summary>
/// something here to manage, instead of in the constructor
/// or relying on the destructor??
/// </summary>
private void ManageTransaction()
{
//commit
//rollback
//dispose
}
}
Ho già inserito alcune informazioni specifiche dell'applicazione nel contesto utilizzando:
app.Use(next => context =>
{
string path = context.Request.Path;
//modify context accordingly per path
return next(context);
});
Ma non potevo definire esattamente cosa significasse "modificare di conseguenza" ... E ora mi trovo in balia di StackOverflow ... Il tempo e la considerazione di tutti sono molto apprezzati. Grazie molto!!!
TL; DR : Il modello di saga ti aiuta a risolvere il tuo problema senza transazioni relative all'ambiente / database. Anche le saghe sono transazioni, ma in un modo diverso, più simile a una macchina a stati.
Il mio dilemma è un'implementazione di un controller MVC in grado di elaborare più richieste e operazioni Db, ma non commettere mai nulla finché l'operatore non ha terminato i processi richiesti.
Questo è il tuo primo malinteso. È così che funzionano Web e Internet. Http è senza stato. Non è possibile memorizzare lo stato con Http e tutti i dati richiesti per una singola richiesta devono essere inviati con la richiesta stessa.
Oppure si modifica la progettazione per consentire la permanenza del record padre e successivamente aggiungere gli oggetti figlio nelle richieste successive. Oppure fai in modo che il tuo cliente li mandi tutti insieme:
Esempio di Json:
{
// EF core assigns a new key when the key is null or has the default value
parentId: 0,
name: "Parent",
children: [{
name: "Child 1",
},{
name: "Child 2",
}]
}
Ora hai tutti i dati in una singola richiesta e puoi elaborarli in un'unica operazione. In età di jQuery, Angular ecc. Non è un problema.
Alcuni di questi registri figlio possono avere figli propri, ecc. L'operatore deve superare l'intero processo prima che venga eseguito QUALSIASI COSA.
Stai pensando come quando sviluppi un'applicazione desktop. Ma il web non è un'applicazione desktop e non è possibile trasferire lo stato tramite http come suo protocollo stateless.
Devi adattare il tuo design e utilizzare un processo più amichevole per il web. O semplicemente crea un'applicazione desktop se questo è un requisito assoluto;)
Dopo aver tentato di implementare l'esempio in un controller, ottengo continuamente il temuto:
â € œInvalidOperationException: la transazione specificata non è associata alla connessione corrente. Possono essere utilizzate solo le transazioni associate alla connessione corrente.â €
Inoltre, cosa più importante anche , in tutta la mia lettura sembra indicare che non molto, se tutto ciò è possibile usando un contesto iniettato ??? !!! Huh, cosa, perché?
Non nel modo in cui lo si desidera, non con gli ambiti di transazione. TransactionScope ha lo scopo di eseguire più operazioni di database per garantire coerenza. Non è inteso quando i dati verranno asincroni e ritardati in qualsiasi momento possibile.
BTW - Non sto usando un repository qui, ma se una soluzione richiede un repository, sono del tutto d'accordo.
Bene, stai usando un repository. EntityFramework è un'implementazione del modello Unit Of Work and Repository. DbContext è l'unità di lavoro, mentre le proprietà DbSet<T>
sono i repository.
Di solito, le persone tendono ad astrarre l'EF core da un repository a sé stante.
Il modo in cui lo desideri non funzionerà, perché DbContext deve essere definito come ambito (o rischi di perdere memoria). Anche DbContext non è thread-safe, quindi può essere utilizzato in modo sicuro solo da una singola discussione. Ecco perché il valore di default è scope, provando ad accedervi da un altro thread verrà appena generata un'eccezione di operazione non valida.
Ultimo ma non meno importante, se si dispone di transazioni a lungo termine in cui i dati possono arrivare in un determinato ordine e in qualsiasi momento, è disponibile un modello.
Si chiama process manager / sagas. Una saga o un gestore di processi è un processo di lunga durata. Ad esempio, un'operazione può avvenire ora e 2 minuti dopo la successiva. Il terzo potrebbe anche richiedere un'ora.
La saga è simile a una macchina a stati. Riceverà un messaggio (comando) per eseguire una determinata operazione e quindi lo continuerà a persistere (in un database, in una memoria, in una cache distribuita, in una sessione, qualunque sia la soluzione adatta al proprio scenario).
Poi qualche tempo indefinito più tardi, arriva il secondo comando e compie alcune operazioni su di esso. E ancora più tardi un terzo comando. Solo quando la saga si trova in una condizione specifica può essere commestata nel suo complesso.
Immagina, vuoi andare in vacanza. Ora vuoi prenotare il tuo volo e l'hotel.
Solo ora dopo che il tuo hotel e il tuo volo sono stati confermati, la tua vacanza può essere "impegnata". Ad esempio, se la società alberghiera rifiuta la tua richiesta, non puoi andare in vacanza. È quindi possibile emettere un altro comando (cercare un altro hotel) o annullare la vacanza. Quando annulli la vacanza, devi anche cancellare il tuo volo.
Questo equivale a un rollback nella transazione.
In una saga eliminerai semplicemente i record registrati o metti la "saga delle vacanze" in uno stato "cancellato".