C# 如何仅在属性已更改的情况下将数据从一个.NET实例复制到另一个.NET实例?
我希望将数据从.NET类的一个实例复制到同一类的另一个实例,但仅复制已修改/更改的属性 例如 到目前为止,我已经为每个类中的每个属性创建了一个C# 如何仅在属性已更改的情况下将数据从一个.NET实例复制到另一个.NET实例?,c#,.net,C#,.net,我希望将数据从.NET类的一个实例复制到同一类的另一个实例,但仅复制已修改/更改的属性 例如 到目前为止,我已经为每个类中的每个属性创建了一个bool IsPropertyNameModified。当我执行复制时,如果IsPropertyNameModified为true,我仅设置值(在destinationFoo)。这是一个非常简单的代码摘要 对于集合,这非常难看,但我认为我需要禁止设置集合,只提供AddProduct(…)。。等方法添加 (基本上) 我希望使用事件更好地处理这个问题。甚至使用
bool IsPropertyNameModified
。当我执行复制
时,如果IsPropertyNameModified
为true
,我仅设置值(在destinationFoo
)。这是一个非常简单的代码摘要
对于集合,这非常难看,但我认为我需要禁止设置集合,只提供AddProduct(…)
。。等方法添加
(基本上)
我希望使用事件更好地处理这个问题。甚至使用IPropertyNotified
或其他东西。我甚至在考虑为我们使用的每种类型(基本上不是很多->int/string/bool
类型)创建一个自定义类,该类具有值且已修改
设置
我不知道哪条路更干净更好。我觉得我现在的方式太难闻了
更新(来自评论和q)
请不要因为destinationFoo
是从数据库加载的事实而挂断电话。这是不相干的。我想说的一点是,destinationFoo
有很多属性,其中数据已经设置好了。它充满了数据。现在,sourceFoo
是同一个类,但只有三个已更改的属性。因此,需要复制这三个属性。并非所有其他97个默认值(想想0
或null
等)。这就是为什么我需要知道更改/修改了哪3个属性,因此,只复制这些属性。像这样的东西怎么样。我创建了一个名为ChangeTracker
的抽象类,用于跟踪列表中的更改
class Program
{
static void Main(string[] args)
{
var foo = new Foo() { Name = "Glenn", Age = 34, Notes = "sample code" };
Foo bar = new Foo();
bar.Apply(foo.GetTrackedChanges());
// ...
}
}
public class Foo : ChangeTracker
{
public Foo()
{
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; Track("Age", value); }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; Track("Name", value); }
}
private string _notes;
public string Notes
{
get { return _notes; }
set { _notes = value; Track("Notes", value); }
}
private string _favoriteColor;
public string FavoriteColor
{
get { return _favoriteColor; }
set { _favoriteColor = value; Track("FavoriteColor", value); }
}
}
// this abstract class does the work
public abstract class ChangeTracker : IChangeTrackerResetable
{
protected List<Tuple<string, object>> _changes = new List<Tuple<string, object>>();
public IEnumerable<Tuple<string, object>> GetTrackedChanges()
{
return _changes;
}
void IChangeTrackerResetable.Reset()
{
_changes.Clear();
}
protected void Track(string propname, object value)
{
_changes.Add(Tuple.Create(propname, value));
}
public void Apply(IEnumerable<Tuple<string, object>> changes)
{
var inst = this;
var props = inst.GetType().GetProperties(System.Reflection.BindingFlags.Public);
foreach(var ch in changes)
{
var property = props.FirstOrDefault(r => r.Name == ch.Item1);
if (property != null)
{
property.SetValue(inst, ch.Item2);
}
}
// reset afterwards
var o = this as IChangeTrackerResetable;
o.Reset();
}
}
public interface IChangeTrackerResetable
{
void Reset();
}
类程序
{
静态void Main(字符串[]参数)
{
var foo=new foo(){Name=“Glenn”,Age=34,Notes=“sample code”};
Foo-bar=新的Foo();
Apply(foo.gettrackdchanges());
// ...
}
}
公共类Foo:ChangeTracker
{
公共食物(
{
}
私人互联网;
公共信息
{
获取{return\u age;}
设置{u age=value;跟踪(“age”,value);}
}
私有字符串\u名称;
公共字符串名
{
获取{return\u name;}
设置{u name=value;轨迹(“name”,value);}
}
私人字符串注释;
公共弦乐
{
获取{return\u notes;}
设置{u notes=value;跟踪(“notes”,value);}
}
私有字符串\u favoriteColor;
公共字符串收藏夹颜色
{
获取{return\u favoriteColor;}
设置{u favoriteColor=value;轨迹(“favoriteColor”,value);}
}
}
//这个抽象类完成这项工作
公共抽象类ChangeTracker:IChangeTrackerResetable
{
受保护列表_changes=新列表();
公共IEnumerable GetTrackChanges()
{
返回更改;
}
void IChangeTrackerResetable.Reset()无效
{
_更改。清除();
}
受保护的无效轨迹(字符串名称、对象值)
{
_changes.Add(Tuple.Create(propname,value));
}
公共无效应用(IEnumerable更改)
{
var inst=此;
var props=inst.GetType().GetProperties(System.Reflection.BindingFlags.Public);
foreach(变化中的var ch)
{
var属性=props.FirstOrDefault(r=>r.Name==ch.Item1);
if(属性!=null)
{
属性设置值(仪表,第2章);
}
}
//事后重置
var o=此为IChangeTrackerResetable;
o、 重置();
}
}
公共接口IChangeTrackerResetable
{
无效重置();
}
是否通过某种UI编辑sourceFoo
或destinationFoo
,或者您只是从文件加载一个,从数据库加载另一个,然后比较它们?哪一个需要事件处理程序?您希望获得什么好处?e、 g.业绩?更少的网络流量?您是否希望将已更改的补丁应用到另一个实例,并进行自身的更改?显然感觉像是过早的优化,而且代码气味非常强烈。我认为您有三个选择:继续您正在做的事情,这感觉像是一个巨大的维护难题;在属性发生更改时使用observables/Rx触发事件;在任何属性发生更改时克隆对象。我很难理解为什么有人要将db项复制到文件中,或者反之亦然。RDBMS中DB的全部意义在于将数据存储在一个文件中,并在其上添加一种查询语言。除非这是为了某种迁移过程,比如获取旧数据库的XML转储并将其导入新数据库。在这种情况下,实体框架将自动处理“跟踪”。TLDR,知道这样做的目的将有助于我们提供更好的解决方案。这是MVVM中模式的一个非常粗糙、快速的n-dirty版本,特别是我熟悉MVVMLight,实现InotifyPropertyChanged的ViewModelBase类我从未使用过它,但快速的nuget搜索使我更改了跟踪和描述“跟踪poco对象和集合中的更改”。它依赖于Castle.Core,并且有一个类似于实体框架打开/关闭跟踪的AsTrack扩展。是的,我同意Track()调用可以在Foo的OnpropertyChanged()中通过[CallerMemberName]实现INPC时完成这样就不会有任何拼写错误的属性名称用于跟踪。
public class Foo : Baa
{
private int _age;
public int Age { get { return _age; } set { _age = value; IsAgeModified = true; }
public bool IsAgeModified { get; set; }
}
class Program
{
static void Main(string[] args)
{
var foo = new Foo() { Name = "Glenn", Age = 34, Notes = "sample code" };
Foo bar = new Foo();
bar.Apply(foo.GetTrackedChanges());
// ...
}
}
public class Foo : ChangeTracker
{
public Foo()
{
}
private int _age;
public int Age
{
get { return _age; }
set { _age = value; Track("Age", value); }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; Track("Name", value); }
}
private string _notes;
public string Notes
{
get { return _notes; }
set { _notes = value; Track("Notes", value); }
}
private string _favoriteColor;
public string FavoriteColor
{
get { return _favoriteColor; }
set { _favoriteColor = value; Track("FavoriteColor", value); }
}
}
// this abstract class does the work
public abstract class ChangeTracker : IChangeTrackerResetable
{
protected List<Tuple<string, object>> _changes = new List<Tuple<string, object>>();
public IEnumerable<Tuple<string, object>> GetTrackedChanges()
{
return _changes;
}
void IChangeTrackerResetable.Reset()
{
_changes.Clear();
}
protected void Track(string propname, object value)
{
_changes.Add(Tuple.Create(propname, value));
}
public void Apply(IEnumerable<Tuple<string, object>> changes)
{
var inst = this;
var props = inst.GetType().GetProperties(System.Reflection.BindingFlags.Public);
foreach(var ch in changes)
{
var property = props.FirstOrDefault(r => r.Name == ch.Item1);
if (property != null)
{
property.SetValue(inst, ch.Item2);
}
}
// reset afterwards
var o = this as IChangeTrackerResetable;
o.Reset();
}
}
public interface IChangeTrackerResetable
{
void Reset();
}