Ошибка «Свойство «PPP» List<NNN>» не может быть сопоставлено...» при попытке заполнить модель отношениями в Entity FrameC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Ошибка «Свойство «PPP» List<NNN>» не может быть сопоставлено...» при попытке заполнить модель отношениями в Entity Frame

Сообщение Anonymous »

Сущность:

Код: Выделить всё

using CommonSolution.Entities.Task;
using Microsoft.EntityFrameworkCore;

namespace EntityFramework.Models;

[System.ComponentModel.DataAnnotations.Schema.Table("tasks_custom_folders")]
[Microsoft.EntityFrameworkCore.EntityTypeConfiguration(typeof(TaskCustomFolderModel.Configuration))]
public class TaskCustomFolderModel
{

/* [ Theory ] `Guid.NewGuid()` return the string of 36 characters. See https://stackoverflow.com/a/4458925/4818123 */
[System.ComponentModel.DataAnnotations.Key]
[System.ComponentModel.DataAnnotations.MaxLength(36)]
public string ID { get; set; } = Guid.NewGuid().ToString();

/* ━━━ Title ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
[System.ComponentModel.DataAnnotations.MaxLength(TaskCustomFolder.Title.MAXIMAL_CHARACTERS_COUNT)]
public string Title { get; set; } = null!;

/* ━━━ Relatives ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
/* ─── Parent ───────────────────────────────────────────────────────────────────────────────────────────────────── */
/* [ Theory ] Although generally the explicit specifying of navigation property is optional, it is required for seeding. */
/* [ Theory ] `Guid.NewGuid()` return the string of 36 characters. See https://stackoverflow.com/a/4458925/4818123 */
[System.ComponentModel.DataAnnotations.MaxLength(36)]
public string? ParentID { get; set; }

public TaskCustomFolderModel? Parent { get; set; }

/* ─── Children ─────────────────────────────────────────────────────────────────────────────────────────────────── */
public List Children { get; set; } = [];

/* ━━━ Order Among Siblings ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
public uint OrderAmongSiblings { get; set; }

/* ━━━ Configuration ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
public class Configuration : Microsoft.EntityFrameworkCore.IEntityTypeConfiguration
{

private const string TABLE_NAME = "TasksCustomFolders";

public void Configure(
Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder builder)
{

builder.ToTable(Configuration.TABLE_NAME);

builder.
Property(taskCustomFolderModel => taskCustomFolderModel.Title).
IsRequired(TaskCustomFolder.Title.IS_REQUIRED);

builder.
Property(taskCustomFolderModel => taskCustomFolderModel.Parent).
IsRequired(TaskCustomFolder.Parent.IS_REQUIRED);

builder.
Property(taskCustomFolderModel => taskCustomFolderModel.ParentID).
IsRequired(TaskCustomFolder.Parent.IS_REQUIRED);

builder.
Property(taskCustomFolderModel => taskCustomFolderModel.Children).
IsRequired(TaskCustomFolder.Children.IS_REQUIRED);

builder.
Property(taskCustomFolderModel => taskCustomFolderModel.OrderAmongSiblings).
IsRequired(TaskCustomFolder.OrderAmongSiblings.IS_REQUIRED);
}
}
}
Ошибка при попытке заполнить базу данных путем вызова base.Database.EnsureCreated();:

System.InvalidOperationException: свойство List
TaskCustomFolderModel.Children не удалось сопоставить, поскольку поставщик базы данных не поддерживает этот тип. Рассмотрите возможность преобразования значения свойства в тип, поддерживаемый базой данных, с помощью преобразователя значений. См. https://aka.ms/efcore-docs-value-converters для получения дополнительной информации. Альтернативно исключите свойство из модели, используя атрибут «[NotMapped]» или используя «EntityTypeBuilder.Ignore» в «OnModelCreating».

Согласно ошибка: поставщик базы данных что-то не поддерживает, однако приведенный ниже пример, который я использовал для справки и который также имеет отношения, работает нормально!

Код: Выделить всё

public class MenuItem
{
public int Id { get; set; }
public string? Title { get; set; }
public int? ParentId { get; set; }
public MenuItem? Parent { get; set; }
public List Children { get; set; } = new();
}

using Microsoft.EntityFrameworkCore;

public class ApplicationContext : DbContext
{
public DbSet MenuItems { get; set; } = null!;

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=helloapp.db");
}
}

using (ApplicationContext db = new ApplicationContext())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();

MenuItem file = new MenuItem { Title = "File" };
MenuItem edit = new MenuItem { Title = "Edit" };
MenuItem open = new MenuItem { Title = "Open", Parent = file };
MenuItem save = new MenuItem { Title = "Save", Parent = file };

MenuItem copy = new MenuItem { Title = "Copy", Parent = edit };
MenuItem paste = new MenuItem { Title = "Paste", Parent = edit };

db.MenuItems.AddRange(file, edit, open, save, copy, paste);
db.SaveChanges();
}

using (ApplicationContext db = new ApplicationContext())
{
var menuItems = db.MenuItems.ToList();
Console.WriteLine("All Menu:");

foreach (MenuItem m in menuItems)
{
Console.WriteLine(m.Title);
}

Console.WriteLine();

var fileMenu = db.MenuItems.FirstOrDefault(m => m.Title == "File");

if (fileMenu != null)
{
Console.WriteLine(fileMenu.Title);

foreach (var m in fileMenu.Children)
{
Console.WriteLine($"---{m.Title}");
}
}
}
Так похоже, что проблема в чем-то другом, поэтому пока не буду говорить, какой провайдер БД я использую.
Классы контекста базы данных с код заполнения:

Код: Выделить всё

public class RemoteDatabaseContext : DatabaseContext
{
public RemoteDatabaseContext()
{
// base.Database.EnsureDeleted();
base.Database.EnsureCreated();
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);

optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Username=postgres;Password=pass1234");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.
Entity().
HasData(TaskCustomFolderModel.FromBusinessRulesEntities(SampleTasksCustomFoldersRepository.TaskCustomFolders));
}
}

public abstract class DatabaseContext : Microsoft.EntityFrameworkCore.DbContext
{
public DbSet TasksModels { get; internal set; } = null!;
public DbSet TaskCustomFoldersModels { get; internal set; } = null!;
public DbSet LocationModels { get; internal set; } = null!;

public DbSet PeopleModels { get; internal set; } = null!;
}
Почему вы устанавливаете IsRequired через Fluent API, а максимальное количество символов рассчитывается через атрибуты?
Краткий ответ: из-за чистой архитектуры и потому что TaskCustomFolderModel и другие модели Entity Framework должны измениться из-за изменений в бизнес-правилах без ручного редактирования кода, если это возможно.
Развернутый ответ:
Согласно чистой архитектуре, бизнес-правила НЕ должны зависеть от каких-либо платформ, в то время как TaskCustomFolderModel зависит. Итак, помимо класса TaskCustomFolderModel есть еще один из бизнес-правил:

Код: Выделить всё

public class TaskCustomFolder
{
public required string ID { get; init; }

/* ━━━ Title ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
public required string title { get; set; }

public abstract record Title
{
public const bool IS_REQUIRED = true;
public const byte MINIMAL_CHARACTERS_COUNT = 1;
public const byte MAXIMAL_CHARACTERS_COUNT = Byte.MaxValue;
}

/* ━━━ Relatives ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
/* ─── Parent ───────────────────────────────────────────────────────────────────────────────────────────────────── */
public TaskCustomFolder? parent { get; set; }

public abstract record Parent
{
public const bool IS_REQUIRED = false;
}

/* ─── Children ─────────────────────────────────────────────────────────────────────────────────────────────────── */
public List children { get; set; } = [];

public abstract record Children
{
public const bool IS_REQUIRED = false;
}

/* ━━━ Order Among Siblings ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
public required uint orderAmongSiblings { get; set; }

public abstract record OrderAmongSiblings
{
public const bool IS_REQUIRED = true;
public const uint MINIMAL_VALUE = 1;
public const uint MAXIMAL_VALUE = UInt32.MaxValue;
}

/* ━━━ Path ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
public string path
{
get
{
TaskCustomFolder currentDepthLevel = this;
List pathSegments = [ currentDepthLevel.title ];

while (currentDepthLevel.parent is not null)
{
currentDepthLevel = currentDepthLevel.parent;
pathSegments.Add(currentDepthLevel.title);
}

return String.Join("/", pathSegments);
}
}
}
Если мы изменим MAXIMAL_CHARACTERS_COUNT какого-либо свойства, нет необходимости редактировать TaskCustomFolderModel, поскольку он ссылается на бизнес-правила через атрибут:

Код: Выделить всё

public class TaskCustomFolderModel
{
// ...   [System.ComponentModel.DataAnnotations.MaxLength(TaskCustomFolder.Title.MAXIMAL_CHARACTERS_COUNT)]
public string Title { get; set; } = null!;
}
Однако не существует атрибута Required, который принимает такой параметр, как TaskCustomFolder.Children.IS_REQUIRED. Так что это можно сделать только с помощью свободного API (или создания нового атрибута).


Подробнее здесь: https://stackoverflow.com/questions/791 ... ng-to-seed
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»