2009-08-29 9 views
7
public class TestBL 
{ 
    public static void AddFolder(string folderName) 
    { 
     using (var ts = new TransactionScope()) 
     { 
      using (var dc = new TestDataContext()) 
      { 
       var folder = new Folder { FolderName = folderName }; 

       dc.Folders.InsertOnSubmit(folder); 
       dc.SubmitChanges(); 

       AddFile("test1.xyz", folder.Id); 
       AddFile("test2.xyz", folder.Id); 
       AddFile("test3.xyz", folder.Id); 

       dc.SubmitChanges(); 
      } 

      ts.Complete(); 
     } 
    } 

    public static void AddFile(string filename, int folderId) 
    { 
     using (var dc = new TestDataContext()) 
     { 
      dc.Files.InsertOnSubmit(
       new File { Filename = filename, FolderId = folderId }); 

      dc.SubmitChanges(); 
     } 
    } 
} 

Este es un ejemplo de DataContext anidado (no probado). El problema comienza cuando se agrega un TransactionScope a nuestro pequeño experimento (como se muestra arriba). El primer AddFile en la función AddFolder escalará la transacción a DTC (lo cual es malo por todos los medios), porque AddFile inicializa el nuevo DataContext, abriendo así una segunda conexión al DB.¿Cómo manejar el contexto de datos anidados en el BL?

  1. ¿Cómo puedo utilizar el DataContext anidado que no producirá un uso de DTC?
  2. ¿Todo esto simplemente está mal? ¿Debo usar el DataContext de manera diferente?
+0

Lamentablemente, sentí que esta pregunta no recibió la atención que merecía y no se ha respondido por completo :( –

Respuesta

3

Sin duda se debe evitar escalar a DTC siempre que sea posible. Cuando leí por primera vez su pregunta, mi instinto me decía que su transacción no pasaría a DTC porque está utilizando la misma cadena de conexión en ambos contextos de datos. Sin embargo, de acuerdo con this article, estaba equivocado.

No está solo en el confusion sobre las mejores prácticas con contextos de datos. Si buscas en la web para esto, hay respuestas en todo el mapa. En su ejemplo, puede pasar el contexto de datos al método AddFile. O bien, podría refactorizar este acceso a datos en una clase que mantenga la duración del contexto de datos hasta que la carpeta y los archivos estén todos guardados. Rick Strahl publicó an article en varias técnicas.

Aún así, ninguna de las respuestas que he visto en LINQ to SQL parece muy satisfactoria. ¿Ha considerado evitar la gestión de su capa de datos mediante el uso de un ORM? He usado NetTiers con gran éxito, pero escuché cosas buenas sobre PLINQO. Ambos requieren CodeSmith, pero hay many alternatives.

+0

Esta es una forma de verlo. Desafortunadamente, no voy a cambiar mi ORM pronto, así que ' Tendré que encontrar una solución adecuada al problema. –

+0

Parece que cambiar el ORM sería la solución más inteligente ya que Microsoft proporciona uno muy básico que no se ajusta a las necesidades de la empresa, así que cambié a NHibernate :) –

0

He encontrado una forma de manejar estas situaciones.

Muestra de clase base para una entidad BL (la entidad heredará esta clase)

abstract public class TestControllerBase : IDisposable 
{ 
    public TestDataContext CurrentDataContext { get; private set; } 

    protected TestControllerBase() 
    { 
     CurrentDataContext = new TestDataContext(); 
    } 

    protected TestControllerBase(TestDataContext dataContext) 
    { 
     CurrentDataContext = dataContext; 
    } 

    protected void ClearDataContext() 
    { 
     CurrentDataContext.Dispose(); 
     CurrentDataContext = new TestDataContext(); 
    } 

    public void Dispose() 
    { 
     CurrentDataContext.Dispose(); 
    } 
} 

controlador Implementado

public sealed class BLTestController : TestControllerBase 
{ 
    public BLTestController() { } 

    public BLTestController(TestDataContext dataContext) 
     : base(dataContext) { } 

    // The entity functions will be implemented here using CurrentDataContext 
} 

simple uso de un controlador implementado

var testController = new BLTestControllerA(); 

testController.DeleteById(1); 
uso

más compleja de un controlador implementado (2 controladores en el mismo DataContext)

var testControllerA = new BLTestControllerA(); 
var testControllerB = new BLTestControllerB(testControllerA.CurrentDataContext); 

testControllerA.DeleteById(1); 
testControllerB.DeleteById(1); 

Me gustaría ver más ideas acerca de la solución de este enigma y comentarios sobre el código de seguridad.

0

No necesita hacer 2 o más viajes de ida y vuelta para esta transacción. Creo que LINQ-DataContext es inteligente al reconocer que esos archivos pertenecen al objeto de la carpeta e insertará primero la fila de la carpeta y los archivos después de eso (todo en el contexto de una transacción, por ejemplo, COMENZAR TRAN/COMPROMISO). Sin embargo, necesita hacer:

dc.Files.InsertOnSubmit(
       new File { Filename = filename, Folder = folder }); 

en lugar de FolderId.Algo como esto:

public class TestBL 
{ 
    public static void AddFolder(string folderName) 
    { 
     using (var ts = new TransactionScope()) 
     { 
      using (var dc = new TestDataContext()) 
      { 
       var folder = new Folder { FolderName = folderName }; 

       AddFile(dc, "test1.xyz", folder); 
       AddFile(dc, "test2.xyz", folder); 
       AddFile(dc, "test3.xyz", folder); 

       dc.SubmitChanges(); 
      } 

      ts.Complete(); 
     } 
    } 

    private static void AddFile(DataContext dc, string filename, Folder folder) 
    { 
      dc.Files.InsertOnSubmit(
       new File { Filename = filename, Folder = folder }); 
    } 

    public static void AddFile(string filename, int folderId) 
    { 
     using (var dc = new TestDataContext()) 
     { 
      var folder = new Folder { FolderId = folderId }; 
      dc.Attach(folder, false); 
      AddFile(dc, filename, folder); 

      dc.SubmitChanges(); 
     } 
    } 
} 

A su pregunta respecto DTC: Yo no creo que sea posible para evitar DTC aquí debido a 2 conexiones abiertas. Creo que hicieron algunos cambios en esta área recientemente, vea here, pero el escenario descrito allí es un poco diferente (2 conexiones abiertas y cerradas una tras otra en lugar de 2 conexiones abiertas simultáneamente).

+0

El enlace que Aquí se explicaron las relaciones entre el alcance de la transacción y el TTD a la perfección, aunque es un poco diferente de lo que pregunté. –

1

Además de pasar el DataContext a AddFiles como parámetro, también puede pasar un valor de Conexión de DataContext a otro DataContext. Eso garantizaría que el otro DataContext tenga la misma conexión.

Cada DataContext también tiene también una propiedad de transacción, que probablemente podría establecer y transferir en lugar de usar el objeto TransactionScope.

+0

No creo que sea posible pasar la propiedad Connection, pero tal vez quise decir algo más: ¿podría agregar un ejemplo de código? –

+0

@Eran: el objeto DataContext tiene un constructor sobrecargado que acepta cualquier objeto IDbConnection. Entonces podrías hacer New DataContext (OldDataContext.Connection) – rossisdead

Cuestiones relacionadas