Table des matières
 C# surveille les changements dans les attributs de classe (les jouets que le chat tigré a déplacés)
Maison développement back-end Tutoriel C#.Net Partage de cas de code sur la façon de surveiller les modifications des attributs de classe en C#

Partage de cas de code sur la façon de surveiller les modifications des attributs de classe en C#

Jul 27, 2017 pm 04:15 PM
.net 代码

 C# surveille les changements dans les attributs de classe (les jouets que le chat tigré a déplacés)

Lors de l'utilisation d'EF pour mettre à jour les entités de la base de données. Souvent, nous souhaitons mettre à jour un ou plusieurs champs du tableau. Bien que nous puissions indiquer au contexte quels champs nous souhaitons mettre à jour via les paramètres. Mais généralement, nous encapsulons la couche de persistance des données. Par des opérations génériques. Pour le moment, nous n'avons aucun moyen de savoir quels champs ont été modifiés au niveau de l'application.

J'apprends également EF récemment et j'ai rencontré ce problème. Bien entendu, si vous l'utilisez directement au niveau de l'application, vous pouvez simplement définir le statut IsModified du champ. Comme suit
db.Entry(model).Property(x => x.Token).IsModified = false;
Cependant, cela se limite à l'apprentissage et démo. Dans le développement formel, ces opérations sous-jacentes ne sont généralement pas exposées au niveau de l'application. La couche de persistance de la base de données sera encapsulée. Ensuite, l'ajout, la suppression, la modification et l'interrogation sont fournis via la fabrique d'entités (entrepôt) et les génériques d'entité.
Pour plus de détails, vous pouvez vous référer à des articles tels que « Conception de modèles de référentiel basée sur Entity Framework ».
Ces méthodes ont une chose en commun Lors de la mise à jour et de la suppression, il existe des codes similaires comme suit :


    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;
            }
        }
Copier après la connexion

Compréhension personnelle : Update(TEntity TObject) transmet une entité à la méthode, puis l'attache au contexte de la base de données et marque les données comme modifiées. Puis mettez à jour.
Dans ce cas, tous les champs de l'entité seront mis à jour. Ensuite, nous devons nous assurer que cette entité est trouvée dans la base de données ou correspond aux enregistrements de la base de données. Cela ne pose aucun problème dans la structure C/S, mais qu’en est-il de la structure B/S ? Il nous est impossible de packager tous les champs de l'entité et de les envoyer au client. Ensuite, le client peut les modifier et les renvoyer au serveur, puis appeler la méthode Warehouse pour mettre à jour. Pour le dire de la manière la plus simple, pour changer le mot de passe utilisateur, nous n’avons besoin que d’un identifiant utilisateur et d’un nouveau mot de passe. Ou pour verrouiller un compte utilisateur, vous n'avez besoin que d'un identifiant utilisateur, d'un statut de verrouillage et d'une heure de verrouillage. De cette façon, il nous est impossible de regrouper l’intégralité de l’entité utilisateur et de la faire circuler. Certaines personnes disent que vous pouvez d'abord vérifier la base de données en fonction de l'ID lors de l'enregistrement, puis y ajouter les valeurs d'attribut modifiées avant la mise à jour. Cela nous ramène au problème : il n'y a que des types génériques dans la méthode Warehouse, et lorsque vous appelez la méthode Warehouse Update, vous lui transmettez un type d'entité. L'entrepôt ne sait pas quelle entité vous êtes et quels champs ont été mis à jour.
Bien sûr, grâce aux déclencheurs, nous savons que les mises à jour de la base de données sont d'abord supprimées puis insérées, il n'y a donc pas beaucoup de différence entre mettre à jour quelques champs et mettre à jour la colonne entière.

Mettez désormais de côté les informations telles que les mises à jour de l'entrepôt et autres génériques d'entité. Il suffit de regarder quand une entité change, comment pouvons-nous savoir quels attributs elle a modifiés.
Normalement, une entité ressemble à ceci


 1     /// <summary> 
 2     /// 一个具体的实体 
 3     /// </summary> 
 4     public class AccountEntity : MainEntity 
 5     { 
 6         /// <summary> 
 7         /// 文本类型 
 8         /// </summary> 
 9         public virtual string Account { get; set; }
 10         /// <summary>
 11         /// 又一个文本属性
 12         /// </summary>
 13         public virtual string Password { get; set; }
 14         /// <summary>
 15         /// 数字类型
 16         /// </summary>
 17         public virtual int Sex { get; set; }
 18         /// <summary>
 19         /// 事件类型
 20         /// </summary>
 21         public virtual DateTime Birthday { get; set; }
 22         /// <summary>
 23         /// 双精度浮点数
 24         /// </summary>
 25         public virtual double Height { get; set; }
 26         /// <summary>
 27         /// 十进制数
 28         /// </summary>
 29         public virtual decimal Monery { get; set; }
 30         /// <summary>
 31         /// 二进制
 32         /// </summary>
 33         public virtual byte[] PublicKey { get; set; }
 34         /// <summary>
 35         /// Guid类型
 36         /// </summary>
 37         public virtual Guid AreaId { get; set; }
 38     }
