Не видно связанных данных

111
05 октября 2019, 15:20

Модель:

using MySql.Data.EntityFrameworkCore.DataAnnotations;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Messager.Models
{
    [MySqlCharset("utf-8")]
    public class Message
    {
        public int MessageId { get; set; }
        [Column("Body")]
        public string BodyMassage { get; set; }
        //Время, когда отправлено
        public DateTime TimeSend { get; set; }
        //Время, когда принято
        public DateTime TimeCame { get; set; }
        //Время, когда прочитано
        public DateTime TimeRead { get; set; }
        [NotMapped]
        public virtual IList<MessageCustomUser> MessageCustomUsers { get; set; }
    }
    public class CustomUser
    {
        public int CustomUserId { get; set; }
        public string Login { get; set; }
        public string Username { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public virtual IList<MessageCustomUser> MessageCustomUsers { get; set; }
    }
    public class MessageCustomUser
    {
        public int MessageId { get; set; }
        public Message Message { get; set; }
        public int CustomUserId { get; set; }
        public CustomUser CustomUser { get; set; }
    }
}

контекст:

using Messager.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Messager.Data
{
    public class dataContext : DbContext
    {
        public dataContext(DbContextOptions<dataContext> options) : base(options)
        {
        }
        public DbSet<Message> Messages { get; set; }
        public DbSet<CustomUser> CustomUsers { get; set; }
        public DbSet<MessageCustomUser> MessageCustomUser { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<MessageCustomUser>().HasKey(mcu => new { mcu.CustomUserId, mcu.MessageId });
            modelBuilder.Entity<MessageCustomUser>()
                .HasOne<CustomUser>(sc => sc.CustomUser)
                .WithMany(s => s.MessageCustomUsers)
                .HasForeignKey(sc => sc.CustomUserId);

            modelBuilder.Entity<MessageCustomUser>()
                .HasOne<Message>(sc => sc.Message)
                .WithMany(s => s.MessageCustomUsers)
                .HasForeignKey(sc => sc.MessageId);
        }
    }
}

действие:

return Ok(context.MessageCustomUser.Where(x => x.CustomUserId == 2).FirstOrDefault());

данные:

результат:

Никак не разберусь со связью в ef, почему свойства Message и CustomUser пустые

UPDATE

действие:

return Ok(context.MessageCustomUser.Where(x => x.CustomUserId == 2).Include(x => x.Message).FirstOrDefault());

по адресу About:

Тот же самый адрес в постмене:

Нашел проблему:

Получается когда я делаю var messages = context.CustomUsers.Include(dd => dd.MessageCustomUsers).ThenInclude( p => p.Message).ToList(); то все нормально. Просто не выводит messages т к попадает в бесконечный цикл по ссылкам. Получается если я пишу так:

public ActionResult About()
        {
            //ViewData["Message"] = "Your application description page.";
            var messages = context.CustomUsers.Include(dd => dd.MessageCustomUsers).ThenInclude( p => p.Message).ToList();
            List<string> str_ = new List<string>();
            foreach(var m in messages)
            {
                var mess = m.MessageCustomUsers.Select(qwe => qwe.Message).ToList();
                foreach (var mm in mess)
                {
                    str_.Add($"логин: {m.Login} Сообщение: {mm.BodyMassage}");
                }
            }
            return Ok(str_);
        }

то все выводит ок:

Вопрос: как сделать чтобы компилятор не скитался по ссылкам и вывести все messages в полноценном виде (все поля пользователя и связанного с ним сообщения)?

Answer 1

Создаем контекст данных. Я взял ваш.

Модели:

[MySqlCharset("utf-8")]
public class Message
{
    public int MessageId { get; set; }
    [Column("Body")]
    public string BodyMassage { get; set; }
    //Время, когда отправлено
    public DateTime TimeSend { get; set; }
    //Время, когда принято
    public DateTime TimeCame { get; set; }
    //Время, когда прочитано
    public DateTime TimeRead { get; set; }
    [NotMapped]
    public virtual IList<MessageCustomUser> MessageCustomUsers { get; set; }
}
public class CustomUser
{
    public int CustomUserId { get; set; }
    public string Login { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public virtual IList<MessageCustomUser> MessageCustomUsers { get; set; }
}
public class MessageCustomUser
{
    public int MessageId { get; set; }
    public Message Message { get; set; }
    public int CustomUserId { get; set; }
    public CustomUser CustomUser { get; set; }
}

Таблица MessageCustomUser нам нужна, так как EFCore не сильно дружит со связями many-to-many.

public class MyAppDbContext : DbContext
{
    public MyAppDbContext(DbContextOptions<MyAppDbContext> options) : base(options)
    {
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<MessageCustomUser>().HasKey(mcu => new { mcu.CustomUserId, mcu.MessageId });
        modelBuilder.Entity<MessageCustomUser>()
            .HasOne<CustomUser>(sc => sc.CustomUser)
            .WithMany(s => s.MessageCustomUsers)
            .HasForeignKey(sc => sc.CustomUserId);
        modelBuilder.Entity<MessageCustomUser>()
            .HasOne<Message>(sc => sc.Message)
            .WithMany(s => s.MessageCustomUsers)
            .HasForeignKey(sc => sc.MessageId);
    }
    public DbSet<Message> Messages { get; set; }
    public DbSet<CustomUser> CustomUsers { get; set; }
    public DbSet<MessageCustomUser> MessageCustomUser { get; set; }
}

Далее, нам нужны модели для отдачи с контроллера. Не путайте объекты, что вы читаете из БД и объекты, что вы отдаете на клиент. Выглядят они так

public class CustomUserModel
{
    public int CustomUserId { get; set; }
    public string Login { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public List<MessageModel> Messages { get; set; }
}
public class MessageModel
{
    public int MessageId { get; set; }
    public string BodyMassage { get; set; }
    //Время, когда отправлено
    public DateTime TimeSend { get; set; }
    //Время, когда принято
    public DateTime TimeCame { get; set; }
    //Время, когда прочитано
    public DateTime TimeRead { get; set; }
}

Далее, в классе Startup

Конфиг для доступа к БД

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<MyAppDbContext>(options =>
                options.UseMySql("server=localhost; database=sampledb; port=3306; user=root;"));
        services.AddMvc();
        // проблема с циклическими ссылками решается так, это если вы таки БД объекты решите сериализовать 
        // .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
    }

Конфиг для контроллеров и маппинга (использую AutoMapper)

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller}/{action}");
        });
        Mapper.Initialize(config =>
        {
            config.CreateMap<Message, MessageModel>();
            config.CreateMap<CustomUser, CustomUserModel>()
                .AfterMap((dbUser, model) =>
                {
                    model.Messages = dbUser.MessageCustomUsers?
                                .Select(m => Mapper.Map<MessageModel>(m.Message))?
                                .ToList();
                });
        });
    }

