2010-09-04 3 views
6

Ich folge von Entity Framework Beispiel:Identitätsspalte in EF 4

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

und ich habe Probleme mit Identitätsspalten.

Hier ist ein Teil des Codes der Erstellung der Datenbank:

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 

In VS 2010 I .edmx bauen und Modell, das ich sehe, dass Person StoreGeneratedPattern auf Identität festgelegt ist.

Aber wenn ich Person erstellt werden soll, durch: alt text

Warum muss ich id setzen, wenn diese Spalte autoincrement?

EDITŁ

Ich dachte, dass ich den Weg gefunden, um mein Problem zu lösen, indem sie:

var schoolContext = new SchoolEntities(); 

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

      schoolContext.SaveChanges(); 

weil es Bill zu Personen hinzugefügt, aber ... weil PersonID nicht NULL sein kann, und es eingefügt ihn mit der ID 0. Als ich versuchte, eine andere Person die gleiche Art und Weise natürlich über Primärschlüssel-I-Fehler :)

mit nichts ...

Irgendwelche Ideen also noch erhalten hinzufügen?

Antwort

6

Ich kann Ihnen nicht sagen, warum das EF-Team es so gemacht hat - meine einzige Vermutung wäre, dass die Codegenerierung, die die Methode erzeugt, nicht überprüft, ob die ID ein Autoinkrement ist und nur erstellt Eine Methode, die unter allen Umständen funktioniert - unabhängig davon, ob die ID automatisch inkrementiert wird oder nicht.

Wenn dies wirklich Sie stört, können Sie auch von der Tatsache profitieren, dass die erzeugte Entitätsklasse Person als Teilklasse definiert ist, so können Sie es leicht erweitern. Erstellen Sie eine neue Klassendatei, die z. PersonEx.cs und diese Teil Klasse erweitern:

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

Jetzt können Sie Person Entitäten erstellen Sie einfach und ohne eine ID-Angabe, und fügen Sie sie auf Ihre Daten:

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

    context.SaveChanges(); 

    int newID = newPerson.PersonID; 
} 

Es ist keine perfekte Lösung - aber es sollte gut funktionieren (zumindest tut es für mich).

+0

Danke Marc! Es ist das nächste Mal, dass Sie mich viel Zeit sparen – user278618

+0

Schönes Muster, aber ich denke, Sie wären sicherer mit der Standard-0 für ID .. -1 würde fehlschlagen Standard-Wert überprüft und man könnte davon ausgehen, dass Sie nicht eine Tabelle mit einer Identität entwerfen würde beginnend bei Null. –

+0

@daveL: negative Zahlen funktionieren gut für mich .... –

7

IMO die ID wird benötigt, auch wenn sie generiert wird. Angenommen, Sie verwenden die Fremdschlüsselzuordnung (anderes Verhalten als unabhängige Zuordnung). Dies bedeutet, dass verwandte untergeordnete Entitäten den Primärschlüssel der übergeordneten Entität verwenden, um eine Beziehung aufzubauen. Angenommen, Sie fügen mehrere übergeordnete Entitäten mit verknüpften Entitäten in einer einzigen Arbeitseinheit hinzu. Sie müssen für jede übergeordnete Entität eine eindeutige (temporäre) ID angeben, andernfalls werden Sie Ihr Objektdiagramm niemals konfigurieren. Also der Code-Generator macht dies wahrscheinlich als Standard.

Edit:

Ich bin überrascht, dass ich für Antwort basierend auf korrekte Fakten Downvoted worden.Also lassen Sie mich meine Antwort klären:

Es gibt zwei Arten von Beziehungen in EF 4.0. Unabhängige Vereinigung und Fremdschlüsselvereinigung. Der Unterschied besteht darin, dass der spätere Fremdschlüssel Eigenschaften zu Entitäten hinzufügt. So können Sie mit Relationen genauso wie in der Datenbank arbeiten - einfach durch Setzen von Schlüsseln. Sie können über diese Unterschiede in MSDN lesen.

