Как сопоставить модель WPF модели Entity Framework - PullRequest
0 голосов
/ 14 марта 2020

У меня проблемы с отображением объектов базы данных в модели WPF и обратно. Мое приложение работает с моделью WPF, которая реализует интерфейс INotifyPropertyChanged. Каждый раз, когда мне нужно сохранить его в базе данных, мне нужно сопоставить мою модель WPF с моделью базы данных. Я создал сложный картограф для этого, но в итоге у него была циклическая зависимость. Как я могу эффективно сопоставить мою модель WPF с моделью базы данных? Моя модель базы данных:

public class DbUser
{
    [Key]
    public Guid UserId { get; set; }
    public List<DbSeries> UserSeries { get; set; }
}

public class DbSeries
{
    [Key]
    public Guid SeriesId { get; set; }

    public List<DbDropPhoto> DropPhotosSeries { get; set; }
    public virtual DbReferencePhoto ReferencePhotoForSeries { get; set; }

    public Guid CurrentUserId { get; set; }
    public DbUser CurrentUser { get; set; }
}

public class DbReferencePhoto
{
    [Key]
    public Guid ReferencePhotoId { get; set; }

    public virtual DbSimpleLine SimpleLine { get; set; }

    public virtual DbSeries Series { get; set; }
}

public class DbDropPhoto
{
    [Key]
    public Guid DropPhotoId { get; set; }

    public virtual DbSimpleLine SimpleHorizontalLine { get; set; }
    public virtual DbSimpleLine SimpleVerticalLine { get; set; }

    public virtual DbDrop Drop { get; set; }

    public Guid CurrentSeriesId { get; set; }
    public DbSeries CurrentSeries { get; set; }
}

public class DbDrop
{
    [Key]
    public Guid DropId { get; set; }

    public virtual DbDropPhoto DropPhoto { get; set; }
}

public class DbSimpleLine
{
    [Key]
    public Guid SimpleLineId { get; set; }

    public virtual DbReferencePhoto ReferencePhoto { get; set; }
    public virtual DbDropPhoto DropPhotoHorizontalLine { get; set; }
    public virtual DbDropPhoto DropPhotoVerticalLine { get; set; }
}

конфигурация беглого API выглядит следующим образом:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<DbUser>()
            .HasMany(s => s.UserSeries)
            .WithRequired(g => g.CurrentUser)
            .HasForeignKey(s => s.CurrentUserId);

        modelBuilder.Entity<DbSeries>()
            .HasMany(s => s.DropPhotosSeries)
            .WithRequired(g => g.CurrentSeries)
            .HasForeignKey(s => s.CurrentSeriesId)
            .WillCascadeOnDelete();

        modelBuilder.Entity<DbSeries>()
            .HasRequired(s => s.ReferencePhotoForSeries)
            .WithRequiredPrincipal(ad => ad.Series);

        modelBuilder.Entity<DbDropPhoto>()
            .HasRequired(s => s.Drop)
            .WithRequiredPrincipal(ad => ad.DropPhoto);

        modelBuilder.Entity<DbDropPhoto>()
            .HasRequired(s => s.SimpleHorizontalLine)
            .WithRequiredPrincipal(ad => ad.DropPhotoHorizontalLine);

        modelBuilder.Entity<DbDropPhoto>()
            .HasRequired(s => s.SimpleVerticalLine)
            .WithRequiredPrincipal(ad => ad.DropPhotoVerticalLine);

        modelBuilder.Entity<DbReferencePhoto>()
            .HasRequired(s => s.SimpleLine)
            .WithRequiredPrincipal(ad => ad.ReferencePhoto);
    }

моя модель WPF:

public class User : INotifyPropertyChanged
{
    public User()
    {
        _userSeries = new ObservableCollection<Series>();
        _userSeries.CollectionChanged += _userSeries_CollectionChanged;
    }

