No Coercion Operator between list and read only collection when try to add entity to DbSet

433 Views Asked by At

I am getting the following error when try and add a new Author object to the AuthorsDbSet and unsure of how to resolve it.

{"No coercion operator is defined between types 'System.Collections.Generic.List1[Persistence.Entities.Book]' and 'System.Collections.ObjectModel.ReadOnlyCollection1[Persistence.Entities.Book]'."}

Method that causes exception when adding entity to db set:

public async Task<Guid> CreateAuthorAsync(string name)
    {
        ArgValidator.ThrowIfNullEmptyOrWhiteSpace(name);
        using var context = _dbContextFactory.CreateDbContext();

        var author = Author.Create(name);
        context.Authors.Add(author);//this line throws the exception
        await context.SaveChangesAsync();
        return author.Id;
    }

Author Class

public class Author
{
    public Guid Id { get; }
    public string Name { get; }
    public ReadOnlyCollection<Book> Books => _books.AsReadOnly();
    private List<Book> _books = new List<Book>();

    private Author(Guid id, string name)
    {
        Id = id;
        Name = name;
    }

    public static Author Create(string name)
    {
        ArgValidator.ThrowIfNullEmptyOrWhiteSpace(name);
        return new Author(Guid.NewGuid(), name);
    }
}

Book Class

public class Book
{
    public Guid Id { get; }
    public string Title { get; }
    public string Description { get; }
    public Author Author { get; }
    public Guid AuthorId { get; }
    public bool Available { get; private set; }

    private Book(Guid id, string title, string description, bool available, Guid authorId)
    {
        Id = id;
        Title = title;
        Description = description;
        Available = available;
        AuthorId = authorId;
    }

    public static Book Create(string title, string description, Guid authorId)
    {
        ArgValidator.ThrowIfNullEmptyOrWhiteSpace(title);
        description = description ?? string.Empty;

        return new Book(Guid.NewGuid(), title, description, true, authorId);
    }

DbContext

public class LibraryContext: DbContext
{
    public DbSet<Book> Books { get; set; }
    public DbSet<Author> Authors { get; set; }

    public LibraryContext(DbContextOptions<LibraryContext> options)
    : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Book>(e =>
        {
            e.ToTable("Books", b => b.IsTemporal());
            e.HasKey(x => x.Id);
            e.Property(x => x.Title).IsRequired();
            e.Property(x => x.Description).IsRequired();
            e.Property(x => x.Available).IsRequired().HasDefaultValue(false);
            e.HasOne(book => book.Author).WithMany(author => author.Books).HasForeignKey(book => book.AuthorId);
        });

        modelBuilder.Entity<Author>(e =>
        {
            e.ToTable("Authors", a => a.IsTemporal());
            e.HasKey(x => x.Id);
            e.Property(x => x.Name).IsRequired();
            e.HasMany(author => author.Books).WithOne(book => book.Author);
            //not explicitly configuring field as there is no proper fluent api method for it.
            //https://stackoverflow.com/questions/60617430/ef-core-3-configure-backing-field-of-navigation-property
            //simply allow it to be pickedup in the background by convention.
        });
    }

I'm not sure if it is as a result of the the onModelCreating code not being correct or something else. Perhaps I need to define some sort of coercion operator for List and Readonly collection, how would I do that?

1

There are 1 best solutions below

3
JHMri On BEST ANSWER

Resolved this by changing to IList and IReadonlyCollection. Leaving question up as I couldn't find similar issue when searching.

public class Author
{
    public Guid Id { get; }
    public string Name { get; }

    public IReadOnlyCollection<Book> Books => (_books as List<Book>).AsReadOnly();
    private IList<Book> _books = new List<Book>();

    private Author(Guid id, string name)
    {
        Id = id;
        Name = name;
    }

    public static Author Create(string name)
    {
        ArgValidator.ThrowIfNullEmptyOrWhiteSpace(name);
        return new Author(Guid.NewGuid(), name);
    }
}