Limited object deep replication, ieditableobject implementation.

Source: Internet
Author: User

 

Recently, this problem occurs in the project, that is, double-click the edit window in the list, and then bindingsource. the value of current is passed to the editing form for Data Binding. After editing, refresh the data in the table. This is a very common process. In the past, we often did not encounter any problems. But now there is a problem. In the editing window, even if you do not click the Save button and press Cancel or close the window, the data in the list will be changed. After thinking about it, this is actually taken for granted. Because the object classes bound to the list are of reference type, it is nothing more than a pointer to pass in the past, in addition, databindings is used in the editing window to bind data. Therefore, the data source is directly updated when data is changed. This was not the case because it was originally used Code Only. This problem should obviously not occur. Even if it is not stored in the database, it will also cause user troubles, so we should solve it. How can this problem be solved? The first thought is to bindingsource during editing. current copies the copy and edits it. If you save the copy and replace it with the data source, nothing will happen if you cancel the copy. However, this also seems to be a problem. The first problem is the replication of the reference type. It is obviously impossible to directly use another variable. The principle is the same as above. Then we need to make a deep copy. Deep replication of a single class is not a problem, so you can write the code. However, this is an entity class. It has more than 50 objects and is not allowed in time. Besides, there are classes with dozens of field attributes. So what else can we do? Well, the serialization and deserialization are completely new objects. The basic principle is feasible. Is there a way to complete this function without modifying the encoding of the current list window and editing window? By querying msdn, we know that if the data source of bindingsource implements ieditableobject, bindingsource. canceledit will automatically call the canceledit method of the object to cancel the editing function. However, the problem arises again. Deserialization of each class can only be performed outside the class, because there is no way to assign a value to this pointer internally. What should we do? Well, there are always methods. As we all know, a field is the root of a class. Any external manifestation of the class is actually related to the field. That is to say, as long as we have backed up all the fields of a class, this class is completely backed up. Once you understand this truth, everything will be fine. Let's get started. Open the object interface definition ientity, add the ieditableobject interface, and then open the base class baseenttiy of the object to implement this interface in this base class, so that all object classes have beginedit, canceledit, endedit, and so on. The ieditobject interface has three methods: beginedit, canceledit, and endedit. The bindingsource provides only the latter two methods for external calls. Beginedit is called internally by bindingsource.

Recently, this problem occurs in the project, that is, double-click the edit window in the list, and then bindingsource. the value of current is passed to the editing form for Data Binding. After editing, refresh the data in the table. This is a very common process. In the past, we often did not encounter any problems. But now there is a problem.

In the editing window, even if you do not click the Save button and press Cancel or close the window, the data in the list will be changed. After thinking about it, this is actually taken for granted. Because the object classes bound to the list are of reference type, it is nothing more than a pointer to pass in the past, in addition, databindings is used in the editing window to bind data. Therefore, the data source is directly updated when data is changed. This was not the case because it was originally bound with code.

This problem should obviously not occur. Even if it is not stored in the database, it will also cause user troubles, so we should solve it. How can this problem be solved?
The first thought is to bindingsource during editing. current copies the copy and edits it. If you save the copy and replace it with the data source, nothing will happen if you cancel the copy. However, this also seems to be a problem. The first problem is the replication of the reference type. It is obviously impossible to directly use another variable. The principle is the same as above. Then we need to make a deep copy.

Deep replication of a single class is not a problem, so you can write the code. However, this is an entity class. It has more than 50 objects and is not allowed in time. Besides, there are classes with dozens of field attributes.

So what else can we do? Well, the serialization and deserialization are completely new objects. The basic principle is feasible. Is there a way to complete this function without modifying the encoding of the current list window and editing window?

By querying msdn, we know that if the data source of bindingsource implements ieditableobject, bindingsource. canceledit will automatically call the canceledit method of the object to cancel the editing function.

However, the problem arises again. Deserialization of each class can only be performed outside the class, because there is no way to assign a value to this pointer internally. What should we do? Well, there are always methods. As we all know, a field is the root of a class. Any external manifestation of the class is actually related to the field. That is to say, as long as we have backed up all the fields of a class, this class is completely backed up.

Once you understand this truth, everything will be fine. Let's get started.

Open the object interface definition ientity, add the ieditableobject interface, and then open the base class baseenttiy of the object to implement this interface in this base class, so that all object classes have beginedit, canceledit, endedit, and so on.

The ieditobject interface has three methods: beginedit, canceledit, and endedit. The bindingsource provides only the latter two methods for external calls. Beginedit is called internally by bindingsource.

 

Let's implement this interface first. The idea is as follows: when you call beginedit, we back up data and call canceledit.

Restore data. When endedit is called, the backup data is cleared. Therefore, at least two methods are required: Backup and Restore.

 

 

# Region ieditableobject member [nonserialized] bool _ intx; Public void beginedit () {If (! _ Intx) {backup (); _ intx = true ;}} public void canceledit () {If (_ intx) {restore (); _ intx = false ;}} public void endedit () {If (_ intx) {If (____ backup! = NULL) {____ backup. Clear () ;____ backup = NULL ;}_ intx = false ;}# endregion

 

 

 

