我很难将数据库实体映射到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称为自身)实现目标的更好方法?
答案 0 :(得分:1)
据我所见,您的方法有点倒退。通常,我将从域模型(实体,映射到数据库表)开始,然后从中定义视图的视图模型。这里重要的是,视图模型通常不会将一对一映射到域模型。视图模型满足了视图的需求,因此尽管我可以采用实体的1对1表示形式(使用NotifyPropertyChanged等设置)并将控件等绑定到实体的层次结构,但最好是简单定义仅视图所需的字段(无论它们来自实体模型的何处),并让映射器将这些值从实体转置到视图模型。这样可以减少从服务器发送到客户端(以及从客户端发送回)的有效负载,并且可以避免暴露比您的视图需要了解的更多有关您的域的信息。 (在调试工具中可见)
Automapper绝对是执行此操作的首选工具,这主要是因为它的ProjectTo
方法可以与EF的IQueryable
配合使用来组成查询,以仅从视图模型的实体图中拉回字段,以及任何需要的相关视图模型。在某些情况下,您将需要为非常规映射设置一些配置,但是执行映射的调用是1-liner。
在编辑实体时,这些实体将更多地是实体与视图模型之间的1对1表示形式,但是在大多数情况下,我将编辑视为单个级别。例如,如果我有一个包含相关订单行,产品,客户等的订单,那么创建订单视图模型将可能包含相关详细信息的整个层次结构(具有产品ID,数量等的订单行)。不必要。根据用户可以执行的操作,视图模型将只包含可以更新的相关字段,而不是尝试来回传递完整订单的表示形式。来回传递完整订单的问题是传输数据包大小的考虑因素,以及由于篡改而不应更新的数据有更新的机会。 (不鼓励为什么来回传递实体并使用Attach
/ Update
。)Automapper可以协助将数据映射回实体,但是我倾向于自己亲自编写更新映射。无论如何都要验证和检查这些值。我不经常使用Automapper映射回实体,因为我不想信任用户ID /客户ID或从客户端返回的东西。这些应该基于会话状态加载。尽管可以可靠地进行验证,但是Automapper可以轻松地帮助您将Insert类型映射回Entities。