2012-12-13 14 views
7

Oto dokument JSON gdzie Mongo dostawcy LINQ nie:Dziwne zachowanie dostawcy MongoDB LINQ dla pól o nazwie "id"

{"results": 
    {"text":"@twitterapi http://tinyurl.com/ctrefg", 
    "to_user_id":396524, 
    "to_user":"TwitterAPI", 
    "from_user":"jkoum", 
    "metadata": 
    { 
     "result_type":"popular", 
     "recent_retweets": 109 
    }, 
    "id":1478555574, 
    "from_user_id":1833773, 
    "iso_language_code":"nl", 
    "source":"<a href=\"http://twitter.com/\">twitter<\/a>", 
    "profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at":"Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id":0, 
    "max_id":1480307926, 
    "refresh_url":"?since_id=1480307926&q=%40twitterapi", 
    "results_per_page":15, 
    "next_page":"?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in":0.031704, 
    "page":1, 
    "query":"%40twitterapi"} 
} 

Uwaga "id" pole. Tutaj są związane C# definicje jednostki:

class Twitter 
{ 
    [BsonId] 
    public ObjectId Id { get; set; } 
    public Result results { get; set; } 
} 

private class Result 
{ 
    public string text { get; set; } 
    public int to_user_id { get; set; } 
    public string to_user { get; set; } 
    public string from_user { get; set; } 
    public Metadata metadata { get; set; } 
    public int id { get; set; } 
    public int from_user_id { get; set; } 
    public string iso_language_code { get; set; } 
    public string source { get; set; } 
    public string profile_image_url { get; set; } 
    public string created_at { get; set; } 
    public int since_id { get; set; } 
    public int max_id { get; set; } 
    public string refresh_url { get; set; } 
    public int results_per_page { get; set; } 
    public string next_page { get; set; } 
    public double completed_in { get; set; } 
    public int page { get; set; } 
    public string query { get; set; } 
} 

class Metadata 
{ 
    public string result_type { get; set; } 
    public int recent_retweets { get; set; } 
} 

Jeśli tworzę „Twitter” zbieranie i zapisać dokument powyżej, a następnie, gdy kwerendy go przy użyciu dostawcy Mongo LINQ, rzuca wyjątek FileFormatException: „«id»elementu nie znaleziono żadnych pole lub właściwość Mongo.Context.Tests.NativeTests klasy + Wynik”

Jednakże istnieją dwie alternatywne obejścia aby rozwiązać ten problem:

  1. Rename Wynik pola«iD», np do "idd" zarówno w dokumencie JSON doc, jak i klasie Result. Wtedy działa zapytanie LINQ.
  2. Zachowaj pole "id", ale dodatkowo dodaj pole "Id" do klasy Wynik i oznacz ją atrybutem [BsonId]. Teraz klasa Result zawiera pola "Id" i "id", ale zapytanie działa!

Używam API Mongo do sprawdzania kolekcji, wszystko działa dobrze, więc myślę, że to musi być błąd w dostawcy MongoDB LINQ. "id" w zagnieżdżonym elemencie JSON nie powinno być zarezerwowanym dziełem, czyż nie?

UPDATE: Oto wynik native wykonania kwerendy API:

> db.Twitter.find().limit(1); 

{ "_id" : ObjectId("50c9d3a4870f4f17e049332b"), 
"results" : { 
    "text" : "@twitterapi http://tinyurl.com/ctrefg", 
    "to_user_id" : 396524, 
    "to_user" : "TwitterAPI", 
    "from_user" : "jkoum", 
    "metadata" : { "result_type" : "popular", "recent_retweets" : 109 }, 
    "id" : 1478555574, 
    "from_user_id" : 1833773, " 
    iso_language_code" : "nl", 
    "source" : "<a href=\"http://twitter.com/\">twitter</a>", 
    "profile_image_url" : "http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at" : "Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id" : 0, 
    "max_id" : 1480307926, 
    "refresh_url" : "?since_id=1480307926&q=%40twitterapi", "results_per_page" : 15, "next_page" : "?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in" : 0.031704, 
    "page" : 1, 
    "query" : "%40twitterapi" 
    } 
} 
+0

+1 za obejście - dzięki. Co ciekawe, wygląda na to, że błąd powstaje tylko wtedy, gdy obiekt zawierający pojedynczy obiekt ma pole "id". Element podrzędny 'List' z' id' nie miał problemów. – StuartLC

Odpowiedz

14

MongoDB wymaga, aby każdy dokument przechowywany w bazie danych posiadał pole (na poziomie root) o nazwie "_id".

Sterownik C# zakłada, że ​​dowolne pole w klasie o nazwie "Id", "id" lub "_id" ma być mapowane na specjalne pole "_id". Jest to konwencja, którą można przesłonić. Sterownik C# nie wie, że twoja klasa Result nie ma być używana jako główny dokument kolekcji, więc znajdzie twoje "id" pole i odwzoruje je na "_id" w bazie danych.

