使用 Unity IoC 容器的更智能的 DTO
Smarter DTO with Unity IoC container
我有一个 MVVM 模型,其中我的视图模型包含一个 observableCollection<RfidData>
。
每当其中的数据更新时,我想在属性上调用 RaisePropertyChanged()
。
我在某个地方看到它可以用 Unity IoC 容器非常巧妙地完成,但我找不到了。
这是我的 class:
public class RfidData : INotifyPropertyChanged
{
private bool _checkedIn = false;
private Guid _id;
private int _collectorId;
private DateTime _checkInTime;
private string _name = "Test Name";
public bool CheckedIn
{
get { return _checkedIn; }
set { _checkedIn = value; RaisePropertyChanged(); }
}
public Guid Id
{
get {return _id;}
set { _id = value; RaisePropertyChanged(); }
}
public int CollectorId
{
set { _collectorId = value; RaisePropertyChanged(); }
get { return _collectorId; }
}
public DateTime CheckInTime
{
set { _checkInTime = value; RaisePropertyChanged(); }
get { return _checkInTime; }
}
public string Name
{
set { _name = value; RaisePropertyChanged(); }
get { return _name; }
}
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
有什么建议吗?
我想你在找拦截器。这是一个如何使用它的例子
Unity Interceptor
适合我的解决方案:
使用 virtual
属性定义 class:
public class RfidData : BaseViewModel
{
public RfidData(Guid id)
{
Id = id;
}
public virtual int CollectorId { get; set; }
public virtual DateTime CheckInTime { get; set; }
public virtual string Name { get; set; }
public virtual bool CheckedIn { get; set; }
public virtual DateTime TimeStamp { get; set; }
public virtual Guid Id { get; }
}
BaseViewModel
将执行 RaidPropertyChange() 方法:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
几乎抄袭@bilpor's link.
由于调用了 RaisePropertyChanged
而不是旧的 PropertyChanged
,因此调用的方法不同。
我确实相信使用像 [CallerMemberName]
这样的新方法会更好一些,但我还没有研究它。
using System;
using System.ComponentModel;
using System.Reflection;
using Microsoft.Practices.Unity.InterceptionExtension;
public class PropertyChangedCallHandler : ICallHandler
{
public int Order { get; set; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
//Only if not same value as current, and it's a 'set' called
var shouldRaise = ShouldRaiseEvent(input);
var res = getNext()(input, getNext);
if (res.Exception == null && shouldRaise)
RaiseEvent(input);
return res;
}
private bool ShouldRaiseEvent(IMethodInvocation input)
{
var methodBase = input.MethodBase;
//Is the method a property setter?
if (!methodBase.IsSpecialName || !methodBase.Name.StartsWith("set_"))
{
return false;
}
//Get the name of the property out so we can use it to raise a
//property changed event
string propertyName = methodBase.Name.Substring(4);
//Retrieve the property getter
PropertyInfo property = methodBase.ReflectedType.GetProperty(propertyName);
MethodInfo getMethod = property.GetGetMethod();
//IF the property has no get method, we don't care
if (getMethod == null)
{
return false;
}
//Get the current value out
object oldValue = getMethod.Invoke(input.Target, null);
//Get the updated value
object value = input.Arguments[0];
//Is the new value null?
if (value != null)
{
//Is the new value different from the old value?
if (value.Equals(oldValue) == false)
{
return true;
}
}
else
{
//Is the new value (null) different from the
//old value (non-null)?
if (value != oldValue)
{
return true;
}
}
return false;
}
private void RaiseEvent(IMethodInvocation input)
{
FieldInfo field = null;
//Get a reference to the PropertyChanged event out of the current
//type or one of the base types
var type = input.MethodBase.ReflectedType;
while (field == null && type != null)
{
//This differs from the original
field = type.GetField("RaisePropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
type = type.BaseType;
}
//If we found the PropertyChanged event
if (field != null)
{
//Get the event handler if there is one
var evt = field.GetValue(input.Target) as MulticastDelegate;
if (evt != null)
{
//Get the property name out
string propertyName = input.MethodBase.Name.Substring(4);
//Invoke the property changed event handlers
evt.DynamicInvoke(input.Target, new PropertyChangedEventArgs(propertyName));
}
}
}
}
最后,用Unity拦截器注册class RfidData
class:
var notificationPolicy = _container.AddNewExtension<Interception>()
.RegisterType<BaseViewModel, RfidData>()
.Configure<Interception>()
.SetDefaultInterceptorFor(userControlType, new VirtualMethodInterceptor())
.AddPolicy("NotificationPolicy");
notificationPolicy.AddMatchingRule(new PropertyMatchingRule("*", PropertyMatchingOption.Set));
notificationPolicy.AddCallHandler<PropertyChangedCallHandler>();
但奇怪的是:
当您实例化它时,通常是通过 contain.Resolve<RfidData>()
,但我通过 NewtonSoft.JsonConvert.DeserializeObject()
方法获取所有数据。
但它接受正确的类型。
我有一个 MVVM 模型,其中我的视图模型包含一个 observableCollection<RfidData>
。
每当其中的数据更新时,我想在属性上调用 RaisePropertyChanged()
。
我在某个地方看到它可以用 Unity IoC 容器非常巧妙地完成,但我找不到了。
这是我的 class:
public class RfidData : INotifyPropertyChanged
{
private bool _checkedIn = false;
private Guid _id;
private int _collectorId;
private DateTime _checkInTime;
private string _name = "Test Name";
public bool CheckedIn
{
get { return _checkedIn; }
set { _checkedIn = value; RaisePropertyChanged(); }
}
public Guid Id
{
get {return _id;}
set { _id = value; RaisePropertyChanged(); }
}
public int CollectorId
{
set { _collectorId = value; RaisePropertyChanged(); }
get { return _collectorId; }
}
public DateTime CheckInTime
{
set { _checkInTime = value; RaisePropertyChanged(); }
get { return _checkInTime; }
}
public string Name
{
set { _name = value; RaisePropertyChanged(); }
get { return _name; }
}
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
有什么建议吗?
我想你在找拦截器。这是一个如何使用它的例子 Unity Interceptor
适合我的解决方案:
使用 virtual
属性定义 class:
public class RfidData : BaseViewModel
{
public RfidData(Guid id)
{
Id = id;
}
public virtual int CollectorId { get; set; }
public virtual DateTime CheckInTime { get; set; }
public virtual string Name { get; set; }
public virtual bool CheckedIn { get; set; }
public virtual DateTime TimeStamp { get; set; }
public virtual Guid Id { get; }
}
BaseViewModel
将执行 RaidPropertyChange() 方法:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
几乎抄袭@bilpor's link.
由于调用了 RaisePropertyChanged
而不是旧的 PropertyChanged
,因此调用的方法不同。
我确实相信使用像 [CallerMemberName]
这样的新方法会更好一些,但我还没有研究它。
using System;
using System.ComponentModel;
using System.Reflection;
using Microsoft.Practices.Unity.InterceptionExtension;
public class PropertyChangedCallHandler : ICallHandler
{
public int Order { get; set; }
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
//Only if not same value as current, and it's a 'set' called
var shouldRaise = ShouldRaiseEvent(input);
var res = getNext()(input, getNext);
if (res.Exception == null && shouldRaise)
RaiseEvent(input);
return res;
}
private bool ShouldRaiseEvent(IMethodInvocation input)
{
var methodBase = input.MethodBase;
//Is the method a property setter?
if (!methodBase.IsSpecialName || !methodBase.Name.StartsWith("set_"))
{
return false;
}
//Get the name of the property out so we can use it to raise a
//property changed event
string propertyName = methodBase.Name.Substring(4);
//Retrieve the property getter
PropertyInfo property = methodBase.ReflectedType.GetProperty(propertyName);
MethodInfo getMethod = property.GetGetMethod();
//IF the property has no get method, we don't care
if (getMethod == null)
{
return false;
}
//Get the current value out
object oldValue = getMethod.Invoke(input.Target, null);
//Get the updated value
object value = input.Arguments[0];
//Is the new value null?
if (value != null)
{
//Is the new value different from the old value?
if (value.Equals(oldValue) == false)
{
return true;
}
}
else
{
//Is the new value (null) different from the
//old value (non-null)?
if (value != oldValue)
{
return true;
}
}
return false;
}
private void RaiseEvent(IMethodInvocation input)
{
FieldInfo field = null;
//Get a reference to the PropertyChanged event out of the current
//type or one of the base types
var type = input.MethodBase.ReflectedType;
while (field == null && type != null)
{
//This differs from the original
field = type.GetField("RaisePropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
type = type.BaseType;
}
//If we found the PropertyChanged event
if (field != null)
{
//Get the event handler if there is one
var evt = field.GetValue(input.Target) as MulticastDelegate;
if (evt != null)
{
//Get the property name out
string propertyName = input.MethodBase.Name.Substring(4);
//Invoke the property changed event handlers
evt.DynamicInvoke(input.Target, new PropertyChangedEventArgs(propertyName));
}
}
}
}
最后,用Unity拦截器注册class RfidData
class:
var notificationPolicy = _container.AddNewExtension<Interception>()
.RegisterType<BaseViewModel, RfidData>()
.Configure<Interception>()
.SetDefaultInterceptorFor(userControlType, new VirtualMethodInterceptor())
.AddPolicy("NotificationPolicy");
notificationPolicy.AddMatchingRule(new PropertyMatchingRule("*", PropertyMatchingOption.Set));
notificationPolicy.AddCallHandler<PropertyChangedCallHandler>();
但奇怪的是:
当您实例化它时,通常是通过 contain.Resolve<RfidData>()
,但我通过 NewtonSoft.JsonConvert.DeserializeObject()
方法获取所有数据。
但它接受正确的类型。