3 votes

Comment modéliser une relation de plusieurs à plusieurs en code ?

Je travaille dans un système C# où j'ai besoin de modéliser une relation many-to-many, où je veux construire les modèles à la main (c'est-à-dire que je ne veux pas utiliser de frameworks).

Je vais vous présenter un exemple simplifié du système, puis vous expliquer le problème : J'ai une bibliothèque qui contient Livre et Auteur s chaque Livre peut avoir de nombreux Auteur et il en va de même pour les Auteur .

public class Book{
     public string Title;
     public List<Author> Authors;
}

public class Author{
     public string Name;
     public List<Book> Books;
 }

Maintenant, si je veux SÉLECTIONNER livres avec tous ses auteurs ; j'écrirais une fonction dans la Livre quelque chose comme ça :

public List<Book> All(){
     List<Book> Books = new List<Book>();
     Books = //Query that brings all books and its Authors and populate them into the Books list.   
 }

Maintenant, je devrais être capable de faire quelque chose comme ça :

 Book boo = new Book();
 List<Book> books = boo.All();
 Console.WriteLine(books[0].Authors[0].Name);

Mais ce que je ne peux pas faire, c'est :

 Console.WriteLine(books[0].Authors[0].Books[0].Title);

Parce que dans ce flux books[0].Authors[0].Books[0] sera nulle (ou la valeur par défaut). Je ne vais donc pas faire quelque chose de semi-dynamique et garder à l'esprit les performances du serveur de base de données et du serveur client. la perspective du serveur de base de données et du serveur client.

J'espère avoir été assez clair. Merci pour votre temps.

0voto

moller1111 Points 426

Je ferais quelque chose comme ça pour imiter la relation n à n de la base de données :

    public class DB
    {
        public class Book
        {
            public string Title;

            public List<Author> Authors
            {
                get
                {
                    return
                        BookAuthor.Connection.Where(ba => ba.Book.Title == this.Title)
                            .Select(ba => ba.Author)
                            .ToList();
                }
            }

            public void AddAuthor(Author a)
            {
                var connection = new BookAuthor(this, a);

                if (!BookAuthor.Connection.Exists(c => c.Author.Name == connection.Author.Name &&
                                                      c.Book.Title == connection.Book.Title))
                    BookAuthor.Connection.Add(connection);
            }
        }

        public class Author
        {
            public string Name;

            public List<Book> Books
            {
                get
                {
                    return
                        BookAuthor.Connection.Where(ba => ba.Author.Name == this.Name)
                            .Select(ba => ba.Book)
                            .ToList();
                }
            }

            public void AddBook(Book b)
            {
                var connection = new BookAuthor(b, this);

                if (!BookAuthor.Connection.Exists(c => c.Author.Name == connection.Author.Name &&
                                                      c.Book.Title == connection.Book.Title))
                    BookAuthor.Connection.Add(connection);
            }
        }

        private class BookAuthor
        {
            public Book Book { get; set; }
            public Author Author { get; set; }

            public static List<BookAuthor> Connection { get; } = new List<BookAuthor>();

            public BookAuthor(Book book, Author author)
            {
                Book = book;
                Author = author;
            }
        }
    }

    public void Run()
    {
        List<DB.Book> books = new List<DB.Book>()
        {
            new DB.Book() {Title = "Crime & Punishment"},
            new DB.Book() {Title = "Karamazov"}
        };

        List<DB.Author> authors = new List<DB.Author>()
        {
            new DB.Author()
            {
                Name = "Dostoyevsky",
                Books = { books[0] }
            }
        };
        authors[0].AddBook(books[1]);
        authors[0].AddBook(books[1]); // constraint

        List<DB.Book> allBooksOfDostoyevsky = authors[0].Books;
        var dost = authors[0].Books[0].Authors[0].Name; // Dostoyevsky
    }

0voto

Philipp Schmid Points 508

Dans la conception traditionnelle des bases de données, une relation n:m est constituée de trois tables :

Author -- 1:n --> helper table <-- n:1 -- Book

C'est ce que je ferais ici, avec 3 classes, Livre, Auteur et quelque chose qui relie les deux :

class Book {
    public int ID { get; set; }
    public string Title { get; set; }
}

class Author {
    public int ID { get; set; }
    public string Name { get; set; }
}

class BookAuthorConnections {
    public int ID_Book { get; set; }
    public int ID_Author { get; set; }
}