Jednym ze sposobów, aby to zmienić, jest zmiana nazwy pola w klasie (jak odkryłeś). Następnie możesz użyć atrybutu [BsonElement], aby odwzorować swoją nazwę pola języka C# (np. "Idd") na dowolną nazwę używaną w bazie danych (np. "Id").Na przykład:

public class Result 
{ 
    [BsonElement("id")] 
    public int idd; // matches "id" in the database 
    // other fields 
} 

Inną alternatywą jest zastąpić konwencję, że znajdzie się „id” członek klasy do tłumienia domyślne zachowanie kierowcy C# dla swojej klasie wynik. Możesz to zrobić, rejestrując nowy profil konwencji dla swojej klasy wyników. Na przykład:

var noIdConventions= new ConventionProfile(); 
noIdConventions.SetIdMemberConvention(new NamedIdMemberConvention()); // no names 
BsonClassMap.RegisterConventions(noIdConventions, t => t == typeof(Result)); 

Musisz upewnić się, że robisz to bardzo wcześnie w swoim programie, zanim klasa wyników zostanie zamapowana.

+0

Stworzyłem bilet JIRA dla tego problemu, ponieważ wydaje się, że powinien istnieć łatwy sposób na zastąpienie mapowania id do _id, gdy nie chcesz, aby to się stało. Zobacz: https://jira.mongodb.org/browse/CSHARP-648 –

+0

dziękuję za wyjaśnienie. Nie, wiem jak to działa wewnętrznie. W bilecie JIRA spodobała mi się alternatywa z użyciem atrybutu BsonNoId dla zagnieżdżonych dokumentów. Łatwy do uchwycenia i użycia. Jeśli myślimy o konwencji dotyczącej konfiguracji, czy myślisz, że mapowanie może być domyślnie wyłączone dla klas zagnieżdżonych, tj. Czy klasa jest zdefiniowana w innej klasie, a klasa zewnętrzna ma pole ID lub pole [BsonId]? –

0

Problem jest Twój POCO nie pasuje do JSON

Twoja klasa Twitter ma identyfikator i klasy o nazwie wyniki
Jednak twój JSON ma właśnie wyniki. Na tym polega problem.

Tak więc w rzeczywistości, jesteś pomijając klasę Twitter, a tylko tworzenie instancji Result

Twój JSON powinien wyglądać mniej więcej tak:

{ 
    _id: ObjectId("50c9c8f3e0ae76405f7d2b5e"), 
    "results": { 
    "text":"@twitterapi http://tinyurl.com/ctrefg", 
    "to_user_id":396524, 
    "to_user":"TwitterAPI", 
    "from_user":"jkoum", 
    "metadata": 
    { 
     "result_type":"popular", 
     "recent_retweets": 109 
    }, 
    "id":1478555574, 
    "from_user_id":1833773, 
    "iso_language_code":"nl", 
    "source":"<a href=\"http://twitter.com/\">twitter<\/a>", 
    "profile_image_url":"http://s3.amazonaws.com/twitter_production/profile_images/118412707/2522215727_a5f07da155_b_normal.jpg", 
    "created_at":"Wed, 08 Apr 2009 19:22:10 +0000", 
    "since_id":0, 
    "max_id":1480307926, 
    "refresh_url":"?since_id=1480307926&q=%40twitterapi", 
    "results_per_page":15, 
    "next_page":"?page=2&max_id=1480307926&q=%40twitterapi", 
    "completed_in":0.031704, 
    "page":1, 
    "query":"%40twitterapi"} 
    } 
} 

Edytuj

Twój results w rzeczywistości jest dokumentem wbudowanym (w przypadku Twojego modelu POCO), więc powinieneś oznaczyć również Result.ID przy użyciu [BsonId]

+0

Dzięki Alex, najpierw pomyślałem, że wytłumaczyłeś zachowanie, ale potem spojrzałem na zawartość MongoDB. Zapomnij o oryginalnym JSON: użyto go do wstawienia danych. Teraz dane są wstawiane, a dokument ma identyfikator ObjectId. Kiedy więc zapytanie o kolekcję, identyfikator dokumentu jest właściwie zmapowany do Twitter.Id, i nie ma więcej Mongo ID w dokumencie, więc zagnieżdżony element nie musi niczego mapować. –

+0

Czy możesz wyjaśnić, co masz na myśli, "aby element zagnieżdżony nie musiał niczego mapować" - czy mógłbyś opublikować dosłowny wynik (na przykład) db.collection.find(). Limit (1) – Alex

+0

Oczywiście, ja ' Właśnie zaktualizowałem oryginalny post z wynikiem natywnego wywołania interfejsu API Mongo. Jak widać, ObjectId jest dołączony do zewnętrznego dokumentu, a nie do zagnieżdżonego elementu "results". Ponadto mógłbym mieć wiele elementów zagnieżdżonych i żaden z nich nie będzie miał odpowiednich identyfikatorów. –