2010-09-04 18 views
6

seguir por ejemplo marco de la entidad:columna de identidad en EF 4

http://msdn.microsoft.com/en-us/library/bb399182.aspx

y tengo problema con columnas de identidad.

Aquí es parte del código de la base de datos de creación:

CREATE TABLE [dbo].[Person](
    [PersonID] [int] IDENTITY(1,1) NOT NULL, 
    [LastName] [nvarchar](50) NOT NULL, 
    [FirstName] [nvarchar](50) NOT NULL, 
    [HireDate] [datetime] NULL, 
    [EnrollmentDate] [datetime] NULL, 
CONSTRAINT [PK_School.Student] PRIMARY KEY CLUSTERED 
(
    [PersonID] ASC 
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] 
) ON [PRIMARY] 
END 
GO 

En VS 2010 construyo .edmx y al modelo que ver a esa persona StoreGeneratedPattern se ajusta a la Identidad.

Pero cuando quiero crear persona, por: alt text

Por qué debo poner Identificación, si esta columna es incrementado automáticamente?

EDITŁ

pensé que encontré la manera de resolver mi problema por:

var schoolContext = new SchoolEntities(); 

      schoolContext.AddToPeople(new Person() { LastName = "Gates", FirstName = "Bil" }); 

      schoolContext.SaveChanges(); 

porque Bill añadió a las personas, pero ... porque PersonaID no es anulable, y le inserté con id 0. Cuando traté de añadir otra persona de la misma manera, por supuesto, me sale error acerca de la clave principal :)

Así todavía sin nada ...

¿Alguna idea?

Respuesta

6

No puedo decirte por qué el equipo de EF eligió hacerlo de esta manera; mi única conjetura es que la generación de código que crea el método CreatePerson no verifica si la ID es una autoincrementación y simplemente crea un método que funcionará en cualquier circunstancia, ya sea que la identificación sea autoincremental o no.

Si esto realmente le molesta, también puede beneficiarse del hecho de que la clase de entidad generada Person se define como una clase parcial , por lo que se puede ampliar fácilmente. Crea un nuevo archivo de clase llamado p. Ej. PersonEx.cs y extender esa clase parcial:

public partial class Person 
{ 
    public static Person CreatePerson(string lastName, string firstName) 
    { 
     return CreatePerson(-1, lastName, firstName); 
    } 
} 

Ahora, usted puede crear fácilmente sus Person entidades sin especificar un ID, y añadirlos a sus datos:

using(SchoolEntities context = new SchoolEntities()) 
{ 
    Person newPerson = Person.CreatePerson("Gates", "Bill"); 
    context.AddToPeople(newPerson); 

    context.SaveChanges(); 

    int newID = newPerson.PersonID; 
} 

No es una solución perfecta - pero debería funcionar bien (al menos lo hace para mí).

+0

Gracias Marc! Es la próxima vez que me ahorras mucho tiempo – user278618

+0

Bonito patrón, pero creo que estarías más seguro usando el 0 predeterminado para el id ... -1 fallaría en las comprobaciones de valor predeterminadas y se podría suponer que no diseñarías una tabla con una identidad comenzando en cero. –

+0

@daveL: los números negativos funcionan bien para mí .... –

7

ID de IMO es necesario incluso si se genera. Supongamos que está utilizando una asociación de clave externa (comportamiento diferente a la asociación independiente). Significa que las entidades secundarias relacionadas están utilizando la clave principal de la entidad principal para construir la relación. Ahora suponga que está agregando varias entidades principales con entidades relacionadas en una sola unidad de trabajo. Debe especificar Id único (temporal) para cada entidad matriz, de lo contrario, nunca configurará su gráfico de objetos. Entonces el generador de código probablemente lo haga de manera predeterminada.

Editar:

Me sorprende que se me ha downvoted de respuesta basada en hechos correctos.Así que déjenme aclarar mi respuesta:

Hay dos tipos de relaciones disponibles en EF 4.0. Asociación independiente y asociación de claves extranjeras. La diferencia es que el último agrega propiedades de clave foránea a las entidades. Esto le permite trabajar con las relaciones de la misma manera que en la base de datos, simplemente configurando las teclas. Puede leer sobre estas diferencias en MSDN.

Ahora supongamos un ejemplo simple. Tengo un modelo EDMX simple con MyContext. El modelo consta de dos entidades Order y OrderLine. Cuando agregué las tablas Pedidos y Líneas de pedidos al modelo, engrosé las columnas Incluir claves externas en el modelo, por lo que estoy usando asociaciones de claves externas en lugar de asociaciones independientes.

