2012-11-07 32 views
5

sto cercando di convertire un IEnumerable<IEnumerable<string>>-ICollection<Character> ma fatica a capire il modo giusto per farlo in quanto la struttura di un oggetto è diverso rispetto alla struttura degli altri.Conversione da IEnumerable <IEnumerable <string>> per ICollection <T>

Il motivo della conversione è di prendere una deserializzazione JSON e inserirla in un database tramite Entity Framework. I dati JSON non sono normalizzati e non corrispondono alla struttura esatta del database. Nel database ho uno movie e uno person con una relazione molti-a-molti. La tabella ponte tra loro è characters. Ma il json ha solo un oggetto film con una serie di oggetti di persone e ogni persona con una serie di personaggi che giocano (IEnumerable<IEnumerable<string>>).

Fondamentalmente, ho bisogno di convertire le persone di ciascun film e i loro personaggi nel personaggio di ciascun film e nella sua rispettiva persona.

var movie = dataContractMovies.Select(m => new Movie { 
    Title = m.Title, 
    // not sure how to convert each Person and their characters 
    Characters = // Need to take each Person and their Characters in m.Cast and cast to a new Character and a new Person for which the Character belongs to 
}); 

Converti da

public class MovieDataContract { 
    [DataMember(Name = "title")] 
    public string Title { get; set; } 

    [DataMember(Name = "abridged_cast")] 
    public virtual IEnumerable<Person> Cast { get; set; } 
} 

[DataContract] 
public class Person { 
    [DataMember(Name = "name")] 
    public string Name { get; set; } 

    [DataMember(Name = "characters")] 
    public IEnumerable<string> Characters { get; set; } 
} 

Convertire a

public partial class Movie { 
    public string Title { get; set; } 
    public virtual ICollection<Character> Characters { get; set; } 

    public Movie() { 
     this.Characters = new HashSet<Character>(); 
    } 
} 

public partial class Character { 
    public string Name { get; set; } 
    public virtual Movie Movie { get; set; } 
    public virtual Person Person { get; set; } 
} 

public partial class Person { 
    public string Name { get; set; } 
    public virtual ICollection<Character> Characters { get; set; } 

    public Person() { 
     this.Characters = new HashSet<Character>(); 
    } 
} 

AGGIORNAMENTO E ulteriore domanda

Wha t se ho appena aggiunto un public IEnumerable<Character> Characters allo MovieDataContract? Poi ho potuto solo fare qualcosa di simile (in teoria, non hanno testato) ...

Characters = m.Characters; 

Quindi il mio nuovo MovieDataContract sarebbe simile a questa ...

[DataContract] 
public class MovieDataContract { 
    [DataMember(Name = "title")] 
    public string Title { get; set; } 

    [DataMember(Name = "abridged_cast")] 
    private IEnumerable<Person> _cast { get; set; } 

    public IEnumerable<Character> Characters { 
     get{ 
      foreach(var person in _cast) { 
       foreach(string name in person.characters) { 
        yield return new Character { Name = name, Person = new Person { Name = person.name }} 
       } 
      } 
     } 
    } 
} 
+0

Come si fa a proporre di andare da 'stringa per 'carattere'? Capire i problemi di packaging non ti aiuterà troppo se non c'è un buon modo per trasformare un oggetto psuedo-value in un oggetto di riferimento. – tmesser

+3

Sembra che * bflemi3 * voglia convertire una struttura ad albero (come restituita da un servizio Web) in una struttura relazionale (da inserire in un database usando il framework di entità). Il materiale IEnumerable > è un'aringa rossa. – dtb

+0

@ bflemi3: Sarebbe utile se fosse possibile aggiungere un esempio. – dtb

risposta

2

I' Sto facendo alcune supposizioni per la mia risposta: 1. Stiamo solo inserendo nuovi film e non aggiornando film già nel DB. 2. Stiamo utilizzando Entity Framework per fare l'inserto. 3. Le persone nei film aggiunti non sono già presenti nel DB associato ad altri film. 4. Ogni film ha un cast e ogni membro del cast ha almeno un personaggio.

Con il rischio di essere buttato fuori dal fantastico bus per bambini, la mia soluzione non utilizza LINQ, anche se sono sicuro che potrebbe essere modificato per farlo. Si noti che con EF non è necessario impostare entrambe le estremità dell'associazione per inserirlo nel database.