Copier après la connexion

Afficher le code

Lorsqu'on souhaite modifier les propriétés de cette entité :


var entity = new accountEntity();
entity.Id=1;
entity.Account = "给属性赋值&#39;;
Copier après la connexion

Passez ensuite cette entité à la couche sous-jacente pour opération.


db.Update(entity);
Copier après la connexion

 Pas de problème du tout, mais ma question est de savoir comment la couche inférieure sait-elle quelles propriétés ma couche d'application a modifiées ? Ajoutez une autre méthode pour indiquer à la couche inférieure que j'ai modifié ces attributs.


db.Update(entity,"Account");
Copier après la connexion

Il ne semble y avoir rien de mal à cela.

Mais que se passe-t-il si je modifie le compte mais que le mot de passe est passé en paramètre ? Par conséquent, il doit y avoir une collection sur l’entité pour stocker si l’intégralité de l’attribut a été modifié. Accédez ensuite à la méthode Update sous-jacente pour supprimer les champs mis à jour pour l’étape suivante.
A travers cette idée, j'ai pensé ajouter un dictionnaire à l'entité :


protected Dictionary<string, dynamic> FieldTracking = new Dictionary<string, dynamic>();
Copier après la connexion

Lors de l'affectation d'attribut , il est ajouté au dictionnaire. (Bien sûr, cette opération augmentera la surcharge du programme)


FieldTracking["Account"]="给属性赋值";
Copier après la connexion

  然后在底层在取出里面的集合,来区分哪些字段被修改(大花猫动了哪些小玩具)。

  改造下实体属性


        public virtual string Account
        {
            get
            { return _Account; }
            set {
                _Account = value;
                FieldTracking["Account"] = value;
            }
        }
Copier après la connexion

  看过编译后的IL代码的都知道,class中的属性最终会编译成两个方法 setvalue和getvalue,那么通过修改set方法添加FieldTracking["Account"] = value;就可以让属性在赋值的时候添加到字典中。

  很简单吧。


  你以为这样就完了。如果拿房间来比喻实体、拿玩具来比作属性。我家那大花猫就是修改实体属性的方法。你知道我家有多少玩具吗?你每天回家的时候你知道大花猫动了哪个小玩具吗?给每个玩具装个GPS?哈哈哈哈,别闹,花这心思还不如再买点回来。什么?买回来的还得装,算了。研究下怎么装吧。

  一个程序可能有上百个实体类,修改现有的实体类,给每个set加一行?作为一个程序员是不可能容忍做这样的操作的。写一个工具,读取所有的实体代码,加上这一行,保存。这是个好办法。那每次添加一个实体类就得调用工具重写来一遍,每次修改属性再调用一遍,恩。没问题。能用就行。这不是一个真心养猫的人的人能容忍的。

  那怎么办?把猫打死?那玩具的存在将会没有任何意义。想到一个办法,在我离开房子的时候(程序初始化),给房子里的所有房间(实体类)创建一个同样的房间(继承),包含了与原房间所有需要监控(标记为virtual)的玩具的复制,在复制过程中加上GPS(-_~)。然后给猫玩。猫通过我给的门进到这个继承的房间中玩所有玩具的时候,GPS就能将猫的动作全部记录下来。我一回家,这猫玩了哪些玩具一看GPS记录就全知道了。哟,这小崽子,在王元鹅呢。
  

  看不懂,没关系,上马:
  1、在程序集初始化的时候,通过反射,查找所有继承自BaseEntity的实体类。遍历其中的属性。找到标记为virtual进行复制。

    刚开始对于如果找到virtual属性花了不少时间。我总只想着在属性上找,却没想到去set_value方法上去找(其实get_value方法也是)。还是太菜啊。

    注:NoMapAttribute特性是一个自定义的标记,表示不参与映射。因为不参与映射就不需要监控。与本文章代码没有太大的关系。仅供参考。