It is worth noting that beginedit will be called multiple times, so we must set a variable to mark it so as not to perform multiple unnecessary backups. The specific Backup and Restore methods are used. If the code is too long, it will not be analyzed one by one, and there will be write comments.
# Region Backup and Restore data stack <byte []> ____ backup; Public Virtual void backup () {If (____ backup = NULL) {____ backup = new stack <byte []> (); Type sourcetype = This. getType (); hybriddictionary state = new hybriddictionary (); fieldinfo [] fields; do {// obtain information about all fields. Fields = sourcetype. getfields (bindingflags. nonpublic | bindingflags. instance | bindingflags. Public); foreach (fieldinfo field in fields) {// only process our own fields. If (field. declaringtype! = Sourcetype) continue; // ignore if nonserialized is marked. If (field. getcustomattributes (typeof (nonserializedattrites), true). Count ()! = 0) continue; object value = field. getvalue (this); If (typeof (baseentity ). isassignablefrom (field. fieldtype) {If (value = NULL) {// The value is null. We also want to save it. State. Add (field. declaringtype. Name + "! "+ Field. Name, null);} else {// is not empty and belongs to the same type. The backup method is also called. (Baseentity) value). Backup () ;}// check whether the field type is icollection to back up items in the collection. Else if (typeof (icollection). isassignablefrom (field. fieldtype) & value! = NULL & (icollection) value ). count> 0) {var Col = (icollection) value; foreach (VAR item in col) {If (item is baseentity) (baseentity) item ). backup ();} state. add (field. declaringtype. name + "! "+ Field. Name, value);} else {// common field, added to the dictionary. State. Add (field. declaringtype. Name + "! "+ Field. Name, value) ;}// recursive call up. Sourcetype = sourcetype. basetype;} while (sourcetype! = NULL); // serialize and push to stack. Using (memorystream buffer = new memorystream () {binaryformatter formatter = new binaryformatter (); formatter. serialize (buffer, State); ____ backup. push (buffer. toarray () ;}} Public Virtual void restore () {// if no backup data exists, ignore this call. If (____ backup! = NULL & ____ backup. Count> 0) {// deserialization to get the dictionary. Hybriddictionary state; using (memorystream buffer = new memorystream (____ backup. pop () {buffer. position = 0; binaryformatter formatter = new binaryformatter (); try {state = (hybriddictionary) formatter. deserialize (buffer) ;}catch {return ;}} object source = This; Type sourcetype = source. getType (); fieldinfo [] fields; do {// obtain field information. Fields = sourcetype. getfields (bindingflags. nonpublic | bindingflags. instance | bindingflags. Public); foreach (fieldinfo field in fields) {// only process our own fields. If (field. declaringtype = sourcetype) {// obtain a new value. Object value = field. getvalue (source); If (typeof (baseentity). isassignablefrom (field. fieldtype) {// This Is A subobject. check whether it is null only. If (State. Contains (field. declaringtype. Name + "! "+ Field. Name) {// It is null and is set to null. Field. setvalue (source, null);} else {If (value! = NULL) {// The restore method is called by the sub-object. (Baseentity) value). Restore () ;}}// restore if the dictionary contains this field. Else if (State. Contains (field. declaringtype. Name + "! "+ Field. Name) {field. setvalue (source, State [field. declaringtype. Name + "! "+ Field. Name]);} // check whether the value is a set. If yes, the restore method is called for each item. VaR oldvalue = State [field. declaringtype. Name + "! "+ Field. Name]; If (oldvalue! = NULL & oldvalue is icollection) {var Col = (icollection) oldvalue; foreach (VAR item in col) {If (item is baseentity) (baseentity) item ). restore () ;}}}// recursive upward call. Sourcetype = sourcetype. basetype;} while (sourcetype! = NULL) ;____ backup = NULL ;}# endregion
Okay. In this way, all our entities have this function, this method is not a complete deep copy, but a limited deep copy, because only data that inherits the baseentity class will be backed up and restored. However, this is enough in the project. Test:
VaR Product = servicefactory. getproductservice (). list (). results. first (); product. properties. each (P => console. writeline (P. propertyid); var COUNT = product. properties. count (); product. beginedit (); product. properties. add (New productproperty {id = guid. newguid (), isnewlyadded = true, propertyid = new GUID ("38b52cdb-0dbb-4183-aa0c-603d95b15885"), value = "test"}); assert. areequal (count + 1, product. properties. count (); product. canceledit (); assert. areequal (count, product. properties. count (); var prop = product. properties. first (); var propid = prop. propertyid; product. beginedit (); prop. propertyid = guid. newguid (); product. properties. remove (product. properties. last (); assert. areequal (count-1, product. properties. count (); product. canceledit (); assert. areequal (propid, product. properties. first (). propertyid); assert. areequal (count, product. properties. count ());
All passed. Well, the task is completed. :-D

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.