如何将WPF模型映射到实体框架模型

时间:2020-03-14 11:06:34

标签: c# wpf entity-framework

我很难将数据库实体映射到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在其中具有公共DbUser CurrentUser,因此当我将Series映射到DbSeries时,我需要用User填充它,然后转换为DbUser,这会导致stackoverflow异常(SingleSeriesToSingleDbSeries称为自身)实现目标的更好方法?

1 个答案:

答案 0 :(得分:1)

据我所见,您的方法有点倒退。通常,我将从域模型(实体,映射到数据库表)开始,然后从中定义视图的视图模型。这里重要的是,视图模型通常不会将一对一映射到域模型。视图模型满足了视图的需求,因此尽管我可以采用实体的1对1表示形式(使用NotifyPropertyChanged等设置)并将控件等绑定到实体的层次结构,但最好是简单定义仅视图所需的字段(无论它们来自实体模型的何处),并让映射器将这些值从实体转置到视图模型。这样可以减少从服务器发送到客户端(以及从客户端发送回)的有效负载,并且可以避免暴露比您的视图需要了解的更多有关您的域的信息。 (在调试工具中可见)

Automapper绝对是执行此操作的首选工具,这主要是因为它的ProjectTo方法可以与EF的IQueryable配合使用来组成查询,以仅从视图模型的实体图中拉回字段,以及任何需要的相关视图模型。在某些情况下,您将需要为非常规映射设置一些配置,但是执行映射的调用是1-liner。

在编辑实体时,这些实体将更多地是实体与视图模型之间的1对1表示形式,但是在大多数情况下,我将编辑视为单个级别。例如,如果我有一个包含相关订单行,产品,客户等的订单,那么创建订单视图模型将可能包含相关详细信息的整个层次结构(具有产品ID,数量等的订单行)。不必要。根据用户可以执行的操作,视图模型将只包含可以更新的相关字段,而不是尝试来回传递完整订单的表示形式。来回传递完整订单的问题是传输数据包大小的考虑因素,以及由于篡改而不应更新的数据有更新的机会。 (不鼓励为什么来回传递实体并使用Attach / Update。)Automapper可以协助将数据映射回实体,但是我倾向于自己亲自编写更新映射。无论如何都要验证和检查这些值。我不经常使用Automapper映射回实体,因为我不想信任用户ID /客户ID或从客户端返回的东西。这些应该基于会话状态加载。尽管可以可靠地进行验证,但是Automapper可以轻松地帮助您将Insert类型映射回Entities。