Code case sharing on how to monitor class property changes in C #

Source: Internet
Author: User


C # Monitoring class properties changes (Big cat's small toys moved)



When you use EF to update database entities. A lot of the time we want is to update one or some of the fields in the table. Although it is possible to tell the context by setting the field we want to update. But in general we will encapsulate the data persistence layer. Through generic operations. And then we don't know what fields have been modified at the application level.



I've been learning about EF recently, and that's just the problem. Of course, if used directly at the application level, it is possible to set the ismodified state of the field. As follows
Db. Entry (model). Property (x = X.token). IsModified = false;
However, this is limited to learning and demo. Formal development generally does not expose this underlying operation to the application level. Will encapsulate the persistence layer of the database. And then through the Physical Factory (warehouse) plus entity generic way to provide additions and deletions to search.
Specifically, you can refer to articles such as repository model design based on the Entity Framework.
This type of approach has one thing in common, with the following similar code when updating and deleting:


public virtual void Update(TEntity TObject)

    {

        try

        {

            var entry = Context.Entry(TObject);

            Context.Set<TEntity>().Attach(TObject);

            entry.State = EntityState.Modified;

        }

        catch (OptimisticConcurrencyException ex)

        {

            throw ex;

        }

    }



Personal understanding: Update (TEntity tobject) by passing an entity to the method, then attaching to the database context, and marking the data as a modified state. Then make the update.
This situation updates all the fields of the entity. Then we need to make sure that the entity is checked out of the database, or that it corresponds to the database record. This is not a problem in the C/s structure, but the problem is in the B/s structure? It is not possible to package all of the entity's fields, send them to the client, and then modify the client to return to the server and then call the Warehouse method update. Say the simplest, modify the user password, we only need a user ID, a new password on it. or lock the user account, only need a user ID, a lock status, a lock time. In this way, we can not package the entire user entity to pass it. Some people say that you can save the time based on the ID to check the database, and then add the modified attribute value up and then update on it. This goes back to the question: there is only a generic type in the warehouse method, and you are passing an entity type when you call the warehouse Update method. The warehouse does not know that you are the entity, and which fields are updated.
Of course, through triggers we know that the database updates are first deleted, so the update of several fields and the whole column update the underlying operation is not much different.



Now throw away information such as entity generics such as warehouse updates. Just look at it. When an entity changes, how can we know which attributes he modified.
Under normal circumstances, an entity grows like this


/// <summary>

2 /// A specific entity

3 /// </summary>

4 public class AccountEntity : MainEntity

5 {

6 /// <summary>

7 /// text type

8 /// </summary>

9 public virtual string Account { get; set; }

10 /// <summary>

11 /// Another text attribute

12 /// </summary>

13 public virtual string Password { get; set; }

14 /// <summary>

15 /// Number type

16 /// </summary>

17 public virtual int Sex { get; set; }

18 /// <summary>

19 /// Event type

20 /// </summary>

21 public virtual DateTime Birthday { get; set; }

22 /// <summary>

23 /// Double precision floating point number

24 /// </summary>

25 public virtual double Height { get; set; }

26 /// <summary>

27 /// Decimal number

28 /// </summary>

29 public virtual decimal Monery { get; set; }

30 /// <summary>

31 /// Binary

32 /// </summary>

33 public virtual byte[] PublicKey { get; set; }

34 /// <summary>

35 /// Guid type

36 /// </summary>

37 public virtual Guid AreaId { get; set; }

38 }



View Code



When we want to modify the properties of this entity:


Var entity = new accountEntity();

entity.Id=1;

entity.Account = "Assign value to attribute";



The entity is then passed on to the underlying operation.





Db. Update (entity);


  No problem at all, but my problem at the bottom how to know my application layer modified those properties? Add a method to tell the bottom, I modified these several properties.





Db. Update (Entity, "account");


There seems to be nothing to do.



But then, what if I change the account, but I pass the password in the argument? Therefore, there should be a collection on the entity to store whether the entire property has a modified state. Then go to the underlying Update method to remove the updated fields for next steps.
With this idea, I want to add a dictionary to the entity:





Protected Dictionary<string, dynamic> fieldtracking = new dictionary<string, dynamic> ();


When the property is assigned a value, it is added to the dictionary. (Of course, this will increase the cost of the program)





