从IEnumerable<IEnumerable<string>>转换为ICollection<T>

5

我正在尝试将一个 IEnumerable<IEnumerable<string>> 转换为 ICollection<Character>,但我很难弄清楚正确的方法,因为一个对象的结构与另一个对象的结构不同。

转换的原因是将json反序列化并通过Entity Framework插入到数据库中。 json数据没有规范化,也不完全匹配数据库的精确结构。 在数据库中,我有一个 电影 和一个 人物,它们之间有多对多的关系。 它们之间的桥梁表是角色。 但是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>();
    }
}

更新和进一步问题

如果我只是在MovieDataContract中添加了一个public IEnumerable<Character> Characters,那么我就可以像这样做(理论上,尚未测试)...

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

你打算如何从 string 转换为 Character?如果没有一个好的方法将伪值对象转换为引用对象,解决包装问题也不会对你有太大帮助。 - tmesser
3
看起来 bflemi3 想要将一个树形结构(由 Web service 返回)转换为关系型结构(使用 Entity Framework 插入到数据库中)。IEnumerable<IEnumerable<string>> 这个东西是一个分散注意力的问题,不需要考虑。 - dtb
@dtb 这个例子是我遇到困难的地方。第一段代码是我的示例。基本上,我需要将每部电影的人物和他们的角色转换为每个电影的角色及其相应的人物。 - bflemi3
1
转换是“从现有实例创建不同类型的新实例”。强制转换是“更改引用类型”。我相信你不想要强制转换。 - Amy B
@DavidB 谢谢你:),我不知道这一点。我已经相应地更改了标题。 - bflemi3
显示剩余2条评论
2个回答

2
我做出以下几个假设: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将电影映射到角色而不是人物(就像您在评论中建议的那样),则可以使用以下更简单的代码:
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];
}

谢谢,这很有趣,给了我一个想法。如果我将 MovieDataContract 更改为具有返回 IEnumerable<Character> 的属性,那么我只需要引用 m.Characters - bflemi3
我同意上面David B的评论。当DataContract对象到达时,每个项目都将拥有自己独特的实例。因此,如果您有两部电影都是哈里森·福特主演,那么将会有两个名称为“哈里森·福特”的数据合同Person对象。如果您将它们转换为实体对象1:1,则最终会得到两个Harrison Ford Person实体,并且在尝试保存到数据库时会出现重复。 - MichaC
我更新了我的答案,使其更接近你最初尝试的内容,如果你对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

谢谢,这很有趣,给了我一个想法。如果我将 MovieDataContract 更改为具有返回 IEnumerable<Character> 的属性,那么我只需要引用 m.Characters - bflemi3
只要人物没有在电影之间共享,那个属性就很好。 - Amy B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接