Nehmen wir nun ein einfaches Beispiel. Ich habe ein einfaches EDMX-Modell mit MyContext. Model besteht aus zwei Entitäten Order und OrderLine. Als ich dem Modell Orders und OrderLines-Tabellen hinzugefügt habe, habe ich die Include-Fremdschlüsselspalten in das Modell eingearbeitet. Daher verwende ich Fremdschlüsselzuordnungen anstelle von unabhängigen Zuordnungen.

Auftrag hat Speicher generierte ID als Schlüssel und CustomerName als eine Eigenschaft. Die Auftragszeile hat eine generierte Geschäfts-ID als Schlüssel, ProductTitle als Eigenschaft und OrderId als Fremdschlüssel. Ich mag zwei Aufträge in einzelner Arbeitseinheit hinzuzufügen:

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. 
} 

Der Fehler, den ich in meinem Code tat setzt Id beiden Neuaufträge auf 0. Auch wenn diese temporäre Id ist es immer noch, wenn Sie eindeutig sein möchte es für Fremdschlüssel verwenden. Sie können wahrscheinlich im Moment fragen, warum der Kontext Id nicht alleine behandelt? Einfach, weil es nicht geht. Context weiß, dass die ID temporär ist und im Store neu generiert wird, aber der Kontext keine Details zur Store-Konfiguration kennt. Wenn Sie Identity in der Datenbank einrichten, richten Sie auch den Seed und Increment ein. Diese beiden Werte sind dem Kontext nicht bekannt, so dass der Kontext keine gültige eindeutige temporäre ID ableiten kann, die nicht bereits vom Speicher verwendet wird. Angenommen, im Kontext wird fälschlicherweise eine temporäre ID erstellt und danach wird die Entität geladen, die dieses Id = Problem bereits verwendet.

Wenn ich einfach meinen Code aktualisieren, um eine eindeutige temporäre ID zu verwenden (ich weiß, wie der Speicher konfiguriert ist), wird es funktionieren. Das ist IMO ein Grund, warum ich eine temporäre ID für Create-Methoden bereitstellen muss.

+0

Ich glaube nicht, dass es ein Problem geben wird, wenn Sie mehrere Elternobjekte mit 'ID = -1' mit untergeordneten Objekten haben. Es fügt nur Eltern für Eltern ein und verwendet '@@ IDENTITY', um die Kinder zu verbinden. Die Id-Eigenschaft wird nicht zum Erstellen des Objektdiagramms verwendet. –

+0

Was du beschrieben hast, funktioniert für unabhängige Vereine. Ich habe die Fremdschlüsselzugehörigkeit beschrieben. –

2

Sie sollten die t4-Vorlage anpassen können, die Ihre Klassen generiert, um die Eigenschaft aus der Factory-Methode zu entfernen. Informationen zum Erstellen des T4 finden Sie unter Danny Simmons' blog post. Ich habe die resultierende Fabrikmethode nicht getestet, aber ich kann nicht sehen, warum es nicht funktionieren würde.

Dann diese Methode ändern:

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

Dies funktioniert für das StorageModel. Wenn Sie das konzeptionelle Modell verwenden möchten, müssen Sie MetadataProperties verwenden. zB: property.MetadataProperties.TryGetValue (annotationNamespace + ": StoreGeneratedPattern", false, out storeGeneratedPatternProperty); –

+0

@SimonFrancesco, können Sie bitte mehr darüber, wie dies zu tun ist, was AnnotationNamespace? – Shimmy

+0

@Shimmy, In Ihrer T4-Vorlage können Sie Folgendes tun: 'String annotationNamespace =" http://schemas.microsoft.com/adobe/2009/02/edm/annotation "; \t \t \t MetadataProperty storeGeneratedPatternProperty = null; \t \t \t property.MetadataProperties.TryGetValue (annotationNamespace + ": StoreGeneratedPattern", false, out storeGeneratedPatternProperty); 'Wenn Sie eine längere Erklärung benötigen, würden Sie gerne antworten :) –