    private void _userSeries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAnySelectedSeriesCanDrawPlot)));
    }

    public Guid UserId { get; set; }

    private ObservableCollection<Series> _userSeries;
    public ObservableCollection<Series> UserSeries
    {
        get
        {
            return _userSeries;
        }
        set
        {
            _userSeries = value;
            OnPropertyChanged(new PropertyChangedEventArgs("UserSeries"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(this, e);
    }
}

public class Series : INotifyPropertyChanged
{
    private Guid _currentUserId;
    public Guid CurrentUserId
    {
        get
        {
            return _currentUserId;
        }
        set
        {
            _currentUserId = value;
            OnPropertyChanged(new PropertyChangedEventArgs("CurrentUserId"));
        }
    }

    private User _currentUser;
    public User CurrentUser
    {
        get
        {
            return _currentUser;
        }
        set
        {
            _currentUser = value;
            OnPropertyChanged(new PropertyChangedEventArgs("CurrentUser"));
        }
    }

    public Series()
    {
        _dropPhotosSeries = new ObservableCollection<DropPhoto>();
        _dropPhotosSeries.CollectionChanged += _dropPhotosSeries_CollectionChanged;
    }

    private void _dropPhotosSeries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanDrawPlot)));
        CurrentUser.OnPropertyChanged(new PropertyChangedEventArgs(nameof(User.IsAnySelectedSeriesCanDrawPlot)));
    }

    public Guid SeriesId { get; set; }

    private ObservableCollection<DropPhoto> _dropPhotosSeries;
    public ObservableCollection<DropPhoto> DropPhotosSeries
    {
        get
        {
            return _dropPhotosSeries;
        }
        set
        {
            _dropPhotosSeries = value;
            OnPropertyChanged(new PropertyChangedEventArgs("DropPhotosSeries"));
        }
    }

    private ReferencePhoto _referencePhotoForSeries;
    public ReferencePhoto ReferencePhotoForSeries
    {
        get
        {
            return _referencePhotoForSeries;
        }
        set
        {
            _referencePhotoForSeries = value;
            OnPropertyChanged(new PropertyChangedEventArgs("ReferencePhotoForSeries"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(IntervalBetweenPhotos))
            OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanDrawPlot)));

        if (e.PropertyName == nameof(IsChecked))
            CurrentUser.OnPropertyChanged(new PropertyChangedEventArgs(nameof(User.IsAnySelectedSeriesCanDrawPlot))); ;

        PropertyChanged?.Invoke(this, e);
    }
}

public class ReferencePhoto : INotifyPropertyChanged
{
    private Series _series;
    public Series Series
    {
        get
        {
            return _series;
        }
        set
        {
            _series = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Series"));
        }
    }

    public Guid ReferencePhotoId { get; set; }