// Lookup is necessary because a person might be in more than one movie. 
var personLookup = new Dictionary<string,Person>(); 

foreach (var contractMovie in dataContractMovies) 
{ 
    var movie = new Movie() { Title = contractMovie.Title }; 

    foreach (var contractPerson in contractMovie.Cast) 
    { 
     if (!personLookup.ContainsKey(contractPerson.Name)) 
     { 
      personLookup.Add(contractPerson.Name, new Person() { Name = contractPerson.Name }); 
     } 
     var person = personLookup[contractPerson.Name]; 

     foreach (var contractCharacter in contractPerson.Characters) 
     { 
      var character = new Character() { Name = contractCharacter.Name, Person = person, Movie = movie }; 

      dataContext.Characters.Add(character); 
     } 
    } 
} 
dataContext.SaveChanges(); 

Se il DataContract mappati filmato per carattere, invece di persona (come da te suggerito nei commenti) si potrebbe ottenere via con qualcosa di più simile a questo:

var personLookup = new Dictionary<string, Person>(); 
var movie = dataContractMovies.Select(m => new Movie { 
    Title = m.Title, 
    Characters = m.Characters.Select(c => new Character() { Name = c.Name, Person = LookupPerson(c.Person, personLookup) }).ToList() 
}); 

public Person LookupPerson(string personName, Dictionary<string, Person> personLookup) 
{ 
     if (!personLookup.ContainsKey(personName)) 
     { 
      personLookup.Add(personName, new Person() { Name = personName }); 
     } 
     return personLookup[personName]; 
} 
+0

Grazie, questo è interessante e mi ha dato un'idea. Cosa succede se cambio 'MovieDataContract' per avere una proprietà che restituisca un' IEnumerable '? Allora, ho solo bisogno di fare riferimento a 'm.Characters'? – bflemi3

+0

Sono d'accordo con il commento di David B sopra. Quando gli oggetti DataContract entrano in ogni oggetto avrà la sua istanza unica. Quindi se hai due film con Harrison Ford, allora ci saranno due oggetti Person contratto contratto con il nome Harrison Ford. Se li trasformi in Entity object 1 to 1 finirai con due entità di Harrison Ford Person, e riceverai duplicati quando proverai a salvarlo nel database. – MichaC

+0

Ho aggiornato la mia risposta a qualcosa di più vicino a quello che hai provato in origine, se hai apportato la tua proposta di modifica a MovieDataContract. – MichaC

1
var flattenedShape = 
    from movie in dataContractMovies 
    from person in movie.Cast 
    from characterName in person.Characters 
    select new {Movie = movie, Person = person, CharacterName = characterName}; 


List<Character> characters = new List<Character>(); 
Dictionary<MovieDataContract, Movie> movieMap = 
    new Dictionary<MovieDataContract, Movie>(); 
Dictionary<PersonDataContract, Person> personMap = 
    new Dictionary<PersonDataContract, Person>(); 

foreach(var row in flattenedShape) 
{ 
    //have I seen this movie yet? 
    if (!movieMap.ContainsKey(row.Movie)) 
    { 
    movieMap.Add(row.Movie, new Movie() { Title = row.Movie.Title }); 
    } 

    //have I seen this person yet? 
    if (!personMap.ContainsKey(row.Person)) 
    { 
    personMap.Add(row.Person, new Person() { Name = row.Person.Name }); 
    } 

    //every character is unique. 
    Character x = new Character() { Name = row.CharacterName }; 
    movieMap.Characters.Add(x); 
    x.Movie = movieMap[row.Movie]; 
    personMap.Characters.Add(x); 
    x.Person = personMap[row.Person]; 
    characters.Add(x); 
} 

//at this point, all the characters are in characters... 
// all the movies are in the movieMap.Values... 
// and all the persons are in the personMap.Values 
+0

Grazie, questo è interessante e mi ha dato un'idea. Cosa succede se cambio 'MovieDataContract' per avere una proprietà che restituisca un' IEnumerable '? Allora, ho solo bisogno di fare riferimento a 'm.Characters'? – bflemi3

+0

Questa proprietà è buona finché le persone non sono condivise tra i film. –

Problemi correlati