fieldtracking["Account"]= "Assign value to attribute";


Then at the bottom of the collection inside out, to distinguish which fields are modified (the cat moved the small toys).

Transformation of entity Properties



public virtual string Account

{

    get

    { return _Account; }

    set {

        _Account = value;

        FieldTracking["Account"] = value;

    }

}



After seeing the compiled IL code, we know that the attributes in class will eventually be compiled into two methods SetValue and GetValue, then add fieldtracking["Account" = value by modifying the Set method; You can have the attribute added to the dictionary when the value is assigned.



It's simple.




You think it's over. If you take a room to compare entities, take toys to attribute. My big cat is the way to modify the entity properties. Do you know how many toys there are in my family? When you go home every day, do you know which little toy the tabby cat moved? Put a GPS on every toy? haha haha, don't make trouble, spend this thought is better than buy some back. What the? Buy back to be installed, forget it. How to put it under study.



A program may have hundreds of entity classes, modify existing entity classes, add a row to each set? As a programmer, it is impossible to tolerate such operations. Write a tool that reads all the entity codes, plus this line, save. That's a good idea. Each time you add an entity class, you have to invoke the tool rewrite to do it again, each time you modify the property and call it again, yes. No problem. You can use it on the line. This is not a person who really has a cat to tolerate.

So what? Kill the Cat? The existence of that toy will make no sense. Think of a way, when I leave the House (program initialization), to create an identical room (inheritance) for all the rooms in the house (the entity Class), including the copy of all the toys that need to be monitored (marked as virtual) in the original room, plus GPS (-_~) during the copy process. Then play for the cat. The cat was able to record all of the cat's movements by entering the door into the inherited room and playing all the toys. As soon as I got home, the cat played with the toys and looked at the GPS records and knew it all. Yo, this son of a Wang Yuan goose.
  



Don't understand, it doesn't matter, horse:
1, in the initialization of the Assembly, through reflection, to find all inherited from the BaseEntity entity class. Iterates over the properties within it. Locate the tag for virtual replication.



At first it took some time to find the virtual property. I always just want to find in the attribute, but did not expect to go to Set_value method to look up (in fact, Get_value method is also). It's too much food.



Note: The Nomapattribute attribute is a custom tag that indicates that the mapping is not involved. Because you do not participate in mapping, you do not need to monitor. There is not much of a relationship with this article code. For reference only.


/ / Get the assembly where the entity is located (ClassLibraryDemo)

Var assemblyArray = AppDomain.CurrentDomain.GetAssemblies()

         .Where(w => w.GetName().Name == "ClassLibraryDemo")

         .ToList();

//The base class of the entity

Var baseEntityType = typeof(BaseEntity);

/ / Loop assembly

Foreach (Assembly item in assemblyArray)

{

     / / Find the entity in this assembly that inherits from the base class

     Var types = item.GetTypes().Where(t => t.IsAbstract == false

         && baseEntityType.IsAssignableFrom(t)

         && t != baseEntityType);

     Foreach (Type btItem in types){

         / / Traverse the attributes in this entity class

Var properties = btItem.GetProperties(BindingFlags.Public | BindingFlags.Instance)

                         .Where(w => w.CanRead && w.CanWrite

                             && w.GetCustomAttributes(typeof(NoMapAttribute), false).Any() == false

                             //TODO: Do you want to check the get method?

                             && w.GetSetMethod().IsVirtual);

     }

}



2, according to the results of 1, copy a new room (dynamic code generates a class, this class inherits the entity in 1, and overrides the property's set method)



This process is designed to generate dynamic code.


/ / First create a dynamic class corresponding to the entity class

CodeTypeDeclaration ct = new CodeTypeDeclaration(btItem.Name + "_Dynamic");

/ / All attributes in the loop entity marked as virtual

Foreach (PropertyInfo fiItem in properties)

{

    / / Create a property

    Var p = new CodeMemberProperty();

    / / Set the property to public, rewrite

    p.Attributes = MemberAttributes.Public | MemberAttributes.Override;//override

    / / Set the type of the property to the data type of the inherited property

    p.Type = new CodeTypeReference(fiItem.PropertyType);

    / / Attribute name and inheritance consistent

    p.Name = fiItem.Name;

    / / Contains set code

    p.HasSet = true;

    / / Contain get code

    p.HasGet = true;

    / / Set the get code

    //return base.Account

    p.GetStatements.Add(new CodeMethodReturnStatement(

                New CodeFieldReferenceExpression(

                        New CodeBaseReferenceExpression(), fiItem.Name)));

    / / Set the set code

    //base.Account=value;

    p.SetStatements.Add(

    New CodeAssignStatement(

                New CodeFieldReferenceExpression(

                        New CodeBaseReferenceExpression(), fiItem.Name),

    New CodePropertySetValueReferenceExpression()));

    //FieldTracking["Account"]=value;

    p.SetStatements.Add(new CodeSnippetExpression("FieldTracking[\"" + fiItem.Name + "\"] = value"));

    / / Add attributes to the class

    ct.Members.Add(p);

}



3. Add the class you just generated to the namespace + "where the original class is located." Dynamic "(plus suffix to differentiate)



/ / Declare a namespace (same name + suffix with the current entity class)

CodeNamespace ns = new CodeNamespace(btItem.Namespace + ".Dynamic");

ns.Types.Add(ct);



  4. Edit the assembly where the generated code resides



/ / The assembly to dynamically generate code

CodeCompileUnit program = new CodeCompileUnit();

/ / Add a reference

program.ReferencedAssemblies.Add("mscorlib.dll");

program.ReferencedAssemblies.Add("System.dll");

program.ReferencedAssemblies.Add("System.Core.dll");

 

/ / Define the code factory

CSharpCodeProvider provider = new CSharpCodeProvider();

/ / Compile the assembly

Var cr = provider.CompileAssemblyFromDom(new System.CodeDom.Compiler.CompilerParameters();

/ / See if the compiler passed

Var error = cr.Errors;

If (error.HasErrors)

{

     Console.WriteLine("error list:");

     //Compile does not pass

     Foreach (dynamic item in error)

     {

         Console.WriteLine("ErrorNumber:{0};Line:{1};ErrorText{2}",

             item.ErrorNumber,

             item.Line,

             item.ErrorText);

     }

     Return;

}

Else

{

     Console.WriteLine("Compile successfully.");

}



To view the generated code



/ / View the generated code

Var codeText = new StringBuilder();

Using (var codeWriter = new StringWriter(codeText))

{

     CodeDomProvider.CreateProvider("CSharp").GenerateCodeFromNamespace(ns,

         codeWriter,

         New CodeGeneratorOptions()

         {

             BlankLinesBetweenMembers = true

         });

}

Console.WriteLine(codeText);



5, the new copy of the class and the original class to establish a mapping relationship.



Foreach (Type item in ts)

{

     / / Registration (simulation implementation, through the dictionary, can also be processed by IOC injection)

     Mapping.Map(item.BaseType, item);

}



6. Obtain this copied entity object





Creates a specified Entity object accountentity AE = mapping.getmap<accountentity> ();


7. Assign values to attributes of this entity object



/ / Primary key assignment does not modify the property update

ae.BaseEntity_Id = 1; / / does not change (not marked as virtual)

ae.MainEntity_Name = "big cat";

ae.MainEntity_UpdateTime = DateTime.Now;

/ / Modify a property

ae.Account = "admin";

ae.Account = "Subject to the last modification";



8, call the underlying method, the underlying property to obtain the modified property name


/ / Call the method in the base class to get the changed properties

Var up = ae.GetFieldTracking();

Console.WriteLine("modified field:");

up.ForEach(fe =>

{

     Console.WriteLine(fe + ":" + ae[fe]);

});



9. Perfect



  





In this way, it is possible to know which entities have been assigned at the bottom.



Of course, some entities we just need to calculate, you can call the method to delete the Assigned property





Delete the change field AE. Removechanges ("account");


This is just a simple implementation, there is a more complicated situation, in the 6th step, to get the copied entity object, how to use an existing new entity object to create and monitor it. Like, someone gave me a room ready-made toys, give me when the cat played in it. Aw, kill the cat.



Summarize:



Again to recognize the power of reflection.
It is also the first time that code generation code has been implemented and used.
A deeper understanding of the differences between fields and properties.
There is a better understanding of access modifiers and virtual virtual methods.


Related Article

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.