2010-04-21 9 views
8

Así que tengo un realmente difícil de entender cuando debería estar conectado a un objeto y cuando no debería estar conectado a un objeto. Lo primero es lo primero, aquí hay un pequeño diagrama de mi modelo de objetos (muy simplificado).LINQ to SQL: Adjuntar o no adjuntar

Schema

En mi DAL creo un nuevo DataContext cada vez que hago una operación relacionados con los datos. Digamos, por ejemplo, que quiero guardar un nuevo usuario. En mi capa de negocios, creo un nuevo usuario.

var user = new User(); 
user.FirstName = "Bob"; 
user.LastName = "Smith"; 
user.Username = "bob.smith"; 
user.Password = StringUtilities.EncodePassword("MyPassword123"); 
user.Organization = someOrganization; // Assume that someOrganization was loaded and it's data context has been garbage collected. 

Ahora quiero ir Guardar este usuario.

var userRepository = new RepositoryFactory.GetRepository<UserRepository>(); 
userRepository.Save(user); 

Neato! Aquí está mi lógica de guardado:

public void Save(User user) 
{ 
if (!DataContext.Users.Contains(user)) 
{ 
    user.Id = Guid.NewGuid(); 
    user.CreatedDate = DateTime.Now; 
    user.Disabled = false; 

    //DataContext.Organizations.Attach(user.Organization); 
    DataContext.Users.InsertOnSubmit(user); 
} 
else 
{ 
    DataContext.Users.Attach(user); 
} 

DataContext.SubmitChanges(); 

// Finished here as well. 
user.Detach(); 
} 

Así que, aquí estamos. Notarás que comento el bit donde el DataContext se adjunta a la organización. Si hay que adjuntar a la organización consigo la siguiente excepción:

NotSupportedException:Se ha hecho un intento de Adjuntar o Agregar una entidad que no es nuevo, tal vez con hayan cargado desde otro DataContext. Esto no es compatible.

Hmm, eso no funciona. Déjame probarlo sin adjuntar (es decir, comentar esa línea sobre cómo adjuntar a la organización).

DuplicateKeyException:No se puede agregar una entidad con una clave que ya está en uso .

WHAAAAT? Solo puedo asumir que esto está tratando de insertar una nueva organización que obviamente es falsa.

Entonces, ¿cuál es el problema chicos? ¿Que debería hacer? ¿Cuál es el enfoque adecuado? Parece que L2S hace que este un poco más difícil de lo que debería ser ...

EDIT: simplemente me di cuenta de que si trato de mirar el conjunto de cambios pendientes (dataContext.GetChangeSet()) me sale el mismo ¡NotSupportedException que describí antes! ¡¿Qué demonios, L2S ?!

+0

Simplemente curioso, ¿por qué no utiliza claves externas en la base de datos para indicar la relación en su modelo de objeto? –

+0

Hmm, estoy, estoy en la base de datos ... no estoy seguro de por qué no aparecieron en el diagrama (es decir, esperaría una clave gris) pero tenía la impresión de que la pequeña flecha que iba de una caja a el otro indicó que había una clave foránea en su lugar. –

+0

Usted está usando L2S, no como se supone que debe usarse. Normalmente, casi nunca se llama Attach. ¿Por qué estás usando un repositorio en primer lugar? Solo parece restringir lo que puede hacer y no ofrecerle nada a cambio. Con L2S, DataContext * es * el repositorio. – usr

Respuesta

1

Por lo tanto, "adjuntar" se usa cuando toma un objeto que existe de la base de datos, lo separa (por ejemplo, organizándolo en un servicio web en otro lugar) y desea volverlo a poner en la base de datos. En lugar de llamar a .Attach(), llame a .InsertOnSubmit() en su lugar. Estás casi ahí conceptualmente, solo estás usando el método incorrecto para hacer lo que quieres.

+0

Gracias por la respuesta ... ¿qué pasa en esta relación Padre -> Hijo que estoy pasando? Si estoy insertando un nuevo objeto secundario (es decir, Usuario en el diagrama anterior), ¿debo primero adjuntarlo al objeto principal? –

+1

Déjame ponerlo de esta manera. He escrito acerca de 5 aplicaciones linq to sql ahora, y nunca he tenido que usar el método Attach(). En realidad, no es necesario que asigne la relación entre los dos objetos si no desea, siempre que los ID de los campos relacionados estén de acuerdo. L2SQL entenderá lo que quiere decir cuando llame a SubmitChanges(). –

