2010-09-04 16 views
6

I śledzić przykładem ramowej jednostki:Tożsamość kolumna EF 4

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

i mają problem z kolumny tożsamości.

Oto część kodu tworzenia bazy danych:

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 

w VS 2010 buduję .edmx i na modelu widzę, że osoba StoreGeneratedPattern jest ustawiony na Identity.

Ale kiedy chcę stworzyć osobiście, przez: alt text

Dlaczego muszę umieścić identyfikator, jeśli kolumna jest autoincrement?

EDITŁ

Myślałem, że znalazłem sposób, aby rozwiązać mój problem:

var schoolContext = new SchoolEntities(); 

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

      schoolContext.SaveChanges(); 

bo to dodaje Bill osób, ale ... ponieważ PersonID nie jest pustych, a to dodaje mu z identyfikatorem 0. Kiedy próbowałem dodać inną osobę w taki sam sposób oczywiście dostaję błąd o kluczu podstawowym :)

Więc jeszcze z niczego ...

pomysłów?

Odpowiedz

6

Nie mogę ci powiedzieć, dlaczego zespół EF zdecydował się zrobić to w ten sposób - moim jedynym przypuszczeniem byłoby, że generowanie kodu, które utworzyło metodę CreatePerson, nie sprawdza, czy identyfikator jest autoreklingą i po prostu tworzy metoda, która zadziała w każdych okolicznościach - niezależnie od tego, czy ID jest autorekling, czy nie.

Jeśli to naprawdę przeszkadza, można również skorzystać z faktu, że wygenerowany klasa jednostka Person jest zdefiniowany jako częściowej klasy, dzięki czemu można go łatwo rozszerzyć. Utwórz nowy plik klasy o nazwie np. PersonEx.cs i przedłużyć ten częściowej klasy:

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

Teraz można łatwo tworzyć podmioty Person bez podania identyfikatora i dodać je do swoich danych:

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

    context.SaveChanges(); 

    int newID = newPerson.PersonID; 
} 

nie jest to idealne rozwiązanie - ale powinien działać dobrze (przynajmniej to dla mnie działa).

+0

dziękuję Marca! Następnym razem zaoszczędzisz dużo czasu – user278618

+0

Niezły wzór, ale myślę, że byłbyś bezpieczniejszy używając domyślnego 0 dla id .. -1 nie przeszedłby domyślnej kontroli wartości i można by założyć, że nie zaprojektowałbyś tabeli z tożsamością począwszy od zera. –

+0

@daveL: Liczby ujemne działają dobrze dla mnie ... –

7

IMO Id jest potrzebny, nawet jeśli został wygenerowany. Załóżmy, że używasz klucza obcego (inne zachowanie niż niezależne powiązanie). Oznacza to, że powiązane podmioty podrzędne używają klucza podstawowego jednostki nadrzędnej do budowania relacji. Załóżmy teraz, że dodajesz wiele jednostek nadrzędnych z powiązanymi jednostkami w jednej jednostce pracy. Musisz podać unikalny (tymczasowy) identyfikator dla każdej jednostki nadrzędnej, inaczej nigdy nie skonfigurujesz wykresu obiektu. Tak więc generator kodu prawdopodobnie robi to domyślnie.

Edit:

Jestem zaskoczony, że zostałem downvoted na odpowiedź na podstawie właściwych faktów.Więc pozwól mi wyjaśnić moją odpowiedź:

Istnieją dwa rodzaje stosunków dostępne w EF 4.0. Niezależne stowarzyszenie i stowarzyszenie klucza obcego. Różnica polega na tym, że ten drugi dodaje własności klucza obcego do encji. Pozwala to na pracę z relacjami w taki sam sposób jak w bazie danych - po prostu przez ustawienie kluczy. Możesz przeczytać o tych różnicach w MSDN.

Teraz pozwala zakładać prosty przykład. Mam prosty model EDMX z MyContext. Model składa się z dwóch elementów: Order i OrderLine. Po dodaniu tabel Orders i OrderLines do modelu zagęszcziłem kolumny Include foreign keys w modelu, więc używam skojarzeń kluczy zagranicznych zamiast niezależnych asocjacji.

Zamówienie ma sklep wygenerowany identyfikator jako klucz i CustomerName jako właściwość. Linia zamówienia ma wygenerowany przez sklep Id jako klucz, ProductTitle jako właściwość i OrderId jako klucz obcy. Chcę dodać dwa zlecenia w pojedynczej jednostce pracy:

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

Błąd zrobiłem w moim kodu jest ustawienie ID obu nowych zamówień na 0. Nawet jeśli jest to sytuacja tymczasowa Id nadal musi być unikalna, jeśli ciebie chcesz go użyć do kluczy zagranicznych. Prawdopodobnie możesz zapytać w tej chwili, dlaczego kontekst nie obsługuje samego Id? Proste, ponieważ nie może. Kontekst wie, że Id jest tymczasowy i zostanie zregenerowany w sklepie, ale kontekst nie zna szczegółów konfiguracji sklepu. Podczas konfigurowania tożsamości w bazie danych konfigurujesz także seed i inkrementację. Te dwie wartości nie są znane kontekstowi, więc kontekst nie jest w stanie wyprowadzić prawidłowego unikalnego identyfikatora tymczasowego, który nie jest już używany przez sklep. Załóżmy, że kontekst błędnie tworzy tymczasowy identyfikator, a następnie ładujesz encję, która już używa tego identyfikatora = problemu.

Jeśli po prostu zaktualizować swój kod, aby użyć unikalny identyfikator tymczasowy (wiem jak sklep jest skonfigurowany) to będzie działać. To jest IMO, jeden z powodów, dla których muszę zapewnić tymczasowe metody tworzenia identyfikatorów.

+0

Nie sądzę, że będzie problem, jeśli masz kilka obiektów nadrzędnych z 'ID = -1' z obiektami podrzędnymi. Po prostu wstawi rodzic dla rodzica i użyje '@@ TOŻSAMOŚCI' do powiązania potomków. Właściwość 'Id' nie jest używana do tworzenia wykresu obiektów. –

+0

Opisane czynności działają w przypadku niezależnego powiązania. Opisałem skojarzenie z kluczem obcym. –

2

Powinieneś być w stanie dostosować szablon t4, który generuje swoje zajęcia, aby usunąć obiekt z metody fabryki. Zobacz Danny Simmons' blog post, aby uzyskać informacje na temat tworzenia t4. Nie przetestowałem powstałej metody fabrycznej, ale nie rozumiem, dlaczego nie zadziałałoby.

Następnie zmodyfikować tę metodę:

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

to będzie działać dla StorageModel. Jeśli chcesz użyć modelu koncepcyjnego, musisz użyć MetadataProperties. tj: property.MetadataProperties.TryGetValue (annotationNamespace + ": StoreGeneratedPattern", false, out storeGeneratedPatternProperty); –

+0

@SimonFrancesco, czy możesz dokładniej wyjaśnić, jak to zrobić, co to jest adnotacjaNamespace? – Shimmy

+0

@Shimmy, W szablonie T4 możesz zrobić coś takiego: '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); 'Jeśli potrzebujesz dłuższego wyjaśnienia, z przyjemnością odpowiesz na pytanie :) –

Powiązane problemy