2012-11-07 4 views
5

Я пытаюсь преобразовать IEnumerable<IEnumerable<string>> в ICollection<Character>, но изо всех сил, чтобы выяснить правильный способ сделать это, так как структура одного объекта отличается от структуры другого.Преобразование из IEnumerable <IEnumerable <string>> к ICollection <T>

Причина преобразования заключается в том, чтобы выполнить десериализацию json и вставить ее в базу данных через Entity Framework. Данные json не нормируются и не соответствуют точной структуре базы данных. В базе данных у меня есть movie и person, которые имеют отношения «многие ко многим». Таблица мостов между ними - characters. Но у json просто есть объект фильма с массивом объектов людей и каждый человек с массивом персонажей, которые они играют (IEnumerable<IEnumerable<string>>).

В принципе, мне нужно преобразовать людей и их персонажей каждого персонажа фильма и его соответствующего человека.

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 
}); 

Преобразовать От

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

Преобразовать в

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>(); 
    } 
} 

UPDATE И еще один вопрос

Wha t, если я только что добавил public IEnumerable<Character> Characters в MovieDataContract? Тогда я мог бы просто сделать что-то вроде этого (в теории, не испытанной) ...

Characters = m.Characters; 

Так мой новый MovieDataContract будет выглядеть следующим образом ...

[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

Как вы предлагаете, чтобы перейти от 'строки '' 'Символ'? Выяснение проблем с упаковкой не поможет вам слишком сильно, если нет хорошего способа превратить объект psuedo-value в ссылочный объект. – tmesser

+3

Похоже, что * bflemi3 * хочет преобразовать древовидную структуру (возвращаемую веб-службой) в реляционную структуру (которая будет вставлена ​​в базу данных с использованием структуры сущностей). Элемент IEnumerable > - это красная селедка. – dtb

+0

@ bflemi3: Было бы полезно, если бы вы могли добавить пример. – dtb

ответ

2

Я m делает несколько предположений для моего ответа: 1. Мы только вставляем новые фильмы и не обновляем фильмы уже в БД. 2. Мы используем Entity Framework для вставки. 3. Люди в добавляемых фильмах еще не находятся в БД, связанных с другими фильмами. 4. В каждом фильме есть актерский состав, и у каждого актера есть хотя бы один персонаж.

С риском попадания в классную детскую шину мое решение не использует LINQ, хотя я уверен, что его можно было бы изменить для этого. Обратите внимание, что с EF вам не нужно устанавливать оба конца ассоциации, чтобы вставить их в базу данных.

// 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(); 

Если DataContract отображается фильм в Character вместо лица (как вы предложили в комментариях) вы могли бы уйти с чем-то более, как это:

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

Спасибо, это интересно, и мне дана идея. Что делать, если я изменяю 'MovieDataContract', чтобы иметь свойство, которое возвратит' IEnumerable '? Значит, мне нужно только ссылаться на «m.Characters»? – bflemi3

+0

Я согласен с комментарием Дэвида Б. выше. Когда объекты DataContract входят в каждый элемент, он будет иметь свой собственный уникальный экземпляр. Так что если у вас есть два фильма с Харрисоном Фордом, тогда будут два объекта Person Person с именем Harrison Ford. Если вы измените их на объект Entity с 1 на 1, вы получите два объекта Harrison Ford Person, и при попытке сохранить его в базе данных вы получите дубликаты. – MichaC

+0

Я обновил свой ответ на что-то более близкое к тому, что вы изначально пытались, если внести предлагаемое изменение в 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

Спасибо, это интересно, и мне дана идея. Что делать, если я изменяю 'MovieDataContract', чтобы иметь свойство, которое возвратит' IEnumerable '? Значит, мне нужно только ссылаться на «m.Characters»? – bflemi3

+0

Это свойство хорошее, пока люди не делятся между фильмами. –

Смежные вопросы