真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享-創(chuàng)新互聯(lián)

小編給大家分享一下C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

成都創(chuàng)新互聯(lián)自2013年起,先為河曲等服務(wù)建站,河曲等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為河曲企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題?! #監(jiān)控類屬性的更改(大花貓動(dòng)了哪些小玩具)  在使用EF更新數(shù)據(jù)庫(kù)實(shí)體時(shí)。很多時(shí)候我們想要的只是更新表中的某一個(gè)或部分字段。雖然可以通過(guò)設(shè)置來(lái)告訴上下文我們要更新的字段。但是一般我們都會(huì)把數(shù)據(jù)持久層封裝起來(lái)。通過(guò)泛型操作。而這時(shí)我們就無(wú)法得知應(yīng)用層面修改了哪些字段了。

  最近也在學(xué)習(xí)EF,就正好遇到了這個(gè)問(wèn)題。當(dāng)然,如果直接在應(yīng)用層面使用,通過(guò)設(shè)置字段的IsModified狀態(tài)就可以了。如下
  db.Entry(model).Property(x => x.Token).IsModified = false;
  可是,這僅限于學(xué)習(xí)和demo。正式開(kāi)發(fā)中一般是不會(huì)把這種底層操作公開(kāi)給應(yīng)用層面的。都會(huì)把數(shù)據(jù)庫(kù)持久層進(jìn)行封裝。然后通過(guò)實(shí)體工廠(倉(cāng)庫(kù))加實(shí)體泛型的方式提供增刪改查。
  這類方式都有一個(gè)共同點(diǎn),更新和刪除的時(shí)候都有如下類似代碼:

    public virtual void Update(TEntity TObject)
        {
            try
            {
                var entry = Context.Entry(TObject);
                Context.Set().Attach(TObject);
                entry.State = EntityState.Modified;
            }
            catch (OptimisticConcurrencyException ex)
            {
                throw ex;
            }
        }

  個(gè)人理解:Update(TEntity TObject)通過(guò)傳遞一個(gè)實(shí)體到方法,然后附加到數(shù)據(jù)庫(kù)上下文,并將數(shù)據(jù)標(biāo)記為修改狀態(tài)。然后進(jìn)行的更新。
  這種情況會(huì)對(duì)實(shí)體的所有字段進(jìn)行更新。那么我們則需要保證這個(gè)實(shí)體是從數(shù)據(jù)庫(kù)查出來(lái)的,或者與數(shù)據(jù)庫(kù)的記錄是對(duì)應(yīng)的上的。這在C/S結(jié)構(gòu)中是沒(méi)有問(wèn)題的,可問(wèn)題是在B/S結(jié)構(gòu)中呢?我們不可能把實(shí)體所有的字段都打包,發(fā)送到客戶端,然后客戶端修改在返回到服務(wù)端,然后在調(diào)用倉(cāng)庫(kù)方法更新吧。說(shuō)個(gè)最簡(jiǎn)單的,修改用戶密碼,我們只需要一個(gè)用戶ID,一個(gè)新密碼就可以了?;蛘哝i定用戶賬號(hào),只需要一個(gè)用戶ID,一個(gè)鎖定狀態(tài),一個(gè)鎖定時(shí)間。這樣,我們不可能把整個(gè)用戶實(shí)體打包傳來(lái)傳去吧。有人說(shuō)可以在保存的時(shí)候先根據(jù)ID查一遍數(shù)據(jù)庫(kù),然后再將修改的屬性值附加上去后再更新就可以了。這就回到問(wèn)題上了:在倉(cāng)庫(kù)方法中只有泛型類型,而你在調(diào)用倉(cāng)庫(kù)更新方法時(shí)傳遞的是一個(gè)實(shí)體類型。倉(cāng)庫(kù)并不知道你是那個(gè)實(shí)體,并且更新了哪些字段。