... puis faire 3 listes qui contiennent ces classes, par exemple :

static void Main(string[] args) {
    var authors = new List<Author>() {
        new Author() { ID = 1, Name = "Blah" },
        new Author() { ID= 2, Name = "Blubb" }
    };
    var books = new List<Book>() {
        new Book() { ID = 1, Title = "Some Book" },
        new Book() { ID = 2, Title = "Some other Book"},
        new Book() { ID = 3, Title = "Book 3"}
    };
    var connections = new List<BookAuthorConnections> {
        new BookAuthorConnections() { ID_Author = 1, ID_Book = 1 },
        new BookAuthorConnections() { ID_Author = 1, ID_Book = 2 },
        new BookAuthorConnections() { ID_Author = 2, ID_Book = 2 },
        new BookAuthorConnections() { ID_Author = 2, ID_Book = 3 }
    };

... puis les rejoindre, livres sur connexions sur auteurs. Avec un simple Where vous pouvez accéder à tout ce que vous voulez :

var result = books
    .Join(connections, book => book.ID, connection => connection.ID_Book, (book, con) => new { book.Title, con.ID_Author, con.ID_Book })
    .Join(authors, temp_result => temp_result.ID_Author, author => author.ID, (temp_result, author) => new { author.Name, temp_result.Title, temp_result.ID_Author, temp_result.ID_Book })
    .Where(x => x.Title == "Some other Book"); //returns all authors who wrote that book
    //.Where(x => x.Author == "...") would return all Books written by that author
    //.Where(x => x.ID_Book .... or x.ID_Author would access everything by ID

0voto

Morten Points 2474

Laissez le Author soit direct (le remplir dans le constructeur) :

public class Author { public Author(string name) { Nom = nom ; Livres = nouvelle liste() ; } public string Name { get ; set ; } public List Books { get ; set ; } }

Dans le Book Je remplis également la classe dans le constructeur. De plus, j'ai défini la propriété book de chaque classe Author a this :

  public class Book
    {
        public Book(string name, IList<Author> authors)
        {
            Name = name;
            Authors = new List<Author>();
            foreach (Author author in authors)
            {
                author.Books.Add(this);
                Authors.Add(author);
            }
        }

        public string Name { get; set; }
        public List<Author> Authors { get; set; }
    }

Le code serait alors quelque chose comme :

    public void main()
    {
        Author hermanM = new Author("Herman Melville");

        IList<Author> authors = new List<Author>();
        authors.Add(hermanM);

        Book mobyDick = new Book("Moby-Dick",authors);

        string bookTitle = mobyDick.Authors[0].Books[0].Name;

    }

Tout livre créé de cette manière serait sélectionnable par des requêtes linq, ou de toute autre manière. De plus, la chaîne serait infinie. Dans ce cas, je peux choisir le titre. De :

string bookTitle = mobyDick.Name;

Mais je peux aussi le faire à un autre niveau, par exemple :

string bookTitle = mobyDick.Authors[0].Books[0].Authors[0].Books[0].Authors[0].Books[0]; 

Dans ce cas, n'ayant qu'un seul livre écrit par un seul auteur, le résultat sera le même.

Dans l'exemple ci-dessous. Je crée une bibliothèque avec deux livres. Puis je recherche les livres d'un auteur.

public void BooksByAuthorExample()
    {

    //Create library 
    IList<Book> myLibrary = new List<Book>();

    //Define first book
    Author hermanM = new Author("Herman Melville");

    IList<Author> authors = new List<Author>();
    authors.Add(hermanM);

    Book mobyDick = new Book("Moby-Dick", authors);

    //Define second book
    Author gamma = new Author("Eric Gamma");
    Author helm = new Author("Richard Helm");
    Author johnson = new Author("Ralph Johnson");
    Author vlissides = new Author("Johm Vlissides");
    IList<Author> gangOfFour = new List<Author>() { gamma, helm, johnson, vlissides};
    Book designPatterns = new Book("Design Patterns - Elements of Reusable Object-Oriented Software", gangOfFour);

    //Add books to the library 
    myLibrary.Add(mobyDick);
    myLibrary.Add(designPatterns);

    //Select books written by Richard Helm

    IList<Book> searchResult = myLibrary.Where(x => x.Authors.Contains(helm)).ToList();
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X