    private SimpleLine _simpleLine;
    public SimpleLine SimpleLine
    {
        get
        {
            return _simpleLine;
        }
        set
        {
            _simpleLine = value;
            OnPropertyChanged(new PropertyChangedEventArgs("SimpleLine"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(this, e);
    }
}

public class DropPhoto : INotifyPropertyChanged
{
    private Guid _currentSeriesId;
    public Guid CurrentSeriesId
    {
        get
        {
            return _currentSeriesId;
        }
        set
        {
            _currentSeriesId = value;
            OnPropertyChanged(new PropertyChangedEventArgs("CurrentSeriesId"));
        }
    }

    private Series _currentSeries;
    public Series CurrentSeries
    {
        get
        {
            return _currentSeries;
        }
        set
        {
            _currentSeries = value;
            OnPropertyChanged(new PropertyChangedEventArgs("CurrentSeries"));
        }
    }

    public Guid DropPhotoId { get; set; }

    private SimpleLine _simpleHorizontalLine;
    public SimpleLine SimpleHorizontalLine
    {
        get
        {
            return _simpleHorizontalLine;
        }
        set
        {
            _simpleHorizontalLine = value;
            OnPropertyChanged(new PropertyChangedEventArgs("SimpleHorizontalLine"));
        }
    }

    private SimpleLine _simpleVerticalLine;
    public SimpleLine SimpleVerticalLine
    {
        get
        {
            return _simpleVerticalLine;
        }
        set
        {
            _simpleVerticalLine = value;
            OnPropertyChanged(new PropertyChangedEventArgs("SimpleVerticalLine"));
        }
    }

    private Drop _drop;
    public Drop Drop
    {
        get
        {
            return _drop;
        }
        set
        {
            _drop = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Drop"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(this, e);
    }
}

public class SimpleLine
{
    public Guid SimpleLineId { get; set; }

    public ReferencePhoto ReferencePhoto { get; set; }
    public DropPhoto DropPhotoHorizontalLine { get; set; }
    public DropPhoto DropPhotoVerticalLine { get; set; }
}

Например, мне нужно создать новую Серии. Мое приложение реализует шаблон хранилища. Метод создания серий выглядит следующим образом:

    public async Task CreateSeries(DbSeries series)
    {
        using (var context = new DDropContext())
        {
            var createdSeries = context.Series.Add(series);

            await context.SaveChangesAsync();
        }
    }

Сначала я создаю новую серию:

            Series seriesToAdd = new Series()
            {
                SeriesId = Guid.NewGuid(),
                Title = seriesTitle,                    
                AddedDate = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"),
                ReferencePhotoForSeries = new ReferencePhoto()
                {
                    ReferencePhotoId = Guid.NewGuid(),
                    Series = CurrentSeries,
                    SimpleLine = new SimpleLine { SimpleLineId = Guid.NewGuid()}
                },
                CurrentUser = User,
                CurrentUserId = User.UserId
            };

Затем мне нужно сопоставить ее с DbSeries и создать новую DbSeries:

            try
            {

                await _dDropRepository.CreateSeries(DDropDbEntitiesMapper.SingleSeriesToSingleDbSeries(seriesToAdd, User));

            }
            catch (Exception)
            {

            }   

Чтобы отобразить Series в DbSeries, я использую очень сложное средство отображения, которое не работает должным образом:

    public static DbSeries SingleSeriesToSingleDbSeries(Series userSeries, User user)
    {
        DbSeries singleSeries = new DbSeries();
        List<DbDropPhoto> dropPhotosSeries = new List<DbDropPhoto>();

        foreach (var dropPhoto in userSeries.DropPhotosSeries)
        {
            DbDropPhoto newDbDropPhoto = new DbDropPhoto()
            {
                Name = dropPhoto.Name,
                Content = dropPhoto.Content,
                AddedDate = dropPhoto.AddedDate,
                DropPhotoId = dropPhoto.DropPhotoId,
                XDiameterInPixels = dropPhoto.XDiameterInPixels,
                YDiameterInPixels = dropPhoto.YDiameterInPixels,
                ZDiameterInPixels = dropPhoto.ZDiameterInPixels,
                CurrentSeries = SingleSeriesToSingleDbSeries(userSeries, user),
                CurrentSeriesId = userSeries.SeriesId,

            };

            DbSimpleLine newHorizontalDbSimpleLine = new DbSimpleLine
            {
                X1 = dropPhoto.SimpleHorizontalLine.X1,
                X2 = dropPhoto.SimpleHorizontalLine.X2,
                Y1 = dropPhoto.SimpleHorizontalLine.Y1,
                Y2 = dropPhoto.SimpleHorizontalLine.Y2,
                DropPhotoHorizontalLine = newDbDropPhoto,
                SimpleLineId = dropPhoto.SimpleHorizontalLine.SimpleLineId,
            };

            DbSimpleLine newVerticalDbSimpleLine = new DbSimpleLine
            {
                X1 = dropPhoto.SimpleVerticalLine.X1,
                X2 = dropPhoto.SimpleVerticalLine.X2,
                Y1 = dropPhoto.SimpleVerticalLine.Y1,
                Y2 = dropPhoto.SimpleVerticalLine.Y2,
                DropPhotoVerticalLine = newDbDropPhoto,
                SimpleLineId = dropPhoto.SimpleHorizontalLine.SimpleLineId,
            };

            DbDrop newDbDrop = new DbDrop()
            {
                DropId = dropPhoto.Drop.DropId,
                RadiusInMeters = dropPhoto.Drop.RadiusInMeters,
                VolumeInCubicalMeters = dropPhoto.Drop.VolumeInCubicalMeters,
                XDiameterInMeters = dropPhoto.Drop.XDiameterInMeters,
                YDiameterInMeters = dropPhoto.Drop.YDiameterInMeters,
                ZDiameterInMeters = dropPhoto.Drop.ZDiameterInMeters,
                DropPhoto = newDbDropPhoto,
            };

            newDbDropPhoto.Drop = newDbDrop;
            newDbDropPhoto.SimpleHorizontalLine = newHorizontalDbSimpleLine;
            newDbDropPhoto.SimpleVerticalLine = newVerticalDbSimpleLine;

            dropPhotosSeries.Add(newDbDropPhoto);
        }

        if (userSeries.ReferencePhotoForSeries != null)
        {
            var referencePhoto = new DbReferencePhoto
            {
                Content = userSeries.ReferencePhotoForSeries.Content,
                Name = userSeries.ReferencePhotoForSeries.Name,
                PixelsInMillimeter = userSeries.ReferencePhotoForSeries.PixelsInMillimeter,
                ReferencePhotoId = userSeries.ReferencePhotoForSeries.ReferencePhotoId,
                Series = singleSeries,
            };

            var simpleLineForReferencePhoto = new DbSimpleLine
            {
                X1 = userSeries.ReferencePhotoForSeries.SimpleLine.X1,
                X2 = userSeries.ReferencePhotoForSeries.SimpleLine.X2,
                Y1 = userSeries.ReferencePhotoForSeries.SimpleLine.Y1,
                Y2 = userSeries.ReferencePhotoForSeries.SimpleLine.Y2,
                ReferencePhoto = referencePhoto,
                SimpleLineId = userSeries.ReferencePhotoForSeries.SimpleLine.SimpleLineId,
            };

            referencePhoto.SimpleLine = simpleLineForReferencePhoto;

            singleSeries.ReferencePhotoForSeries = referencePhoto;
        }

        singleSeries.DropPhotosSeries = dropPhotosSeries;
        singleSeries.IntervalBetweenPhotos = userSeries.IntervalBetweenPhotos;
        singleSeries.AddedDate = userSeries.AddedDate;
        singleSeries.SeriesId = userSeries.SeriesId;
        singleSeries.Title = userSeries.Title;
        singleSeries.CurrentUser = UserToDbUser(user);
        singleSeries.CurrentUserId = user.UserId;

        return singleSeries;
    }

Основная проблема заключается в том, что в DbSeries есть publi c DbUser CurrentUser, поэтому, когда я сопоставить Series с DbSeries. Мне нужно заполнить его User, чтобы я преобразовал его в DbUser, что приводит к исключению stackoverflow (сам метод SingleSeriesToSingleDbSeries вызывает). Есть ли лучший способ достичь моей цели?

1 Ответ

1 голос
/ 14 марта 2020

Из того, что я вижу, твой подход немного задом наперед. Обычно я начинаю с моделей предметной области (сущностей, сопоставленных с таблицами БД), а затем определяю модели представлений для представления. Здесь важно то, что модели представлений, как правило, не отображают 1-к-1 в доменные модели. Модели представления удовлетворяют потребности представления, поэтому, хотя я могу взять представление сущности 1-к-1 (настроенное с помощью NotifyPropertyChanged et c.) И связать элементы управления и тому подобное с этой иерархией сущностей, лучше, если Я просто определяю только те поля, которые нужны представлению (независимо от того, откуда в модели сущностей они берутся), и позволяю преобразователю перенести эти значения из сущностей в модель представления. Это уменьшает полезную нагрузку, отправляемую с сервера клиенту (и обратно), и не дает больше информации о вашем домене, чем необходимо знать вашему представлению. (Видно в инструментах отладки)

Automapper определенно является предпочтительным инструментом для выполнения этого в значительной степени благодаря его методу ProjectTo, который может работать с IQueryable EF для составления запросов для получения только полей из сущности. график, необходимый для модели представления и любых связанных моделей представления. В некоторых случаях вам нужно будет настроить некоторую конфигурацию для нетрадиционных отображений, но вызов для выполнения сопоставления является однострочным.

Когда дело доходит до редактирования сущностей, это будет скорее представление 1: 1 между сущностью и моделью представления, но по большей части я рассматриваю редактирование на основе одного уровня. Например, если у меня есть Заказ со связанными строками заказа, продуктами, клиентом и т. Д. c. тогда модель представления создания заказа, скорее всего, будет содержать целую иерархию связанных деталей (строки заказа с идентификатором продукта, количеством и т. д. c.) Действие Изменить заказ не обязательно. В зависимости от того, какие действия может выполнить пользователь, модель представления будет просто содержать соответствующие поля, которые могут быть обновлены, а не пытаться передавать назад и вперед представление полного заказа. Проблема с передачей полного заказа туда-обратно связана с размером пакета передачи, а также с возможностью для данных, которые не должны обновляться, обновляться из-за фальсификации. (Почему отсылка объектов назад и вперед и использование Attach / Update должны сильно обескураживать.) Automapper может помочь с отображением данных обратно на объекты, но я, как правило, сам пишу сопоставления обновлений вручную, так как собираюсь быть проверка и проверка значений в любом случае. Я не часто использую Automapper для сопоставления с сущностями, потому что не хочу доверять таким вещам, как идентификаторы пользователей / идентификаторы клиентов или тому подобное, возвращаемое от клиента. Они должны быть загружены в зависимости от состояния сеанса. Хотя при условии, что у вас есть надежная проверка на месте, Automapper может легко помочь с сопоставлением типов вставки обратно в сущности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...