C#에서 클래스 속성 변경을 모니터링하는 방법에 대한 코드 사례 공유
C#은 클래스 속성의 변경 사항(큰 고양이가 어떤 장난감을 움직였는지)을 모니터링합니다.
EF를 사용하여 데이터베이스 엔터티를 업데이트하는 경우. 우리가 원하는 것은 테이블의 하나 또는 일부 필드를 업데이트하는 것입니다. 설정을 통해 어떤 필드를 업데이트할지 컨텍스트에 알릴 수 있습니다. 그러나 일반적으로 우리는 데이터 지속성 계층을 캡슐화합니다. 일반 작업을 통해. 현재로서는 애플리케이션 수준에서 어떤 필드가 수정되었는지 알 수 있는 방법이 없습니다.
저도 최근에 EF를 배우고 있는데 우연히 이런 문제를 접하게 되었습니다. 물론 애플리케이션 수준에서 직접 사용하는 경우 필드의 IsModified 상태만 설정할 수 있습니다. 다음과 같습니다
db.Entry(model).Property(x => x.Token).IsModified = false;
단, 학습 및 데모에 한합니다. 공식 개발에서 이러한 기본 작업은 일반적으로 애플리케이션 수준에 노출되지 않습니다. 데이터베이스 지속성 계층이 캡슐화됩니다. 그리고 엔터티 팩토리(웨어하우스)와 엔터티 Generic을 통해 추가, 삭제, 수정, 조회가 제공됩니다.
자세한 내용은 "Entity Framework 기반 리포지토리 패턴 디자인" 등의 기사를 참조하세요.
이러한 메서드에는 모두 업데이트 및 삭제 시 다음과 같은 유사한 코드가 있습니다.
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; } }
개인적 이해: 업데이트(TEntity TObject)가 엔터티를 메서드에 전달한 다음 이를 연결합니다. 데이터베이스 컨텍스트에 추가하고 데이터를 수정된 것으로 표시합니다. 그런 다음 업데이트하십시오.
이 경우 엔터티의 모든 필드가 업데이트됩니다. 그런 다음 이 엔터티가 데이터베이스에서 발견되었는지 또는 데이터베이스의 레코드와 일치하는지 확인해야 합니다. C/S 구조에서는 문제가 없는데 B/S 구조에서는 어떤가요? 엔터티의 모든 필드를 패키징하여 클라이언트에 보내는 것은 불가능합니다. 그런 다음 클라이언트는 이를 수정하여 서버에 반환한 다음 웨어하우스 메서드를 호출하여 업데이트할 수 있습니다. 가장 간단하게 말하면 사용자 비밀번호를 변경하려면 사용자 ID와 새 비밀번호만 있으면 됩니다. 또는 사용자 계정을 잠그려면 사용자 ID, 잠금 상태, 잠금 시간만 있으면 됩니다. 이러한 방식으로 전체 사용자 엔터티를 패키지하여 전달하는 것은 불가능합니다. 어떤 분들은 저장할 때 ID를 기준으로 데이터베이스를 먼저 확인한 뒤, 수정된 속성값을 추가한 뒤 업데이트하면 된다고 말씀하시는 분들도 계십니다. 이는 다시 문제로 돌아갑니다. 웨어하우스 메소드에는 일반 유형만 있고 웨어하우스 업데이트 메소드를 호출하면 엔터티 유형을 전달합니다. 창고에서는 귀하가 어떤 법인인지, 어떤 필드가 업데이트되었는지 알 수 없습니다.
물론 트리거를 통해 데이터베이스 업데이트가 먼저 삭제된 다음 삽입된다는 것을 알고 있으므로 몇 가지 필드를 업데이트하는 것과 전체 열을 업데이트하는 것에는 큰 차이가 없습니다.
이제 창고 업데이트 및 기타 엔터티 일반 정보와 같은 정보는 제쳐두세요. 엔터티가 변경될 때 어떤 속성이 수정되었는지 어떻게 알 수 있는지 살펴보세요.
일반적으로 엔터티는 다음과 같습니다.
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 }
코드 보기
이 엔터티의 속성을 수정하려는 경우:
var entity = new accountEntity(); entity.Id=1; entity.Account = "给属性赋值';
그럼 이건 엔터티는 조작을 위해 기본 레이어로 전달됩니다.
db.Update(entity);
전혀 문제가 없지만 내 문제는 내 애플리케이션 레이어가 어떤 속성을 수정했는지 맨 아래 레이어가 어떻게 알 수 있느냐는 것입니다. 이러한 속성을 수정했음을 맨 아래 레이어에 알리는 또 다른 방법을 추가합니다.
db.Update(entity,"Account");
별 문제 없는 것 같습니다.
그런데 계정을 수정했는데 매개변수에 비밀번호가 전달되면 어떻게 되나요? 따라서 전체 특성이 수정되었는지 여부에 대한 상태를 저장하려면 엔터티에 대한 컬렉션이 있어야 합니다. 그런 다음 기본 Update 메서드로 이동하여 다음 단계를 위해 업데이트된 필드를 가져옵니다.
이 아이디어를 통해 엔터티에 사전을 추가하려고 생각했습니다.
protected Dictionary<string, dynamic> FieldTracking = new Dictionary<string, dynamic>();
속성에 값이 할당되면 사전에 추가됩니다. (물론 이 작업은 프로그램의 오버헤드를 증가시킵니다)
FieldTracking["Account"]="给属性赋值";
然后在底层在取出里面的集合,来区分哪些字段被修改(大花猫动了哪些小玩具)。
改造下实体属性
public virtual string Account { get { return _Account; } set { _Account = value; FieldTracking["Account"] = value; } }
看过编译后的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); } }
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); }
3、将刚才生成的类加到原类所在的命名空间+".Dynamic"(加后缀以示区分)
//声明一个命名空间(与当前实体类同名+后缀) CodeNamespace ns = new CodeNamespace(btItem.Namespace + ".Dynamic"); ns.Types.Add(ct);
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("编译成功。"); }
查看生成的代码
//查看生成的代码 var codeText = new StringBuilder(); using (var codeWriter = new StringWriter(codeText)) { CodeDomProvider.CreateProvider("CSharp").GenerateCodeFromNamespace(ns, codeWriter, new CodeGeneratorOptions() { BlankLinesBetweenMembers = true }); } Console.WriteLine(codeText);
5、将复制的新类与原类建立映射关系。
foreach (Type item in ts) { //注册(模拟实现,通过字典实现的,也可以通过IOC注入方式处理) Mapping.Map(item.BaseType, item); }
6、获得这个复制的实体对象
//创建一个指定的实体对象 AccountEntity ae = Mapping.GetMap<AccountEntity>();
7、对这个实体对象的属性进行赋值
//主键赋值不会修改属性更新 ae.BaseEntity_Id = 1;//不会变(未标记为virtual) ae.MainEntity_Name = "大花猫"; ae.MainEntity_UpdateTime = DateTime.Now; //修改某个属性 ae.Account = "admin"; ae.Account = "以最后一次的修改为准";
8、调用底层方法,底层根据这个实体属性获得被修改的属性名称
//调用基类中的方法 获取变动的属性 var up = ae.GetFieldTracking(); Console.WriteLine("有修改的字段:"); up.ForEach(fe => { Console.WriteLine(fe + ":" + ae[fe]); });
9、完美
就这样,在底层就能知道哪些实体被赋值过了。
当然,有些实体我们只是需要用来计算,则可以调用方法将赋值过的属性进行删除
//删除变更字段 ae.RemoveChanges("Account");
这只是一个简单的实现,还有一种比较复杂的情况,在第6步,获得这个复制的实体对象时,怎么用一个现有的new出来的实体对象去创建建并监控呢。就像,别人送我一房间现成的玩具,给我的时候猫就在里面玩了。嗷,把猫打死吧。
总结:
再次认识到反射的强大。
也第一次实现了代码生成代码并使用的经历。
对字段和属性的区别有了更深的认识。
对访问修饰符和虚virtual方法有了更好的认识。
위 내용은 C#에서 클래스 속성 변경을 모니터링하는 방법에 대한 코드 사례 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











일부 사용자는 장치를 설치할 때 오류 코드 28을 표시하는 오류가 발생했습니다. 실제로 이는 주로 드라이버 때문입니다. win7 드라이버 코드 28의 문제만 해결하면 됩니다. 수행해야 할 작업을 살펴보겠습니다. 그것. win7 드라이버 코드 28로 수행할 작업: 먼저 화면 왼쪽 하단에 있는 시작 메뉴를 클릭해야 합니다. 그런 다음 팝업 메뉴에서 "제어판" 옵션을 찾아 클릭하세요. 이 옵션은 일반적으로 메뉴 하단이나 그 근처에 있습니다. 클릭하면 시스템이 자동으로 제어판 인터페이스를 엽니다. 제어판에서는 다양한 시스템 설정 및 관리 작업을 수행할 수 있습니다. 이것이 향수 청소 수준의 첫 번째 단계입니다. 도움이 되기를 바랍니다. 그런 다음 계속해서 시스템에 들어가야 합니다.

블루 스크린 코드 0x0000001로 수행할 작업 블루 스크린 오류는 컴퓨터 시스템이나 하드웨어에 문제가 있을 때 나타나는 경고 메커니즘입니다. 코드 0x0000001은 일반적으로 하드웨어 또는 드라이버 오류를 나타냅니다. 사용자가 컴퓨터를 사용하는 동안 갑자기 블루 스크린 오류가 발생하면 당황하고 당황할 수 있습니다. 다행히도 대부분의 블루 스크린 오류는 몇 가지 간단한 단계를 통해 문제를 해결하고 처리할 수 있습니다. 이 기사에서는 독자들에게 블루 스크린 오류 코드 0x0000001을 해결하는 몇 가지 방법을 소개합니다. 먼저, 블루 스크린 오류가 발생하면 다시 시작해 보세요.

win10 시스템은 매우 뛰어난 지능 시스템으로 사용자에게 최고의 사용자 경험을 제공할 수 있습니다. 정상적인 상황에서는 사용자의 win10 시스템 컴퓨터에 아무런 문제가 없습니다! 그러나 우수한 컴퓨터에서는 다양한 오류가 발생하는 것은 불가피합니다. 최근 친구들은 win10 시스템에서 블루 스크린이 자주 발생한다고 보고했습니다. 오늘 편집자는 Windows 10 컴퓨터에서 자주 블루 스크린을 발생시키는 다양한 코드에 대한 솔루션을 제공합니다. 매번 다른 코드로 자주 나타나는 컴퓨터 블루 스크린에 대한 해결 방법: 다양한 오류 코드의 원인 및 해결 방법 제안 1. 0×000000116 오류의 원인: 그래픽 카드 드라이버가 호환되지 않는 것이어야 합니다. 해결책: 원래 제조업체의 드라이버를 교체하는 것이 좋습니다. 2,

종료 코드 0xc000007b 컴퓨터를 사용하는 동안 때때로 다양한 문제와 오류 코드가 발생할 수 있습니다. 그 중 종료코드가 가장 충격적이며, 특히 종료코드 0xc000007b가 가장 충격적이다. 이 코드는 애플리케이션이 제대로 시작되지 않아 사용자에게 불편을 초래함을 나타냅니다. 먼저 종료코드 0xc000007b의 의미를 알아보겠습니다. 이 코드는 32비트 응용 프로그램이 64비트 운영 체제에서 실행을 시도할 때 일반적으로 발생하는 Windows 운영 체제 오류 코드입니다. 그래야 한다는 뜻이다

블루 스크린은 시스템을 사용할 때 자주 발생하는 문제입니다. 오류 코드에 따라 다양한 원인과 해결 방법이 있습니다. 예를 들어 stop: 0x0000007f 문제가 발생하면 하드웨어 또는 소프트웨어 오류일 수 있습니다. 편집기를 따라 해결책을 찾아보겠습니다. 0x000000c5 블루 스크린 코드 이유: 답변: 메모리, CPU 및 그래픽 카드가 갑자기 오버클럭되었거나 소프트웨어가 잘못 실행되고 있습니다. 해결 방법 1: 1. 부팅할 때 F8을 계속 눌러 들어가고 안전 모드를 선택한 다음 Enter를 눌러 들어갑니다. 2. 안전모드 진입 후 win+r을 눌러 실행창을 열고 cmd를 입력한 후 Enter를 누릅니다. 3. 명령 프롬프트 창에서 "chkdsk /f /r"을 입력하고 Enter를 누른 다음 y 키를 누릅니다. 4.

장치를 원격으로 프로그래밍해야 하는 경우 이 문서가 도움이 될 것입니다. 우리는 모든 장치 프로그래밍을 위한 최고의 GE 범용 원격 코드를 공유할 것입니다. GE 리모콘이란 무엇입니까? GEUniversalRemote는 스마트 TV, LG, Vizio, Sony, Blu-ray, DVD, DVR, Roku, AppleTV, 스트리밍 미디어 플레이어 등과 같은 여러 장치를 제어하는 데 사용할 수 있는 리모컨입니다. GEUniversal 리모컨은 다양한 기능과 기능을 갖춘 다양한 모델로 제공됩니다. GEUniversalRemote는 최대 4개의 장치를 제어할 수 있습니다. 모든 장치에서 프로그래밍할 수 있는 최고의 범용 원격 코드 GE 리모컨에는 다양한 장치에서 작동할 수 있는 코드 세트가 함께 제공됩니다. 당신은 할 수있다

0x000000d1 블루 스크린 코드는 무엇을 의미합니까? 최근 몇 년 동안 컴퓨터의 대중화와 인터넷의 급속한 발전으로 인해 운영 체제의 안정성 및 보안 문제가 점점 더 부각되고 있습니다. 일반적인 문제는 블루 스크린 오류이며, 코드 0x000000d1이 그 중 하나입니다. 블루 스크린 오류 또는 "죽음의 블루 스크린"은 컴퓨터에 심각한 시스템 오류가 발생할 때 발생하는 상태입니다. 시스템이 오류로부터 복구할 수 없는 경우 Windows 운영 체제는 화면에 오류 코드와 함께 블루 스크린을 표시합니다. 이러한 오류 코드

Python 그리기를 빠르게 시작하세요: 그리기를 위한 코드 예제 Bingdundun Python은 배우기 쉽고 강력한 프로그래밍 언어입니다. Python의 그리기 라이브러리를 사용하면 다양한 그리기 요구 사항을 쉽게 실현할 수 있습니다. 이 기사에서는 Python의 그리기 라이브러리 matplotlib를 사용하여 간단한 얼음 그래프를 그릴 것입니다. 빙둔둔은 귀여운 이미지를 지닌 판다로 어린이들에게 인기가 매우 높습니다. 먼저 matplotlib 라이브러리를 설치해야 합니다. 터미널에서 실행하면 됩니다.