當(dāng)然,通過(guò)觸發(fā)器我們知道數(shù)據(jù)庫(kù)的更新都是先刪后插,所以更新幾個(gè)字段與全列更新底層操作是沒(méi)有多少區(qū)別的。

  現(xiàn)在拋開(kāi)倉(cāng)庫(kù)更新等實(shí)體泛型等信息。就單看一下當(dāng)一個(gè)實(shí)體發(fā)生改變時(shí),我們?cè)趺茨苤浪薷牧四男傩浴?br/>   正常情況下一個(gè)實(shí)體長(zhǎng)這樣

C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享

 1     ///  
 2     /// 一個(gè)具體的實(shí)體 
 3     ///  
 4     public class AccountEntity : MainEntity 
 5     { 
 6         ///  
 7         /// 文本類型 
 8         ///  
 9         public virtual string Account { get; set; }
 10         /// 
 11         /// 又一個(gè)文本屬性
 12         /// 
 13         public virtual string Password { get; set; }
 14         /// 
 15         /// 數(shù)字類型
 16         /// 
 17         public virtual int Sex { get; set; }
 18         /// 
 19         /// 事件類型
 20         /// 
 21         public virtual DateTime Birthday { get; set; }
 22         /// 
 23         /// 雙精度浮點(diǎn)數(shù)
 24         /// 
 25         public virtual double Height { get; set; }
 26         /// 
 27         /// 十進(jìn)制數(shù)
 28         /// 
 29         public virtual decimal Monery { get; set; }
 30         /// 
 31         /// 二進(jìn)制
 32         /// 
 33         public virtual byte[] PublicKey { get; set; }
 34         /// 
 35         /// Guid類型
 36         /// 
 37         public virtual Guid AreaId { get; set; }
 38     }

View Code

  當(dāng)我們要修改這個(gè)實(shí)體的屬性時(shí):

var entity = new accountEntity();
entity.Id=1;
entity.Account = "給屬性賦值';

  然后將這個(gè)實(shí)體傳遞到底層進(jìn)行操作。

db.Update(entity);

  完全沒(méi)有問(wèn)題,可是我的問(wèn)題在底層怎么知道我應(yīng)用層修改了那幾個(gè)屬性呢?再加一個(gè)方法,告訴底層,我修改了這幾個(gè)屬性。

db.Update(entity,"Account");

  好像也沒(méi)有什么不可哈。

  可是這樣,如果我修改了Account,參數(shù)中卻傳遞了Password怎么辦?所以,應(yīng)該在實(shí)體上就應(yīng)該有一個(gè)集合對(duì)整個(gè)屬性是否有修改的狀態(tài)進(jìn)行存儲(chǔ)。然后到底層Update方法在取出更新過(guò)的字段進(jìn)行下一步操作。
  通過(guò)這一思路,我想到在實(shí)體中加一個(gè)字典:

protected Dictionary FieldTracking = new Dictionary();

  當(dāng)屬性賦值時(shí),則添加到字典中來(lái)。(當(dāng)然,這種操作是會(huì)增加程序的開(kāi)銷的)