+0

Sí. Entonces eso insertó una nueva fila con una nueva ID en la base de datos. ¿No estás seguro de cómo lo conseguiste para actualizar las filas existentes con la ID? Obtuve el Objeto de DBCOntext, cambié algunos campos, envié el objeto de regreso para ser guardado. ¿Niether Attach o InsertOnSubmit() funciona, a menos que elimine la fila anterior? – ppumkin

6

Puede que no funcione exactamente así, pero así es como lo conceptualizo: cuando invoca un objeto de un DataContext, una de las cosas que Linq hace es seguir los cambios a este objeto con el tiempo para que sepa qué guardar de nuevo si envía cambios.Si pierde este contexto de datos original, el objeto Linq invocado desde allí no tiene el historial de lo que ha cambiado desde el momento en que se invocó desde el DB.

Por ejemplo:

DbDataContext db = new DbDataContext(); 
User u = db.Users.Single(u => u.Id == HARD_CODED_GUID); 
u.FirstName = "Foo"; 
db.SubmitChanges(); 

En este caso, ya que el objeto del usuario fue convocado desde el contexto de datos, que fue el seguimiento de todos los cambios a "u" y sabe cómo presentar esos cambios en la base de datos.

En su ejemplo, tenía un objeto de usuario que se había conservado en algún lugar (o que se había pasado desde otro lugar y no tenía su DataContext original desde el que se invocó). En este caso, debe adjuntarlo al nuevo contexto de datos.

User u; // User object passed in from somewhere else 
DbDataContext db = new DbDataContext(); 
u.FirstName = "Foo"; 
DbDataContext.Users.Attach(u); 
db.SubmitChanges(); 

Dado que la relación entre el usuario y la organización es sólo un GUID (organizationid) en el modelo de datos, es suficiente para sujetar el objeto de usuario.

no estoy seguro acerca de su código de andamios, pero tal vez algo como esto:

private const Guid DEFAULT_ORG = new Guid("3cbb9255-1083-4fc4-8449-27975cb478a5"); 
    public void Save(User user) 
    { 
     if (!DataContext.Users.Contains(user)) 
     { 
      user.Id = Guid.NewGuid(); 
      user.CreatedDate = DateTime.Now; 
      user.Disabled = false; 
      user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just 
               // via a GUID, not by assigning an 
               // Organization object 

      DataContext.Users.InsertOnSubmit(user); 
     } 
     else 
     { 
      DataContext.Users.Attach(user); 
     } 

     DataContext.SubmitChanges(); 

    } 
+0

Debe usar ** la sobrecarga ** 'Adjuntar (usuario, verdadero)' Para decirle a Linq2SQL que el modelo ha cambiado del original. La sobrecarga de tercios le permite pasar el original y el modificado y lo reasignará en segundo plano. Increíble. Todavía no entiendo cómo Dave Markle usó 'InsertOnSubmit' para actualizarse? – ppumkin

0

que utiliza una tabla grande con más de 400 columnas. ¡De ninguna manera voy a mapear y probar todo eso!

Obtenga el objeto original de la base de datos y adjúntelo con el objeto modificado. ¡Solo asegúrese de que el objeto que vuelve a entrar esté completamente lleno, de lo contrario, lo anulará con los espacios en blanco!

O puede copiar el GET original en la memoria y trabajar en una copia adecuada (no solo de referencia) del MOdel, luego pasar el original y el modificado, en lugar de volver a obtener como lo hago en el ejemplo. Esto es solo un ejemplo de cómo funciona.

public void Save(User user) 
{ 

    if (!DataContext.Users.Contains(user)) 
    { 
     user.Id = Guid.NewGuid(); 
     user.CreatedDate = DateTime.Now; 
     user.Disabled = false; 
     user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just 
              // via a GUID, not by assigning an 
              // Organization object 

     DataContext.Users.InsertOnSubmit(user); 
    } 
    else 
    { 
     var UserDB = DataContext.Users.FirstOrDefault(db => db.id == user.id); //Costs an extra call but its worth it if oyu have 400 columns! 
     DataContext.Users.Attach(user, userDB); //Just maps all the changes on the flu 
    } 

    DataContext.SubmitChanges(); 

}