利用INotifyPropertyChanged 接口做商品属性变更记录
在开发企业应用的时候,特别是涉及到敏感数据的应用,如财务系统、物流系统,我们往往会有这样的需求:对于数据库中的每一条记录的变更,都要有一个明确的日志,记录数据的变更人、时间、变更的内容。
本文我们来处理这个问题。
假定我们在做的是一个电子商务的项目,项目中的商品信息比较重要,商品的更改需要记录日志。
首先我们来看下商品的实体组成,假定商品是由 商品基本信息 商品详细信息 商品付款方式 商品联系人 商品图片信息 以及 商品库存和价格信息 构成。此处没有涉及到商品库存和价格信息
上图涉及到7个类,分别是 ProductBase 商品基类 LineProductBasicInfo 线路商品基本信息 CommodityPayment 付款信息 ProductsContact 商品联系信息 LineProductDetail 线路商品详细信息 ProductImgInfo 商品图片信息 AuditLog 商品更改记录
现在我们只对 商品的联系信息和商品的付款信息的更改记录日志。我们需要处理的是当更改商品的联系信息和商品的付款信息的时候,通过INotifyPropertyChanged接口来构造AuditLog。然后通过AuditLogRepository来存储变更记录。描述的是如此的苍白无力阿,还是来点代码比较实在阿
ProductBases [ActiveRecord("ProductBases"), JoinedBase] public class ProductBase : ActiveRecordBase<ProductBase>, IAggregateRoot { #region Field private Int64 _id; private string _baseTitle; #endregion #region Property [PrimaryKey("Id")] public Int64 Id { get { return _id; } set { _id = value; } } [Property] public string BaseTitle { get { return _baseTitle; } set { _baseTitle = value; } } /// <summary> /// 商品图片信息 /// </summary> [HasMany(Inverse = true)] public IList<ProductImgInfo> ProductImgInfos { get; set; } [HasMany(Inverse = true)] public IList<AuditLog> AuditLogs { get; set; } #endregion #region Constructor public ProductBase() { } public ProductBase(string title) { this._baseTitle = title; } #endregion }
LineProductBasicInfo [ActiveRecord("LineProductBasicInfos")] public class LineProductBasicInfo : ProductBase { #region Field private Int64 _ProductId; private string _title; private LineTypes _lineType; private DateTime _departureDate; private DateTime _returnDate; private int _travelDays; private string _departureCity; private string _purposeOfCity; private TransportationTypes _tripTransportation; private TransportationTypes _returnTransportation; private DateTime _createDate; private AccountUser _accountUser; private ProductsContact _productsContact; private LineProductDetail _lineProductDetail; private CommodityPayment _commodityPayment; private ProductStatus _productStatus; #endregion #region Property /// <summary> /// 实体标识 /// </summary> [JoinedKey("Id")] public Int64 ProductId { get { return _ProductId; } set { _ProductId = value; } } /// <summary> /// 线路名称 /// </summary> [Property] [Required] [Display(Name = "商品名称")] public string Title { get { return _title; } set { _title = value; } } /// <summary> /// 线路类型 /// </summary> [Property] [Display(Name = "线路类型")] public LineTypes LineType { get { return _lineType; } set { _lineType = value; } } /// <summary> /// 出发日期 /// </summary> [Property] [Display(Name = "出发日期")] [Required] public DateTime DepartureDate { get { return _departureDate; } set { _departureDate = value; } } /// <summary> /// 返程日期 /// </summary> [Property] [Display(Name = "返程日期")] [Required] public DateTime ReturnDate { get { return _returnDate; } set { _returnDate = value; } } /// <summary> /// 行程天数 /// </summary> [Property] [Display(Name = "行程天数")] public int TravelDays { get { return _travelDays; } set { _travelDays = value; } } /// <summary> /// 发团城市 /// </summary> [Property] [Display(Name = "发团城市")] [Required] public string DepartureCity { get { return _departureCity; } set { _departureCity = value; } } /// <summary> /// 到达城市 /// </summary> [Property] [Display(Name = "到达城市")] [Required] public string PurposeOfCity { get { return _purposeOfCity; } set { _purposeOfCity = value; } } /// <summary> /// 去程交通 /// </summary> [Property] [Display(Name = "去程交通")] [Required] public TransportationTypes TripTransportation { get { return _tripTransportation; } set { _tripTransportation = value; } } /// <summary> /// 返程交通 /// </summary> [Property] [Display(Name = "返程交通")] [Required] public TransportationTypes ReturnTransportation { get { return _returnTransportation; } set { _returnTransportation = value; } } /// <summary> /// 创建时间 /// </summary> [Property] public DateTime CreateDate { get { return _createDate; } set { _createDate = value; } } /// <summary> /// 创建人 /// </summary> [BelongsTo("UserId")] public AccountUser AccountUser { get { return _accountUser; } set { _accountUser = value; } } /// <summary> /// 商品联系人信息 /// </summary> [OneToOne(Cascade = CascadeEnum.All)] public ProductsContact ProductsContact { get { if (_productsContact == null) { _productsContact = new ProductsContact(); } return _productsContact; } set { _productsContact = value; } } /// <summary> /// 商品详细信息 /// </summary> [OneToOne(Cascade = CascadeEnum.All)] public LineProductDetail LineProductDetail { get { if (_lineProductDetail == null) { _lineProductDetail = new LineProductDetail(); } return _lineProductDetail; } set { _lineProductDetail = value; } } /// <summary> /// 付款方式 /// </summary> [OneToOne(Cascade = CascadeEnum.All)] public CommodityPayment CommodityPayment { get { if (_commodityPayment == null) { _commodityPayment = new CommodityPayment(); } return _commodityPayment; } set { _commodityPayment = value; } } /// <summary> /// 商品状态 默认创建完成 /// </summary> [Property] public ProductStatus ProductStatus { get { return _productStatus; } set { _productStatus = value; } } #endregion #region Constructor public LineProductBasicInfo() { } public LineProductBasicInfo( string title, LineTypes lineType, DateTime departureDate, DateTime returnDate, string departureCity, string purposeOfCity, TransportationTypes tripTransportation, TransportationTypes returnTransportation, AccountUser accountUser) : base(title) { this._title = title; this._lineType = lineType; this._departureDate = departureDate; this._returnDate = returnDate; this._travelDays = returnDate.Subtract(departureDate).Days; this._departureCity = departureCity; this._purposeOfCity = purposeOfCity; this._tripTransportation = tripTransportation; this._returnTransportation = returnTransportation; this._accountUser = accountUser; this._createDate = DateTime.Now; this._productStatus = ProductStatus.Created; } #endregion }
CommodityPayment [ActiveRecord("CommodityPayments")] public class CommodityPayment : ActiveRecordBase<CommodityPayment>, IAggregateRoot, INotifyPropertyChanged, ICloneable { #region Field private Int64 _commodityPaymentId; private PaymentTimeLimitsTypes _paymentTimeLimitsType; private PaymentTypes _paymentType; private int _paymentTime; private decimal? _stageOne; private decimal? _stageTwo; private decimal? _stageThree; private ProductBase _productBase; private CommodityPayment _copyCommodityPayment; #endregion #region Property [PrimaryKey(Column = "Id", Generator = PrimaryKeyType.Foreign)] public Int64 CommodityPaymentId { get { return _commodityPaymentId; } set { _commodityPaymentId = value; } } /// <summary> /// 付款时限 /// </summary> [Property] public PaymentTimeLimitsTypes PaymentTimeLimitsType { get { return _paymentTimeLimitsType; } set { _paymentTimeLimitsType = value; } } /// <summary> /// 付款方式 /// </summary> [Property] public PaymentTypes PaymentType { get { return _paymentType; } set { _paymentType = value; if (PropertyChanged != null) { if (CopyCommodityPayment._paymentType != _paymentType) { if (PaymentType == PaymentTypes.InstantCccounts) { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "付款方式由<span style='color:red'>{0}</span>更改为<span style='color:red'>{1}</span>", "担保交易", "即时到账") ) ); } else { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "付款方式由<span style='color:red'>{0}</span>更改为<span style='color:red'>{1}</span>", "即时到账", "担保交易") ) ); } } } } } /// <summary> /// 付款小时数 /// </summary> [Property] [Required] [Display(Name = "付款时限")] public int PaymentTime { get { return _paymentTime; } set { _paymentTime = value; if (PropertyChanged != null) { if (CopyCommodityPayment._paymentTime != _paymentTime) { if (PaymentTimeLimitsType == PaymentTimeLimitsTypes.Day) { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "付款时限由<span style='color:red'>{0}天</span>更改为<span style='color:red'>{1}天</span>", CopyCommodityPayment.PaymentTime, PaymentTime) ) ); } else { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "付款时限由<span style='color:red'>{0}时</span>更改为<span style='color:red'>{1}时</span>", CopyCommodityPayment.PaymentTime, PaymentTime) ) ); } } } } } /// <summary> /// 阶段1 /// </summary> [Property] public decimal? StageOne { get { return _stageOne; } set { _stageOne = value; } } /// <summary> /// 阶段2 /// </summary> [Property] public decimal? StageTwo { get { return _stageTwo; } set { _stageTwo = value; } } /// <summary> /// 阶段3 /// </summary> [Property] public decimal? StageThree { get { return _stageThree; } set { _stageThree = value; } } [OneToOne] public ProductBase ProductBase { get; set; } public CommodityPayment CopyCommodityPayment { get { return _copyCommodityPayment; } set { _copyCommodityPayment = value; } } #endregion #region Constructor public CommodityPayment() { } public CommodityPayment(PaymentTimeLimitsTypes paymentTimeLimitsType, PaymentTypes paymentType, int paymentTime) : this(paymentTimeLimitsType, paymentType, null, null, null, paymentTime) { } public CommodityPayment(PaymentTimeLimitsTypes paymentTimeLimitsType, PaymentTypes paymentType, decimal? stageOne, decimal? stageTwo, decimal? stageThree, int paymentTime) { this._paymentTimeLimitsType = paymentTimeLimitsType; this._paymentType = paymentType; this._paymentTime = paymentTime; this._stageOne = stageOne; this._stageTwo = stageTwo; this._stageThree = stageThree; } #endregion public event PropertyChangedEventHandler PropertyChanged; #region Public Method public object Clone() { CommodityPayment commodityPayment = new CommodityPayment(); commodityPayment._paymentTimeLimitsType = this._paymentTimeLimitsType; commodityPayment._paymentType = this._paymentType; commodityPayment._paymentTime = this._paymentTime; commodityPayment._stageOne = this._stageOne; commodityPayment._stageTwo = this._stageTwo; commodityPayment._stageThree = this._stageThree; return commodityPayment; } #endregion }
ProductsContact [ActiveRecord("ProductsContacts")] public class ProductsContact : ActiveRecordBase<ProductsContact>, IAggregateRoot, INotifyPropertyChanged, ICloneable { #region Field private Int64 _productsContactId; private string _productsContactUserName; private string _productsContactPhone; private int _productsContactQQ; private ProductBase _productBase; private ProductsContact _copyProductsContact; #endregion #region Property [PrimaryKey(Column = "Id", Generator = PrimaryKeyType.Foreign)] public Int64 ProductsContactId { get { return _productsContactId; } set { _productsContactId = value; } } /// <summary> /// 联系人 /// </summary> [Property] [Display(Name = "联系人")] [Required] public string ProductsContactUserName { get { return _productsContactUserName; } set { _productsContactUserName = value; if (PropertyChanged != null) { if (CopyProductsContact._productsContactUserName != _productsContactUserName) { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "产品联系人由<span style='color:red'>{0}</span>更改为<span style='color:red'>{1}</span>", CopyProductsContact.ProductsContactUserName, ProductsContactUserName) ) ); } } } } /// <summary> /// 联系电话 /// </summary> [Property] [Display(Name = "联系电话")] [Required] public string ProductsContactPhone { get { return _productsContactPhone; } set { _productsContactPhone = value; if (PropertyChanged != null) { if (CopyProductsContact._productsContactPhone != _productsContactPhone) { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "产品联系人电话由<span style='color:red'>{0}</span>更改为<span style='color:red'>{1}</span>", CopyProductsContact.ProductsContactPhone, ProductsContactPhone) ) ); } } } } /// <summary> /// 联系人QQ /// </summary> [Property] [Display(Name = "联系人QQ")] [Required] public int ProductsContactQQ { get { return _productsContactQQ; } set { _productsContactQQ = value; if (PropertyChanged != null) { if (CopyProductsContact._productsContactQQ != _productsContactQQ) { PropertyChanged( this, new PropertyChangedEventArgs( string.Format( "产品联系人QQ由<span style='color:red'>{0}</span>更改为<span style='color:red'>{1}</span>", CopyProductsContact.ProductsContactQQ, ProductsContactQQ) ) ); } } } } [OneToOne] public ProductBase ProductBase { get; set; } public ProductsContact CopyProductsContact { get { return _copyProductsContact; } set { _copyProductsContact = value; } } #endregion #region Constructor public ProductsContact() { } public ProductsContact(string productsContactUserName, string productsContactPhone, int productsContactQQ) { this._productsContactUserName = productsContactUserName; this._productsContactPhone = productsContactPhone; this._productsContactQQ = productsContactQQ; } #endregion public event PropertyChangedEventHandler PropertyChanged; #region Public Method public object Clone() { ProductsContact productsContact = new ProductsContact(); productsContact._productsContactUserName = this._productsContactUserName; productsContact._productsContactPhone = this._productsContactPhone; productsContact._productsContactQQ = this._productsContactQQ; return productsContact; } #endregion }
调用 private ProductsContact UpdateProductsContact(FormCollection collection, VASModel.ProductsContact productsContact) { productsContact.CopyProductsContact = (VASModel.ProductsContact)productsContact.Clone(); productsContact.PropertyChanged += productsContact_PropertyChanged; productsContact.ProductsContactUserName = Server.HtmlEncode(collection["ProductsContact.ProductsContactUserName"]); productsContact.ProductsContactPhone = Server.HtmlEncode(collection["ProductsContact.ProductsContactPhone"]); productsContact.ProductsContactQQ = int.Parse(collection["ProductsContact.ProductsContactQQ"]); return productsContact; } void productsContact_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { AuditLog auditLog = new AuditLog("hzd", e.PropertyName); _auditLogList.Add(auditLog); } public void UpdateLineProduct(LineProductBasicInfo lineProductBasicInfo, List<AuditLog> auditLogList) { _lineProductBasicInfoRepository.Save(lineProductBasicInfo); _unitOfWork.Commit(); foreach (var auditLog in auditLogList) { auditLog.ProductBase = lineProductBasicInfo; _auditLogService.CreateAuditLog(auditLog); } }
本文没什么难度,主要是INotifyPropertyChanged接口和深拷贝。