FieldTracking["Account"]="給屬性賦值";

  然后在底層在取出里面的集合,來(lái)區(qū)分哪些字段被修改(大花貓動(dòng)了哪些小玩具)。

  改造下實(shí)體屬性

        public virtual string Account
        {
            get
            { return _Account; }
            set {
                _Account = value;
                FieldTracking["Account"] = value;
            }
        }

  看過(guò)編譯后的IL代碼的都知道,class中的屬性最終會(huì)編譯成兩個(gè)方法 setvalue和getvalue,那么通過(guò)修改set方法添加FieldTracking["Account"] = value;就可以讓屬性在賦值的時(shí)候添加到字典中。

  很簡(jiǎn)單吧。


  你以為這樣就完了。如果拿房間來(lái)比喻實(shí)體、拿玩具來(lái)比作屬性。我家那大花貓就是修改實(shí)體屬性的方法。你知道我家有多少玩具嗎?你每天回家的時(shí)候你知道大花貓動(dòng)了哪個(gè)小玩具嗎?給每個(gè)玩具裝個(gè)GPS?哈哈哈哈,別鬧,花這心思還不如再買點(diǎn)回來(lái)。什么?買回來(lái)的還得裝,算了。研究下怎么裝吧。

  一個(gè)程序可能有上百個(gè)實(shí)體類,修改現(xiàn)有的實(shí)體類,給每個(gè)set加一行?作為一個(gè)程序員是不可能容忍做這樣的操作的。寫(xiě)一個(gè)工具,讀取所有的實(shí)體代碼,加上這一行,保存。這是個(gè)好辦法。那每次添加一個(gè)實(shí)體類就得調(diào)用工具重寫(xiě)來(lái)一遍,每次修改屬性再調(diào)用一遍,恩。沒(méi)問(wèn)題。能用就行。這不是一個(gè)真心養(yǎng)貓的人的人能容忍的。

  那怎么辦?把貓打死?那玩具的存在將會(huì)沒(méi)有任何意義。想到一個(gè)辦法,在我離開(kāi)房子的時(shí)候(程序初始化),給房子里的所有房間(實(shí)體類)創(chuàng)建一個(gè)同樣的房間(繼承),包含了與原房間所有需要監(jiān)控(標(biāo)記為virtual)的玩具的復(fù)制,在復(fù)制過(guò)程中加上GPS(-_~)。然后給貓玩。貓通過(guò)我給的門進(jìn)到這個(gè)繼承的房間中玩所有玩具的時(shí)候,GPS就能將貓的動(dòng)作全部記錄下來(lái)。我一回家,這貓玩了哪些玩具一看GPS記錄就全知道了。喲,這小崽子,在王元鵝呢。
  

  看不懂,沒(méi)關(guān)系,上馬:
  1、在程序集初始化的時(shí)候,通過(guò)反射,查找所有繼承自BaseEntity的實(shí)體類。遍歷其中的屬性。找到標(biāo)記為virtual進(jìn)行復(fù)制。

    剛開(kāi)始對(duì)于如果找到virtual屬性花了不少時(shí)間。我總只想著在屬性上找,卻沒(méi)想到去set_value方法上去找(其實(shí)get_value方法也是)。還是太菜啊。

    注:NoMapAttribute特性是一個(gè)自定義的標(biāo)記,表示不參與映射。因?yàn)椴粎⑴c映射就不需要監(jiān)控。與本文章代碼沒(méi)有太大的關(guān)系。僅供參考。

//獲取實(shí)體所在的程序集(ClassLibraryDemo)
var assemblyArray = AppDomain.CurrentDomain.GetAssemblies()
        .Where(w => w.GetName().Name == "ClassLibraryDemo")
        .ToList();
//實(shí)體的基類
var baseEntityType = typeof(BaseEntity);
//循環(huán)程序集
foreach (Assembly item in assemblyArray)
{
    //找到這個(gè)程序集中繼承自基類的實(shí)體
    var types = item.GetTypes().Where(t => t.IsAbstract == false
        && baseEntityType.IsAssignableFrom(t) 
        && t != baseEntityType);
    foreach (Type btItem in types){
        //遍歷這個(gè)實(shí)體類中的屬性
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、根據(jù)1的結(jié)果,復(fù)制一個(gè)新的房間(動(dòng)態(tài)代碼生成一個(gè)類,這個(gè)類繼承1中的實(shí)體,并且重寫(xiě)了屬性的set方法)

  這個(gè)過(guò)程就設(shè)計(jì)到動(dòng)態(tài)代碼的生成了。

//首先創(chuàng)建一個(gè)與實(shí)體類對(duì)應(yīng)的動(dòng)態(tài)類
CodeTypeDeclaration ct = new CodeTypeDeclaration(btItem.Name + "_Dynamic");
//循環(huán)實(shí)體中的所有標(biāo)記為virtual的屬性
foreach (PropertyInfo fiItem in properties)
{
	//創(chuàng)建一個(gè)屬性
	var p = new CodeMemberProperty();
	//設(shè)置屬性為公共、重寫(xiě)
	p.Attributes = MemberAttributes.Public | MemberAttributes.Override;//override
	//設(shè)置屬性的類型為繼承的屬性的數(shù)據(jù)類型
	p.Type = new CodeTypeReference(fiItem.PropertyType);
	//屬性名稱與繼承的一致
	p.Name = fiItem.Name;
	//包含set代碼
	p.HasSet = true;
	//包含get代碼
	p.HasGet = true;
	//設(shè)置get代碼
	//return base.Account
	p.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                        new CodeBaseReferenceExpression(), fiItem.Name)));
	//設(shè)置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"(加后綴以示區(qū)分)

