C# 将类属性标记为脏
下面是一个简单的枚举示例,它定义了对象的状态,而类则显示了该枚举的实现。
public enum StatusEnum
{
Clean = 0,
Dirty = 1,
New = 2,
Deleted = 3,
Purged = 4
}
public class Example_Class
{
private StatusEnum _Status = StatusEnum.New;
private long _ID;
private string _Name;
public StatusEnum Status
{
get { return _Status; }
set { _Status = value; }
}
public long ID
{
get { return _ID; }
set { _ID = value; }
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
当使用数据库中的数据填充类对象时,我们将枚举值设置为“clean”。 为了将大部分逻辑保留在表示层之外,我们如何在属性更改时将枚举值设置为“脏”。
我在想一些类似的事情;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = value;
_Status = StatusEnum.Dirty;
}
}
}
在类的每个属性的设置器中。
这听起来是个好主意吗?有没有人对如何分配脏标志有更好的想法,而无需在表示层中这样做。
The following is a simple example of an enum which defines the state of an object and a class which shows the implementation of this enum.
public enum StatusEnum
{
Clean = 0,
Dirty = 1,
New = 2,
Deleted = 3,
Purged = 4
}
public class Example_Class
{
private StatusEnum _Status = StatusEnum.New;
private long _ID;
private string _Name;
public StatusEnum Status
{
get { return _Status; }
set { _Status = value; }
}
public long ID
{
get { return _ID; }
set { _ID = value; }
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
when populating the class object with data from the database, we set the enum value to "clean". with the goal of keeping most of the logic out of the presentation layer, how can we set the enum value to "dirty" when a property is changed.
i was thinking something along the lines of;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = value;
_Status = StatusEnum.Dirty;
}
}
}
in the setter of each property of the class.
does this sound like a good idea, does anyone have any better ideas on how the dirty flag can be assigned without doing so in the presentation layer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
当您确实想要在类级别(或者就此而言,通知)使用脏标志时 - 您可以使用如下技巧来最大程度地减少属性中的混乱(此处显示
IsDirty
和PropertyChanged
,只是为了好玩)。显然,使用枚举方法是一件微不足道的事情(我没有使用枚举方法的唯一原因是为了保持示例简单):
您也可以选择将其中一些推入抽象基类中,但这是一个单独的讨论
When you really do want a dirty flag at the class level (or, for that matter, notifications) - you can use tricks like below to minimise the clutter in your properties (here showing both
IsDirty
andPropertyChanged
, just for fun).Obviously it is a trivial matter to use the enum approach (the only reason I didn't was to keep the example simple):
You might also choose to push some of that into an abstract base class, but that is a separate discussion
一种选择是在写入时更改它; 另一种方法是保留所有原始值的副本,并在有人要求时计算脏度。 这还有一个额外的好处,您可以准确地知道哪些字段已更改(以及以何种方式更改),这意味着您可以发出最少的更新语句并使合并冲突解决稍微容易一些。
您还可以将所有脏污检查放在一处,这样就不会污染代码的其余部分。
我并不是说它是完美的,但这是一个值得考虑的选择。
One option is to change it on write; another is to keep a copy of all the original values and compute the dirtiness when anyone asks for it. That has the added benefit that you can tell exactly which fields have changed (and in what way) which means you can issue minimal update statements and make merge conflict resolution slightly easier.
You also get to put all the dirtiness-checking in one place, so it doesn't pollute the rest of your code.
I'm not saying it's perfect, but it's an option worth considering.
如果你想以这种方式实现它,并且你想减少代码量,你可以考虑应用面向方面编程。
例如,您可以使用像 PostSharp 这样的编译时编织器,并创建一个可应用于特性。 这方面可以确保在适当的时候设置脏标志。
该方面可以如下所示:
当然,这意味着您想要实现 ChangeTracking 的类应该实现 IChangeTrackable 接口(自定义接口),该接口至少具有“Status”属性。
您还可以创建自定义属性
ChangeTrackingProperty
,并确保上面创建的方面仅应用于用此ChangeTrackingProperty
属性修饰的属性。例如:
这就是我的一点看法。
您甚至可以确保 PostSharp 在编译时检查具有用 ChangeTrackingProperty 属性修饰的属性的类是否实现 IChangeTrackable 接口。
If you want to implement it in this way, and you want to reduce the amount of code, you might consider applying Aspect Oriented Programming.
You can for instance use a compile-time weaver like PostSharp , and create an 'aspect' that can be applied to properties. This aspect then makes sure that your dirty flag is set when appropriate.
The aspect can look like this:
Offcourse, this means that the classes for which you want to implement ChangeTracking, should implement the
IChangeTrackable
interface (custom interface), which has at least the 'Status' property.You can also create a custom attribute
ChangeTrackingProperty
, and make sure that the aspect that has been created above, is only applied to properties that are decorated with thisChangeTrackingProperty
attribute.For instance:
This is a little bit how I see it.
You can even make sure that PostSharp checks at compile-time whether classes that have properties that are decorated with the ChangeTrackingProperty attribute, implement the IChangeTrackable interface.
该方法基于该线程中提供的一组不同概念。 我想我会把它发布给任何正在寻找一种干净有效的方法的人,就像我自己一样。
这种混合概念的关键在于:
考虑到这些要求,这就是我想到的,它似乎非常适合我,并且在处理 UI 和准确捕获用户更改时变得非常有用。 我还在下面发布了“如何使用”,向您展示我如何在 UI 中使用它。
对象
它非常简单,满足了我们的所有要求:
当然,您可以对其进行调整以涵盖许多不同的状态……这完全取决于您。 此示例仅显示如何进行正确的 IsDirty 标志操作。
场景
让我们回顾一下一些场景,看看会返回什么结果:
场景 1
使用空构造函数创建新对象,
属性名称从“”更改为“James”,
调用 IsDirty 返回 True! 准确。
场景 2
使用参数“John”和 12345 创建新对象,
属性名称从“John”更改为“James”,
属性名称从“James”更改回“John”,
调用 IsDirty 返回 False。 准确,而且我们也不需要复制数据来做到这一点!
如何使用,WinForms UI 示例
这只是一个示例,您可以通过 UI 以多种不同的方式使用它。
假设您有两种表单([A] 和 [B])。
第一个 ([A]) 是主窗体,第二个 ([B]) 是允许用户更改 MySmartObject 内的值的窗体。
[A] 和 [B] 表单都声明了以下属性:
当用户单击 [A] 表单上的按钮时,将创建 [B] 表单的实例,设置其属性并显示为一个对话框。
表单 [B] 返回后,[A] 表单根据 [B] 表单的 IsDirty 检查更新其属性。 像这样:
另外,在 [B] 中,当关闭时,您可以检查并提示用户是否正在关闭其中包含未保存更改的表单,如下所示:
正如您从上面的示例中看到的,这可以是非常有用的东西,因为它确实简化了用户界面。
注意事项
希望这对某人有帮助。
This method is based on a set of different concepts provided in this thread. I thought i'd put it out there for anyone that is looking for a way to do this cleanly and efficiently, as i was myself.
The key of this hybrid concept is that:
Given those requirements, this is what i came up with, and it seems to be working perfectly for me, and has become very useful when working against UIs and capturing user changes accurately. I have also posted an "How to use" below to show you how I use this in the UI.
The Object
It's simple enough and addresses all of our requirements:
Of course you can adapt this to encompass a bunch of different states... it's really up to you. This example only shows how to have a proper IsDirty flag operation.
Scenarios
Let's go over some scenarios for this and see what comes back:
Scenario 1
New object is created using empty constructor,
Property Name changes from "" to "James",
call to IsDirty returns True! Accurate.
Scenario 2
New object is created using paramters of "John" and 12345,
Property Name changes from "John" to "James",
Property Name changes back from "James" to "John",
Call to IsDirty returns False. Accurate, and we didn't have to duplicate the data to do it either!
How to use, a WinForms UI example
This is only an example, you can use this in many different ways from a UI.
Let's say you have a two forms ([A] and [B]).
The first([A]) is your main form, and the second([B]) is a form that allows the user to change the values within the MySmartObject.
Both the [A] and the [B] form have the following property declared:
When the user clicks a button on the [A] form, an instance of the [B] form is created, its property is set and it is displayed as a dialog.
After form [B] returns, the [A] form updates its property based on the [B] form's IsDirty check. Like this:
Also, in [B], when it is closing, you can check and prompt the user if they are closing the form with unsaved changes in it, like so:
As you can see from the examples above, this can be a very useful thing to have since it really streamlines the UI.
Caveats
Hopefully this helps someone.
看一下 PostSharp (http://www.postsharp.org/)。
您可以轻松创建一个将其标记为脏的属性,您可以将属性添加到需要它的每个属性,并将所有代码保留在一个位置。
粗略地说,创建一个接口,让该类实现它。
创建一个可应用于属性并转换为界面的属性,以便在标记属性之一发生更改时设置值。
Take a look at PostSharp (http://www.postsharp.org/).
You can easily create a Attribute which marks it as dirty you can add the attrubute to each property that needs it and it keeps all your code in one place.
Roughly speaking Create an interface which has your status in make the class implement it.
Create an attribute which can be applied on properties and cast to your interface in order to set the value when something changes one of the marked properties.
你的方法基本上就是我会做的。 我只想
删除 Status 属性的设置器:
并添加一个函数
以及
SetStatusDeleted()
和SetStatusPurged()
,因为我发现它更好地表明了意图。编辑
阅读乔恩·斯基特的回答< /a>,我需要重新考虑我的方法;-) 对于简单的对象,我会坚持我的方式,但如果它变得更复杂,他的建议将导致更好的组织代码。
Your approach is basically how I would do it. I would just
remove the setter for the Status property:
and instead add a function
As well as
SetStatusDeleted()
andSetStatusPurged()
, because I find it better indicates the intention.Edit
Having read the answer by Jon Skeet, I need to reconsider my approach ;-) For simple objects I would stick with my way, but if it gets more complex, his proposal would lead to much better organised code.
如果您的 Example_Class 是轻量级的,请考虑存储原始状态,然后将当前状态与原始状态进行比较,以确定更改。 如果不是,您的方法是最好的,因为在这种情况下存储原始状态会消耗大量系统资源。
If your Example_Class is lightweight, consider storing the original state and then comparing the current state to the original in order to determine the changes. If not your approach is the best because stroing the original state consumes a lot of system resources in this case.
除了“考虑让你的类型不可变”的建议之外,这是我写的一些东西(并且让 Jon 和 Marc 一路教我一些东西)
你明白了..可能的改进:使用 PropertyNames 和 常量 检查属性是否真的发生了变化。
这里的一个缺点是
Apart from the advice of 'consider making your type immutable', here's something I wrote up (and got Jon and Marc to teach me something along the way)
You get the idea.. possible improvements: Use constants for PropertyNames & check if property has really changed.
One drawback here is that
我是这样做的。
如果我不需要测试特定字段是否脏,
我有一个抽象类:
}
以及一个接口
这允许我进行部分保存,并在有其他详细信息需要保存时保留 IsDirty 状态。 并不完美,但涵盖了很多内容。
临时 IsDirty 状态的使用示例(为了清楚起见,删除了错误包装和验证):
这对于大多数情况都很好,但是对于某些类,我想使用原始数据的支持字段来测试每个字段,并且返回更改列表或者至少更改了字段的枚举。
随着字段枚举的更改,我可以将其通过消息链向上推送,以便有选择地更新远程缓存中的字段。
Here is how i do it.
In cases where i do not need to test for specific fields being dirty,
I have an abstract class:
}
As well as an interface
This allows me to do partial saves, and preserve the IsDirty state if there is other details to save. Not perfect, but covers a lot of ground.
Example of usage with interim IsDirty State (Error wrapping and validation removed for clarity):
This is good for most scenarios, however for some classes i want to test for each field with a backing field of original data, and either return a list of changes or at least an enum of fields changed.
With an enum of fields changed i can then push that up through a message chain for selective update of fields in remote caches.
您还可以考虑对变量进行装箱,这会降低性能,但也有其优点。 它非常简洁,您不会在不设置脏状态的情况下意外更改值。
然后挂钩一个回调,将您的类标记为脏。
You could also think about boxing your variables, which comes at a performance cost, but also has its merits. It is pretty consise and you cannot accidentally change a value without setting your dirty status.
and then hook in a callback that marks your class as dirty.
另一种方法是重写 GetHashCode() 方法,如下所示:
从数据库加载后,获取对象的哈希码。 然后在保存之前检查当前哈希码是否等于先前的哈希码。 如果它们相同,则不保存。
编辑:
正如人们指出的那样,这会导致哈希代码发生变化 - 当我使用 Guid 来识别我的对象时,我不介意哈希代码是否发生变化。
Edit2:
由于人们反对更改哈希码,因此不要重写 GetHashCode 方法,只需将该方法调用为其他名称即可。 重点是检测变化,而不是使用 guid 还是哈希码进行对象识别。
Another method is to override the GetHashCode() method to somthing like this:
Once loaded from the database, get the hash code of the object. Then just before you save check if the current hash code is equal to the previous hash code. if they are the same, don't save.
Edit:
As people have pointed out this causes the hash code to change - as i use Guids to identify my objects, i don't mind if the hashcode changes.
Edit2:
Since people are adverse to changing the hash code, instead of overriding the GetHashCode method, just call the method something else. The point is detecting a change not whether i use guids or hashcodes for object identification.