Пример контроллера

public class HomeController : Controller
{
    MyAppDbContext _ctx;
    public HomeController(MyAppDbContext ctx)
    {
        _ctx = ctx;
    }
    public async Task<IActionResult> Index()
    {
        return Ok( 
                (await _ctx.CustomUsers
                .Include(x=>x.MessageCustomUsers)
                .ThenInclude(x=>x.Message)
                .ToArrayAsync())
                .Select(c=>Mapper.Map<CustomUserModel>(c))
                .ToArray());
    }
}

После этого добавляем миграцию

dotnet ef migrations add initial

Обновляем БД

dotnet ef database update

Далее, открываем базу, накидываем данные, запускаем вебсайт

dotnet run

Идем поглядеть, что же там в контроллере

http://localhost:5000/home/index

Получаем на выходе

[
    {
        "customUserId": 1,
        "login": "11",
        "username": "222",
        "email": "333",
        "password": "444",
        "messages": [
            {
                "messageId": 1,
                "bodyMassage": "sfsdfsdf",
                "timeSend": "2019-01-01T00:00:00",
                "timeCame": "2019-01-01T00:00:00",
                "timeRead": "2019-01-01T00:00:00"
            },
            {
                "messageId": 2,
                "bodyMassage": "sdfsdf",
                "timeSend": "2019-01-01T00:00:00",
                "timeCame": "2019-01-01T00:00:00",
                "timeRead": "2019-01-01T00:00:00"
            }
        ]
    },
    {
        "customUserId": 2,
        "login": "222",
        "username": "444",
        "email": "666",
        "password": "777",
        "messages": [
            {
                "messageId": 1,
                "bodyMassage": "sfsdfsdf",
                "timeSend": "2019-01-01T00:00:00",
                "timeCame": "2019-01-01T00:00:00",
                "timeRead": "2019-01-01T00:00:00"
            },
            {
                "messageId": 2,
                "bodyMassage": "sdfsdf",
                "timeSend": "2019-01-01T00:00:00",
                "timeCame": "2019-01-01T00:00:00",
                "timeRead": "2019-01-01T00:00:00"
            }
        ]
    }
]
READ ALSO
Построение график в WPF Toolkit

Построение график в WPF Toolkit

Строю график, отражающий динамику прохождения тестов пользователемНа оси Y отражается коэффициент(результат), на Х - дата выполнения

112
web.config MVC запись

web.config MVC запись

Я хочу перезаписать данные в файл webconfig из секции

103
Live Charts Обработка нажатия

Live Charts Обработка нажатия

Работаю в Wpf с библиотекой Live ChartsНужно обработать нажатие на квадрат,чтобы квадрат заполнился или поменял цвет! Достиг только того что при...

138