//聲明一個(gè)命名空間(與當(dāng)前實(shí)體類同名+后綴)
CodeNamespace ns = new CodeNamespace(btItem.Namespace + ".Dynamic");
ns.Types.Add(ct);

  4、編輯生成代碼所在的程序集

    //要?jiǎng)討B(tài)生成代碼的程序集
    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();
    //看編譯是否通過(guò)
    var error = cr.Errors;
    if (error.HasErrors)
    {
        Console.WriteLine("錯(cuò)誤列表:");
        //編譯不通過(guò)
        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、將復(fù)制的新類與原類建立映射關(guān)系。

foreach (Type item in ts)
{
    //注冊(cè)(模擬實(shí)現(xiàn),通過(guò)字典實(shí)現(xiàn)的,也可以通過(guò)IOC注入方式處理)
    Mapping.Map(item.BaseType, item);
}

  6、獲得這個(gè)復(fù)制的實(shí)體對(duì)象

//創(chuàng)建一個(gè)指定的實(shí)體對(duì)象
AccountEntity ae = Mapping.GetMap();

  7、對(duì)這個(gè)實(shí)體對(duì)象的屬性進(jìn)行賦值

//主鍵賦值不會(huì)修改屬性更新
ae.BaseEntity_Id = 1;//不會(huì)變(未標(biāo)記為virtual)
ae.MainEntity_Name = "大花貓";
ae.MainEntity_UpdateTime = DateTime.Now;
//修改某個(gè)屬性
ae.Account = "admin";
ae.Account = "以最后一次的修改為準(zhǔn)";

  8、調(diào)用底層方法,底層根據(jù)這個(gè)實(shí)體屬性獲得被修改的屬性名稱

//調(diào)用基類中的方法 獲取變動(dòng)的屬性
var up = ae.GetFieldTracking();
Console.WriteLine("有修改的字段:");
up.ForEach(fe =>
{
    Console.WriteLine(fe + ":" + ae[fe]);
});

  9、完美

  C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享

  就這樣,在底層就能知道哪些實(shí)體被賦值過(guò)了。

  當(dāng)然,有些實(shí)體我們只是需要用來(lái)計(jì)算,則可以調(diào)用方法將賦值過(guò)的屬性進(jìn)行刪除

//刪除變更字段
ae.RemoveChanges("Account");

  這只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),還有一種比較復(fù)雜的情況,在第6步,獲得這個(gè)復(fù)制的實(shí)體對(duì)象時(shí),怎么用一個(gè)現(xiàn)有的new出來(lái)的實(shí)體對(duì)象去創(chuàng)建建并監(jiān)控呢。就像,別人送我一房間現(xiàn)成的玩具,給我的時(shí)候貓就在里面玩了。嗷,把貓打死吧。

以上是C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


新聞名稱:C#中實(shí)現(xiàn)監(jiān)控類屬性更改的代碼案例分享-創(chuàng)新互聯(lián)
URL鏈接:http://weahome.cn/article/cdjcji.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部