1

The problem is "An author may have none or many books, but a book is never written by two or more authors."

public Author(Document cpf, string name, DateTime birthdate)
        {
            Cpf = cpf;
            Name = name;
            Birthdate = birthdate;
        }

        public Document Cpf { get; private set; }
        public string Name { get; private set; }
        public DateTime Birthdate { get; private set; }
        // EF Relation
        public Book Book { get; private set; }
        public ICollection<Book> Books { get; private set; }

        protected Author() { }

// 0 : N => Author : Books
            builder.HasMany(c => c.Books)
                .WithOne(b => b.Author)
                .HasForeignKey(b => b.AuthorId);
public Book(Guid categoryId, Guid authorId, string title, DateTime releaseDate, BookIdentificator isbn)
        {
            CategoryId = categoryId;
            AuthorId = authorId;
            Title = title;
            ReleaseDate = releaseDate;
            Isbn = isbn;
        }

        public Guid CategoryId { get; private set; }
        public Guid AuthorId { get; private set; }
        public string Title { get; private set; }
        public DateTime ReleaseDate { get; private set; }
        public BookIdentificator Isbn { get; private set; }
        public Category Category { get; private set; }
        public Author Author { get; private set; }
            // 1 : 1 => Books : Author
            builder.HasOne(c => c.Author)
                   .WithOne(b => b.Book)
                   .HasForeignKey(b => b.AuthorId);

Error: Cannot convert lambda expression to type 'string' because it is not a delegate type BookManager.Catalog.Data

Thiago Cruz
  • 75
  • 1
  • 9
  • 3
    Not an answer, but books often have multiple authors... – DavidG Dec 22 '19 at 19:12
  • 1
    Not in this context – Thiago Cruz Dec 22 '19 at 20:12
  • What you are describing sounds like standard one-to-many relationship from Author to Book. Every one-to-many 1:0..N from X to Y is either 1:0..1 (optional) or 1:1 (required) from Y to X. Hence `Author.Book` property is redundant and should be removed. Also the second fluent configuration. – Ivan Stoev Dec 22 '19 at 21:38

2 Answers2

2

This is a hard issue to help you with because you have not posted a complete example of your Author or Book classes, only snippets and those snippets are not complete enough.

Your particular error has been discussed:

Cannot convert lambda expression to type 'string' because it is not a delegate type

This is raised because the compiler is expecting your to use one of the overloads to QueryTypeBuilder.HasOne Method and finding no match it defaults to the overload of HasOne(string, string). You should use the extension methods to simplify access to the fluent configuration.

Add using statements:

using System.Linq; 
using System.Data.Entity;

That is only the start of your issues, then next one is that Author has both of the following properties:

// EF Relation
public Book Book { get; private set; }
public ICollection<Book> Books { get; private set; }

Here you are saying that each Author has one specific book AND a collection of books. Without understanding the rest of the application, this might be valid, perhaps the single book reference is the biography of the author? or their first book? either way if you want such relationships to exist, you will need to use fluent notation to specifically describe the endpoints used for both relationships.

Might I suggest instead that you only need the Books property on the Author class:

// EF Relation
public virtual ICollection<Book> Books { get; set; } = new HashSet<Book>();

Then your fluent notation still needs the following tweak:

Author

No changes here, this one is valid, using longer variable names to help explain the relationship.

You do not have to reduce variable names in linq statements to a single character, but if you do, please use the first character of the conceptual variable name or the type, to make it easier to read and review the expectations of the code.

// 0 : N => Author : Books
builder.HasMany(author => a.Books)
       .WithOne(book => book.Author)
       .HasForeignKey(book => book.AuthorId);

Book

In the original code OP has incorrectly stated that the Book:Author ratio was 1:1, when in fact it is Many:1

// N : 1 => Books : Author
builder.HasOne(book => book.Author)
       .WithMany(author => author.Books)
       .HasForeignKey(book => book.AuthorId);

NOTE: Although you can specify each relationship in a forward or reverse logic, it is only necessary to specify each relationship once via fluent notation.

  • Only one of the above configurations needs to be defined, they are both describing the same link.

The same configuration could have been achieved with attribute notation on the Book class:

[ForeignKey(nameof(AuthorId))]
public virtual Author Author { get; set; }

A final note about my code style, I recommend that all navigation properties are declared as virtual, to facilitate lazy loading and change tracking optimizations.

Also, all collection properties should be auto-initialised using new HashSet<T>() to reduce the code you have to write to create objects at runtime and to support chained data operations.

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
  • Really the `public Book Book {get; private set; }` is not necessary. `// 1: 1 => Books: Author` setting is required if I make `public virtual ICollection Books {get; set; } = new HashSet ();` ? – Thiago Cruz Dec 22 '19 at 22:27
  • I suspect `public Book Book {get; private set; }` is an error, it should not be there at all, unless you can explain the business reason for it. It has nothing to do with the relationship you have described: `Author` has a collection of `Books`, `Book` has a single `Author`. – Chris Schaller Dec 22 '19 at 22:31
  • Reviewing the situation, real relationship is "one book has one author and one author has N books". – Thiago Cruz Dec 22 '19 at 22:34
  • Absolutely, sorry I forgot to address that in my solution, I've updated my response. – Chris Schaller Dec 23 '19 at 00:55
0

Can't see any problems with your code. Maybe you're only missing a using statement? Cannot convert lambda expression to type 'string' because it is not a delegate type

Peter Sandor
  • 143
  • 1
  • 7