El pedido tiene identificador generado por la tienda como clave y CustomerName como propiedad. La línea de pedido tiene ID generado por la tienda como clave, ProductTitle como propiedad y OrderId como clave externa. Quiero añadir dos órdenes en una sola unidad de trabajo:

using (var context = new MyContext()) 
{ 
    var ox = Order.CreateOrder(0, "CustomerX"); 
    var oy = Order.CreateOrder(0, "CustomerY"); 

    // Building relationship in the same way as in database 
    var olx = OrderLine.CreateOrderLine(0, ox.Id, "ProductX"); 
    var oly = OrderLine.CreateOrderLine(0, oy.Id, "ProductY"); 

    context.Orders.AddObject(ox); 
    context.Orders.AddObject(oy); 
    context.OrderLines.AddObject(olx); 
    context.OrderLines.AddObject(oly); 
    context.SaveChanges(); // UpdateException: Unable determine principal end of Model.FK_OrderLine_Order relationship. Multiple added entities have the same primary key. 
} 

El error que hice en mi código se ajuste de la identificación de los nuevos pedidos a 0. Incluso si se trata de identificación temporal que todavía tiene que ser único si quiero usarlo para claves extranjeras. Probablemente pueda preguntar en este momento por qué el contexto no maneja el Id por sí mismo. Simple porque no puede. El contexto sabe que Id es temporal y se regenerará en la tienda, pero el contexto no conoce los detalles sobre la configuración de la tienda. Cuando configura Identity en la base de datos, también configura seed e incremente. Esos dos valores no son conocidos por el contexto, por lo que el contexto no puede derivar Id temporal único válido que la tienda ya no usa. Supongamos que el contexto crea erróneamente un Id temporal y luego carga la entidad que ya usa este Id = problema.

Si simplemente actualizo mi código para usar Id temporal exclusivo (sé cómo está configurada la tienda), funcionará. Esa es la OMI, una de las razones por las que necesito proporcionar métodos temporales para crear Id.

+0

No creo que haya un problema si tiene varios objetos padre con 'ID = -1' con objetos secundarios. Simplemente insertará parent para parent y usar '@@ IDENTITY' para vincular los hijos. La propiedad 'Id' no se usa para crear el gráfico de objetos. –

+0

Lo que describes funciona para una asociación independiente. Describí la asociación de claves extranjeras. –

2

Debería poder personalizar la plantilla t4 que genera sus clases para eliminar la propiedad del método de fábrica. Consulte Danny Simmons' blog post para obtener información sobre cómo crear el t4. No he probado el método de fábrica resultante, pero no veo por qué no funcionaría.

a continuación, modificar este método:

bool IncludePropertyInFactoryMethod(StructuralType factoryType, EdmProperty edmProperty) 
{ 
    if (edmProperty.Nullable) 
    { 
     return false; 
    } 

    if (edmProperty.DefaultValue != null) 
    { 
     return false; 
    } 

    if ((Accessibility.ForReadOnlyProperty(edmProperty) != "public" && Accessibility.ForWriteOnlyProperty(edmProperty) != "public") || 
     (factoryType != edmProperty.DeclaringType && Accessibility.ForWriteOnlyProperty(edmProperty) == "private") 
     ) 
    { 
     // There is no public part to the property. 
     return false; 
    } 

    /********* 
    * This was added: 
    */ 

    var identity = edmProperty.TypeUsage.Facets 
     .Where(f => f.Name == "StoreGeneratedPattern").FirstOrDefault(); 

    if (identity != null && identity.Value.ToString() == "Identity") 
    { 
     // property is "Identity" generated, so skip from the factory method. 
     return false; 
    } 

    /********* 
    * end of the custom code 
    */ 

    return true; 
} 
+2

esto funcionará para StorageModel. Si desea utilizar el modelo conceptual, deberá usar MetadataProperties. es decir: property.MetadataProperties.TryGetValue (annotationNamespace + ": StoreGeneratedPattern", false, out storeGeneratedPatternProperty); –

+0

@SimonFrancesco, ¿pueden dar más detalles sobre cómo hacer esto? ¿Qué es annotationNamespace? – Shimmy

+0

@Shimmy, en su plantilla T4 puede hacer algo como: 'String annotationNamespace =" http://schemas.microsoft.com/ado/2009/02/edm/annotation "; \t \t \t MetadataProperty storeGeneratedPatternProperty = null; \t \t \t property.MetadataProperties.TryGetValue (annotationNamespace + ": StoreGeneratedPattern", false, out storeGeneratedPatternProperty); 'Si necesita una explicación más larga, estaría feliz de publicar la respuesta :) –

Cuestiones relacionadas