//获取实体所在的程序集(ClassLibraryDemo)
var assemblyArray = AppDomain.CurrentDomain.GetAssemblies()
        .Where(w => w.GetName().Name == "ClassLibraryDemo")
        .ToList();
//实体的基类
var baseEntityType = typeof(BaseEntity);
//循环程序集
foreach (Assembly item in assemblyArray)
{
    //找到这个程序集中继承自基类的实体
    var types = item.GetTypes().Where(t => t.IsAbstract == false
        && baseEntityType.IsAssignableFrom(t) 
        && t != baseEntityType);
    foreach (Type btItem in types){
        //遍历这个实体类中的属性
var properties = btItem.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                        .Where(w => w.CanRead && w.CanWrite
                            && w.GetCustomAttributes(typeof(NoMapAttribute), false).Any() == false
                            //TODO:要不要检查get方法?
                            && w.GetSetMethod().IsVirtual);
    }
}
Copier après la connexion

  2、根据1的结果,复制一个新的房间(动态代码生成一个类,这个类继承1中的实体,并且重写了属性的set方法)

  这个过程就设计到动态代码的生成了。


//首先创建一个与实体类对应的动态类
CodeTypeDeclaration ct = new CodeTypeDeclaration(btItem.Name + "_Dynamic");
//循环实体中的所有标记为virtual的属性
foreach (PropertyInfo fiItem in properties)
{
	//创建一个属性
	var p = new CodeMemberProperty();
	//设置属性为公共、重写
	p.Attributes = MemberAttributes.Public | MemberAttributes.Override;//override
	//设置属性的类型为继承的属性的数据类型
	p.Type = new CodeTypeReference(fiItem.PropertyType);
	//属性名称与继承的一致
	p.Name = fiItem.Name;
	//包含set代码
	p.HasSet = true;
	//包含get代码
	p.HasGet = true;
	//设置get代码
	//return base.Account
	p.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                        new CodeBaseReferenceExpression(), fiItem.Name)));
	//设置set代码
	//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"));
	//将属性添加到类中
	ct.Members.Add(p);
}
Copier après la connexion

  3、将刚才生成的类加到原类所在的命名空间+".Dynamic"(加后缀以示区分)


//声明一个命名空间(与当前实体类同名+后缀)
CodeNamespace ns = new CodeNamespace(btItem.Namespace + ".Dynamic");
ns.Types.Add(ct);
Copier après la connexion

  4、编辑生成代码所在的程序集


    //要动态生成代码的程序集
    CodeCompileUnit program = new CodeCompileUnit();
    //添加引用
    program.ReferencedAssemblies.Add("mscorlib.dll");
    program.ReferencedAssemblies.Add("System.dll");
    program.ReferencedAssemblies.Add("System.Core.dll");

    //定义代码工厂
    CSharpCodeProvider provider = new CSharpCodeProvider();
    //编译程序集
    var cr = provider.CompileAssemblyFromDom(new System.CodeDom.Compiler.CompilerParameters();
    //看编译是否通过
    var error = cr.Errors;
    if (error.HasErrors)
    {
        Console.WriteLine("错误列表:");
        //编译不通过
        foreach (dynamic item in error)
        {
            Console.WriteLine("ErrorNumber:{0};Line:{1};ErrorText{2}",
                item.ErrorNumber,
                item.Line, 
                item.ErrorText);
        }
        return;
    }
    else
    {
        Console.WriteLine("编译成功。");
    }
Copier après la connexion

  查看生成的代码


//查看生成的代码
var codeText = new StringBuilder();
using (var codeWriter = new StringWriter(codeText))
{
    CodeDomProvider.CreateProvider("CSharp").GenerateCodeFromNamespace(ns,
        codeWriter,
        new CodeGeneratorOptions()
        {
            BlankLinesBetweenMembers = true
        });
}
Console.WriteLine(codeText);
Copier après la connexion

  5、将复制的新类与原类建立映射关系。


foreach (Type item in ts)
{
    //注册(模拟实现,通过字典实现的,也可以通过IOC注入方式处理)
    Mapping.Map(item.BaseType, item);
}
Copier après la connexion

  6、获得这个复制的实体对象


//创建一个指定的实体对象
AccountEntity ae = Mapping.GetMap<AccountEntity>();
Copier après la connexion

  7、对这个实体对象的属性进行赋值


//主键赋值不会修改属性更新
ae.BaseEntity_Id = 1;//不会变(未标记为virtual)
ae.MainEntity_Name = "大花猫";
ae.MainEntity_UpdateTime = DateTime.Now;
//修改某个属性
ae.Account = "admin";
ae.Account = "以最后一次的修改为准";
Copier après la connexion

  8、调用底层方法,底层根据这个实体属性获得被修改的属性名称


//调用基类中的方法 获取变动的属性
var up = ae.GetFieldTracking();
Console.WriteLine("有修改的字段:");
up.ForEach(fe =>
{
    Console.WriteLine(fe + ":" + ae[fe]);
});
Copier après la connexion

  9、完美

  

  就这样,在底层就能知道哪些实体被赋值过了。

  当然,有些实体我们只是需要用来计算,则可以调用方法将赋值过的属性进行删除


//删除变更字段
ae.RemoveChanges("Account");
Copier après la connexion

  这只是一个简单的实现,还有一种比较复杂的情况,在第6步,获得这个复制的实体对象时,怎么用一个现有的new出来的实体对象去创建建并监控呢。就像,别人送我一房间现成的玩具,给我的时候猫就在里面玩了。嗷,把猫打死吧。

  总结:

再次认识到反射的强大。
也第一次实现了代码生成代码并使用的经历。
对字段和属性的区别有了更深的认识。
对访问修饰符和虚virtual方法有了更好的认识。

 

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment résoudre le code 28 du pilote Win7 Comment résoudre le code 28 du pilote Win7 Dec 30, 2023 pm 11:55 PM

Certains utilisateurs ont rencontré des erreurs lors de l'installation du périphérique, provoquant le code d'erreur 28. En fait, cela est principalement dû au pilote. Il nous suffit de résoudre le problème du code de pilote Win7 28. Voyons ce qu'il faut faire. . Que faire avec le code 28 du pilote Win7 : Tout d'abord, nous devons cliquer sur le menu Démarrer dans le coin inférieur gauche de l'écran. Ensuite, recherchez et cliquez sur l'option "Panneau de configuration" dans le menu contextuel. Cette option est généralement située en bas ou près du bas du menu. Après avoir cliqué, le système ouvrira automatiquement l'interface du panneau de configuration. Dans le panneau de configuration, nous pouvons effectuer divers paramètres système et opérations de gestion. C'est la première étape du niveau de nettoyage nostalgique, j'espère que cela aidera. Ensuite, nous devons continuer et entrer dans le système et

Que faire si le code d'écran bleu 0x0000001 apparaît Que faire si le code d'écran bleu 0x0000001 apparaît Feb 23, 2024 am 08:09 AM

Que faire avec le code d'écran bleu 0x0000001. L'erreur d'écran bleu est un mécanisme d'avertissement en cas de problème avec le système informatique ou le matériel. Le code 0x0000001 indique généralement une panne de matériel ou de pilote. Lorsque les utilisateurs rencontrent soudainement une erreur d’écran bleu lors de l’utilisation de leur ordinateur, ils peuvent se sentir paniqués et perdus. Heureusement, la plupart des erreurs d’écran bleu peuvent être dépannées et traitées en quelques étapes simples. Cet article présentera aux lecteurs certaines méthodes pour résoudre le code d'erreur d'écran bleu 0x0000001. Tout d'abord, lorsque nous rencontrons une erreur d'écran bleu, nous pouvons essayer de redémarrer

L'ordinateur affiche fréquemment des écrans bleus et le code est différent à chaque fois L'ordinateur affiche fréquemment des écrans bleus et le code est différent à chaque fois Jan 06, 2024 pm 10:53 PM

Le système Win10 est un très excellent système à haute intelligence. Sa puissante intelligence peut apporter la meilleure expérience utilisateur aux utilisateurs. Dans des circonstances normales, les ordinateurs du système Win10 des utilisateurs n'auront aucun problème ! Cependant, il est inévitable que divers défauts se produisent sur d'excellents ordinateurs. Récemment, des amis ont signalé que leurs systèmes Win10 rencontraient fréquemment des écrans bleus ! Aujourd'hui, l'éditeur vous proposera des solutions aux différents codes qui provoquent des écrans bleus fréquents sur les ordinateurs Windows 10. Jetons un coup d'œil. Solutions aux écrans bleus fréquents de l'ordinateur avec des codes différents à chaque fois : causes des différents codes d'erreur et suggestions de solutions 1. Cause de l'erreur 0×000000116 : Il se peut que le pilote de la carte graphique soit incompatible. Solution : Il est recommandé de remplacer le pilote d'origine du fabricant. 2,

Résoudre l'erreur du code 0xc000007b Résoudre l'erreur du code 0xc000007b Feb 18, 2024 pm 07:34 PM

Code de terminaison 0xc000007b Lors de l'utilisation de votre ordinateur, vous rencontrez parfois divers problèmes et codes d'erreur. Parmi eux, le code de terminaison est le plus inquiétant, notamment le code de terminaison 0xc000007b. Ce code indique qu'une application ne peut pas démarrer correctement, provoquant des désagréments pour l'utilisateur. Tout d’abord, comprenons la signification du code de terminaison 0xc000007b. Ce code est un code d'erreur du système d'exploitation Windows qui se produit généralement lorsqu'une application 32 bits tente de s'exécuter sur un système d'exploitation 64 bits. Cela signifie que ça devrait

Explication détaillée des causes et des solutions du code d'écran bleu 0x0000007f Explication détaillée des causes et des solutions du code d'écran bleu 0x0000007f Dec 25, 2023 pm 02:19 PM

L'écran bleu est un problème que nous rencontrons souvent lors de l'utilisation du système. Selon le code d'erreur, il existe de nombreuses raisons et solutions différentes. Par exemple, lorsque nous rencontrons le problème d'arrêt : 0x0000007f, il peut s'agir d'une erreur matérielle ou logicielle. Suivons l'éditeur pour découvrir la solution. Raison du code d'écran bleu 0x000000c5 : Réponse : La mémoire, le processeur et la carte graphique sont soudainement overclockés ou le logiciel ne fonctionne pas correctement. Solution 1 : 1. Continuez à appuyer sur F8 pour entrer lors du démarrage, sélectionnez le mode sans échec et appuyez sur Entrée pour entrer. 2. Après être entré en mode sans échec, appuyez sur win+r pour ouvrir la fenêtre d'exécution, entrez cmd et appuyez sur Entrée. 3. Dans la fenêtre d'invite de commande, saisissez « chkdsk /f /r », appuyez sur Entrée, puis appuyez sur la touche y. 4.

Programme de codes à distance universels GE sur n'importe quel appareil Programme de codes à distance universels GE sur n'importe quel appareil Mar 02, 2024 pm 01:58 PM

Si vous devez programmer un appareil à distance, cet article vous aidera. Nous partagerons les meilleurs codes de télécommande universelle GE pour programmer n’importe quel appareil. Qu'est-ce qu'une télécommande GE ? GEUniversalRemote est une télécommande qui peut être utilisée pour contrôler plusieurs appareils tels que les téléviseurs intelligents, LG, Vizio, Sony, Blu-ray, DVD, DVR, Roku, AppleTV, lecteurs multimédias en streaming et plus encore. Les télécommandes GEUniversal sont disponibles en différents modèles avec différentes caractéristiques et fonctions. GEUniversalRemote peut contrôler jusqu'à quatre appareils. Les meilleurs codes de télécommande universels à programmer sur n'importe quel appareil. Les télécommandes GE sont livrées avec un ensemble de codes qui leur permettent de fonctionner avec différents appareils. vous pouvez

Que représente le code écran bleu 0x000000d1 ? Que représente le code écran bleu 0x000000d1 ? Feb 18, 2024 pm 01:35 PM

Que signifie le code d'écran bleu 0x000000d1 ? Ces dernières années, avec la popularisation des ordinateurs et le développement rapide d'Internet, les problèmes de stabilité et de sécurité du système d'exploitation sont devenus de plus en plus importants. Les erreurs d’écran bleu sont un problème courant, le code 0x000000d1 en fait partie. Une erreur d'écran bleu, ou « Écran bleu de la mort », est une condition qui se produit lorsqu'un ordinateur subit une panne système grave. Lorsque le système ne parvient pas à récupérer de l'erreur, le système d'exploitation Windows affiche un écran bleu avec le code d'erreur à l'écran. Ces codes d'erreur

Un guide rapide pour apprendre le dessin Python : exemple de code pour dessiner des glaçons Un guide rapide pour apprendre le dessin Python : exemple de code pour dessiner des glaçons Jan 13, 2024 pm 02:00 PM

Démarrez rapidement avec le dessin Python : exemple de code pour dessiner Bingdundun Python est un langage de programmation puissant et facile à apprendre. En utilisant la bibliothèque de dessins de Python, nous pouvons facilement réaliser divers besoins de dessin. Dans cet article, nous utiliserons la bibliothèque de dessins de Python matplotlib pour dessiner des graphiques simples de glace. Bingdundun est un panda avec une jolie image et est très populaire parmi les enfants. Tout d’abord, nous devons installer la bibliothèque matplotlib. Vous pouvez le faire en exécutant dans le terminal

See all articles