Have you ever discussed remote work with someone who has never worked remotely? How did it go? If they were like most people, it probably didn’t go all that well. It can be difficult for people to understand how someone could work from home, be productive, and end up being a normal human being at the end of the day.
اشتراکها
اشتراکها
کنفرانس NET Conf: Focus on Blazor.
.NET Conf: Focus on Blazor is a free, one-day livestream event that features speakers from the community and .NET product teams that are working on building web apps with C# and Blazor. You don't need to use JavaScript anymore with Blazor technology! Blazor lets you build interactive web UIs using C# instead of JavaScript.
اشتراکها
بخش های خوب ASP.NET MVC Core
Today is a pretty exciting day if you’re an enterprise developer. . . Red Hat and Microsoft have announced that the two companies will be working together to bring a supported version of the .NET r untime to Red Hat’s Enterprise Linux and OpenShift 3
نظرات مطالب
تقویم شمسی در ویندوز 10
یک نکتهی تکمیلی
در ویندوز 10 که تقویم شمسی آن انتخاب شده، فرض کنید قصد داریم رشتهی زیر را به تاریخ معادل آن تبدیل کنیم:
این کد با خطای زیر که بیانگر قابل تفسیر نبودن روز Wed است خاتمه مییابد:
راه حل:
شبیه به مطلب «تاثیر فرهنگ جاری سیستم بر روی اعداد در دات نت» در اینجا نیز باید از CultureInfo.InvariantCulture استفاده کرد:
در ویندوز 10 که تقویم شمسی آن انتخاب شده، فرض کنید قصد داریم رشتهی زیر را به تاریخ معادل آن تبدیل کنیم:
const string dateTime = "Wed, 17 Apr 2013 12:10:18 GMT"; Console.WriteLine(DateTime.Parse(dateTime));
String was not recognized as a valid DateTime because the day of week was incorrect.
شبیه به مطلب «تاثیر فرهنگ جاری سیستم بر روی اعداد در دات نت» در اینجا نیز باید از CultureInfo.InvariantCulture استفاده کرد:
Console.WriteLine(DateTime.Parse(dateTime, CultureInfo.InvariantCulture));
- Productivity and Performance improvements – Visual Studio is more responsive and usable, and projects load faster.
- Improved tooling to profile and understand your applications performance.
- Updated JavaScript and TypeScript tooling, including improved Vue.js and ESLint support, and right-click context menu productivity improvements.
- More C++ productivity improvements in IntelliSense, Code Analysis, and Just My Code debugging.
- Improved performance for Visual Basic integer manipulation.
- Azure Development improvements, including continuous delivery for Azure Functions, better experience managing secrets via Key Vault, and ability to configure Application Insights during initial site creation.
- More Library Manager features for managing Web Projects’ client-side library files.
- Mobile Development improvements such as faster Android incremental builds and inclusion of Xamarin.Essentials to facilitate building native apps.
مطالب دورهها
لیست ها و آرایه ها در #F
برای تعریف لیست در #F فقط کافیست از [] و برای جداسازی آیتمهای موجود در لیست از عملگر :: (بخوانید cons) استفاده کنید. #F از لیستهای خالی نیز پشتیبانی میکند. به مثال هایی از این دست توجه کنید
#1 تعریف یک لیست خالی
#2 تعریف یک لیست به همراه یک آیتم
#3 تعریف یک لیست به همراه دو آیتم
قبول دارم که دستورالعمل بالا برای مقدار دهی اولیه به لیست کمی طولانی و سخت است. برای همین میتونید از روش زیر هم استفاده کنید.
*کد بالا یک لیست با دو آیتم که از نوع رشته ای هستند تولید خواهد کرد.
میتونید از عملگر @ برای پیوستن دو لیست به هم نیز استفاده کنید.
نکته : تمام آیتمهای موجود در لیست باید از یک نوع باشند. بعنی امکان تعریف لیستی که دارای آیتم هایی با datatypeهای متفاوت باشد باعث تولید خطای کامپایلری میشود. اما اگر نیاز به لیستی دارید که باید چند datatype رو هم پوشش دهد میتونید از objectها استفاده کنید.
در بالا یک لیست از objectها رو تعریف کرده ایم. فقط دقت کنید برای اینکه آیتمهای موجود در لیست رو تبدیل به object کنیم از دستور box قبل از هر آیتم استفاده کردیم.
در هنگام استفاده از عملگرها @ و :: مقدار لیست تغییر نمیکند بلکه یک لیست جدید تولید خواهد شد.
#1 تعریف لیستی که دارای یک آیتم است.
#2 تعریف لیستی که دارای دو آیتم است(آیتم دوم لیست خود از نوع لیست است)
#3 تعریف لیستی که دارای سه آیتم است(ایتم دوم لیست خود از نوع لیستی است که دارای دو آیتم است)
# از تابع List.rev برای معکوس کردن آیتمهای لیست three استفاده کردیم و مقادیر در لیستی به نام rightWayRound قرار گرفت.
#5 تابع main برای چاپ اطلاعات لیست ها
بعد از اجرا خروجی زیر مشاهده میشود.
تفاوت بین لیستها در #F و لیست و آرایه در دات نت(System.Collection.Generic)
#1 در #F بعد از ساختن یک لیست امکان تغییر در مقادیر عناصر آن وجود ندارد.
#2 در #F بعد از ساختن یک لیست دیگه نمیتونید یک عنصر جدید به لیست اضافه کنید.
#3 جستجوی در لیستهای #F به نسبت لیستها و آرایههای در دات نت کندتر عمل میکند.
استفاده از عبارات در لیست ها
برای تعریف محدوده در لیست میتونیم به راحتی از روش زیر استفاده کنیم
برای ساخت لیستها به صورت داینامیک استفاده از حلقههای تکرار در لیست مجاز است.
کد بالا معادل کد زیر در #C است.
لیستها و الگوی Matching
روش عادی برای کار با لیستها در #F استفاده از الگوی Matching و توابع بازگشتی است.
در مثال بالا ابتدا یک لیست تعریف کردیم که دارای 3 آیتم است و هر آیتم آن خود یک لیست با سه آیتم است.(تمام آیتمها از نوع داده عددی هستند). یک تابع بازگشتی برای پیمایش تمام آیتمهای لیست نوشتم که در اون از الگوی Matching استفاده کردیم.
خروجی :
ماژول لیست
در جدول زیر تعدادی از توابع ماژول لیست رو مشاهده میکنید.
مثال هایی از توابع بالا
مثال هایی از نحوه استفاده seq
#1 seq بامحدوده 1 تا 100 و توالی 10
#2 استفاده از حلقههای تکرار برای تعریف محدوده و توالی در seq
#3 استفاده از <- به جای yield
#4 استفاده از حلقه for به همراه شرط برای فیلتر کردن
چگونگی استفاده از توابع seq
در این بخش به ارائه مثال هایی کاربردیتر از چگونگی استفاده از seq در #F میپردازیم. برای شروع نحوه ساخت یک seq خالی یا empty رو خواهم گفت.
روش ساخت یک seq که فقط یک عنصر را برگشت میدهد.
برای ساختن یک seq همانند لیستها میتونیم از seq.init استفاده کنیم. عدد 5
که بلافاصله بعد از تابع seq.init آمده است نشان دهنده تعداد آیتمها
موجود در seq خواهد بود. seq.iter هم یک تابع مورد نظر رو بر روی تک تک
عناصر seq اجرا خواهد کرد.(همانند list.iter)
خروجی مثال بالا
با استفاده از توابع seq.ofArray , seq.ofList میتونیم seq مورد نظر خود را از لیست یا آرایه مورد نظر بسازیم.
البته این نکته رو هم یادآور بشم که به کمک عملیات تبدیل نوع(type casting) هم میتونیم آرایه رو به seq تبدیل کنیم. به صورت زیر
برای مشخص کردن اینکه آیا یک آیتم در seq موجود است یا نه میتونیم از seq.exists به صورت زیر استفاده کنیم.
اگر seq پاس داده شده به تابع exists خالی باشد یا یک ArgumentNullException متوقف خواهید شد.
برای جستجو و پیدا کردن یک آیتم در seq میتونیم از seq.find استفاده کنیم.
دقت کنید که اگر هیچ آیتمی در sequence با predicate مورد نظر پیدا نشود
یک KeyNotFoundException رخ خواهد داد. در صورتی که مایل نباشید که استثنا
رخ دهد میتوانید از تابع seq.tryFind استفاده کنید. هم چنین خالی بودن
sequence ورودی باعث ArgumentNullExceptionخواهد شد.
استفاده از lambda expression در توابع
lamdaExpressoion از تواناییها مورد علاقه برنامه نویسان دات نت است و کمتر کسی است حاضر به استفاده از آن در کوئریهای linq نباشد. در #F نیز میتوانید از lambda Expression استفاده کنید. در ادامه به بررسی مثال هایی از این دست خواهیم پرداخت.
تابع skipWhile
همانند skipWhile در linq عمل میکند. یعنی یک predicate مورد نظر را بر روی تک تک عناصر یک لیست اجرا میکند و آیتم هایی که شرط برای آنها true باشد نادیده گرفته میشوند و مابقی آیتمها برگشت داده میشوند.
میبینید که predicate
مورد نظر برای تابع skipWhile به صورت lambda expression است که با رنگ
متفاوت نمایش داده شده است.(استفاده از کلمه fun).
خروجی به صورت زیر است:
برای
بازگرداندن یک تعداد مشخص از آیتمهای seq میتونید از توابع seq.take یا
seq.truncate استفاده کنید. ابتدا باید تعداد مورد نظر و بعد لیست مورد نظر
را به عنوان پارامتر مقدار دهی کنید.
مثال:
خروجی
Tuples
tuples در #F به گروهی از مقادیر بی نام ولی مرتب شده که میتوانند انواع متفاوت هم داشته باشند گفته میشود. ساختار کلی آن به صورت ( element , ... , element ) است که هر element خود میتواند یک عبارت نیز باشد.(مشابه کلاس Tuple در #C که به صورت generic استفاده میکنیم)
نکات استفاده از tuple
#1 میتونیم از الگوی Matching برای دسترسی به عناصر tuple استفاده کنیم.
#2 میتونیم از let برای تعربف الگوی tuple استفاده کنیم.
#3 توابع fst و snd مقادیر اول و دوم هر tuple رو بازگشت میدهند
#4 تابعی برای بازگشت عنصر سوم یک tuple وجود ندارد ولی این تابع رو با هم مینویسیم:
کاربرد tuple در کجاست
زمانی که یک تابع باید بیش از یک مقدار را بازگشت دهد از tupleها استفاده میکنیم. برای مثال
خروجی تابع divRem از نوع tuple که دارای 2 مقدار است میباشد.
#1 let emptyList = [] #2 let oneItem = "one " :: [] #3 let twoItem = "one " :: "two " :: []
#2 تعریف یک لیست به همراه یک آیتم
#3 تعریف یک لیست به همراه دو آیتم
قبول دارم که دستورالعمل بالا برای مقدار دهی اولیه به لیست کمی طولانی و سخت است. برای همین میتونید از روش زیر هم استفاده کنید.
let shortHand = ["apples "; "pears"]
میتونید از عملگر @ برای پیوستن دو لیست به هم نیز استفاده کنید.
let twoLists = ["one, "; "two, "] @ ["buckle "; "my "; "shoe "]
نکته : تمام آیتمهای موجود در لیست باید از یک نوع باشند. بعنی امکان تعریف لیستی که دارای آیتم هایی با datatypeهای متفاوت باشد باعث تولید خطای کامپایلری میشود. اما اگر نیاز به لیستی دارید که باید چند datatype رو هم پوشش دهد میتونید از objectها استفاده کنید.
let objList = [box 1; box 2.0; box "three"]
در هنگام استفاده از عملگرها @ و :: مقدار لیست تغییر نمیکند بلکه یک لیست جدید تولید خواهد شد.
#1 let one = ["one "] #2 let two = "two " :: one #3 let three = "three " :: two #4 let rightWayRound = List.rev three #5 let main() = printfn "%A" one printfn "%A" two printfn "%A" three printfn "%A" rightWayRound
#2 تعریف لیستی که دارای دو آیتم است(آیتم دوم لیست خود از نوع لیست است)
#3 تعریف لیستی که دارای سه آیتم است(ایتم دوم لیست خود از نوع لیستی است که دارای دو آیتم است)
# از تابع List.rev برای معکوس کردن آیتمهای لیست three استفاده کردیم و مقادیر در لیستی به نام rightWayRound قرار گرفت.
#5 تابع main برای چاپ اطلاعات لیست ها
بعد از اجرا خروجی زیر مشاهده میشود.
["one "] ["two "; "one "] ["three "; "two "; "one "] ["one "; "two "; "three "]
F#List | Net Array | Net List | |
#1 امکان تغییر در عناصر لیست | No | Yes | Yes |
#2 امکان اضافه کردن عنصر جدید | No | No | Yes |
#3 جستجو | On | O1 | O1 |
#2 در #F بعد از ساختن یک لیست دیگه نمیتونید یک عنصر جدید به لیست اضافه کنید.
#3 جستجوی در لیستهای #F به نسبت لیستها و آرایههای در دات نت کندتر عمل میکند.
استفاده از عبارات در لیست ها
برای تعریف محدوده در لیست میتونیم به راحتی از روش زیر استفاده کنیم
let rangeList = [1..99]
let dynamicList = [for x in 1..99 -> x*x]
for(int x=0;x<99 ; x++) { myList.Add(x*x); }
روش عادی برای کار با لیستها در #F استفاده از الگوی Matching و توابع بازگشتی است.
let listOfList = [[2; 3; 5]; [7; 11; 13]; [17; 19; 23; 29]] let rec concatList l = match l with | head :: tail -> head @ (concatList tail) | [] -> [] let primes = concatList listOfList printfn "%A" primes
خروجی :
[2; 3; 5; 7; 11; 13; 17; 19; 23; 29]
در جدول زیر تعدادی از توابع ماژول لیست رو مشاهده میکنید.
نام تابع | توضیحات |
List.length | تابعی که طول لیست را برمی گرداند |
List.head | تابعی برای برگشت عنصر اول لیست |
List.tail | تمام عناصر لیست را بر میگرداند به جز عنصر اول |
List.init | یک لیست با توجه به تعداد آیتم ایجاد میکند و یم تابع را بر روی تک تک عناصر لیست ایجاد میکند. |
List.append | یک لیست را به عنوان ورودی دریافت میکند و به لیست مورد نظر اضافه میکند و مجموع دو لیست را برگشت میدهد |
List.filter | فقط عناصری را برگشت میدهد که شرط مورد نظر بر روی آنها مقدار true را برگشت دهد |
List.map | یک تابع مورد نظر را بر روی تک تک عناصر لیست اجرا میکند و لیست جدید را برگشت میدهد |
List.iter | یک تابع مورد نظر را بر روی تک تک عناصر لیست اجرا میکند |
List.zip | مقادیر دو لیست را با هم تجمیع میکند و لیست جدید را برگشت میدهد. اگر طول 2 لیست ورودی یکی نباشد خطا رخ خواهدداد |
List.unzip | درست برعکس تابع بالا عمل میکند |
List.toArray | لیست را تبدیل به آرایه میکند |
List.ofArray | آرایه را تبدیل به لیست میکند |
List.head [5; 4; 3] List.tail [5; 4; 3] List.map (fun x -> x*x) [1; 2; 3] List.filter (fun x -> x % 3 = 0) [2; 3; 5; 7; 9]
Sequence Collection
seq در #F یک توالی از عناصری است که هم نوع باشند. عموما از sequenceها زمانی استفاده میکنیم که یک مجموعه از دادهها با تعداد زیاد و مرتب شده داشته باشیم ولی نیاز به استفاده از تمام عناصر آن نیست. کارایی sequence در مجموعههای با تعداد زیاد از listها به مراتب بهتر است. sequenceها را با تابع seq میشناسند که معادل IEnumerable در دات نت است. بنابر این هر مجمو عه ای که IEnumerable رو در دات نت پیاده سازی کرده باشد در #F با seq قابل استفاده است.مثال هایی از نحوه استفاده seq
#1 seq بامحدوده 1 تا 100 و توالی 10
seq { 0 .. 10 .. 100 }
seq { for i in 1 .. 10 do yield i * i }
seq { for i in 1 .. 10 -> i * i }
let isprime n = let rec check i = i > n/2 || (n % i <> 0 && check (i + 1)) check 2 let aSequence = seq { for n in 1..100 do if isprime n then yield n }
در این بخش به ارائه مثال هایی کاربردیتر از چگونگی استفاده از seq در #F میپردازیم. برای شروع نحوه ساخت یک seq خالی یا empty رو خواهم گفت.
let seqEmpty = Seq.empty
let seqOne = Seq.singleton 10
let seqFirst5MultiplesOf10 = Seq.init 5 (fun n -> n * 10) Seq.iter (fun elem -> printf "%d " elem) seqFirst5MultiplesOf10
0 10 20 30 40
let seqFromArray2 = [| 1 .. 10 |] |> Seq.ofArray
let seqFromArray1 = [| 1 .. 10 |] :> seq<int>
let containsNumber number seq1 = Seq.exists (fun elem -> elem = number) seq1 let seq0to3 = seq {0 .. 3} printfn "For sequence %A, contains zero is %b" seq0to3 (containsNumber 0 seq0to3)
برای جستجو و پیدا کردن یک آیتم در seq میتونیم از seq.find استفاده کنیم.
let isDivisibleBy number elem = elem % number = 0 let result = Seq.find (isDivisibleBy 5) [ 1 .. 100 ] printfn "%d " result
استفاده از lambda expression در توابع
lamdaExpressoion از تواناییها مورد علاقه برنامه نویسان دات نت است و کمتر کسی است حاضر به استفاده از آن در کوئریهای linq نباشد. در #F نیز میتوانید از lambda Expression استفاده کنید. در ادامه به بررسی مثال هایی از این دست خواهیم پرداخت.
تابع skipWhile
همانند skipWhile در linq عمل میکند. یعنی یک predicate مورد نظر را بر روی تک تک عناصر یک لیست اجرا میکند و آیتم هایی که شرط برای آنها true باشد نادیده گرفته میشوند و مابقی آیتمها برگشت داده میشوند.
let mySeq = seq { for i in 1 .. 10 -> i*i }
let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
let mySeqSkipWhileLessThan10 = Seq.skipWhile (fun elem -> elem < 10) mySeq
mySeqSkipWhileLessThan10 |> printSeq
خروجی به صورت زیر است:
16 25 36 49 64 81 100
مثال:
let mySeq = seq { for i in 1 .. 10 -> i*i } let truncatedSeq = Seq.truncate 5 mySeq let takenSeq = Seq.take 5 mySeq let printSeq seq1 = Seq.iter (printf "%A ") seq1; printfn ""
#1 truncatedSeq |> printSeq #3 takenSeq |> printSeq
1 4 9 16 25 //truncate 1 4 9 16 25 //take
Tuples
tuples در #F به گروهی از مقادیر بی نام ولی مرتب شده که میتوانند انواع متفاوت هم داشته باشند گفته میشود. ساختار کلی آن به صورت ( element , ... , element ) است که هر element خود میتواند یک عبارت نیز باشد.(مشابه کلاس Tuple در #C که به صورت generic استفاده میکنیم)
// Tuple of two integers. ( 1, 2 ) // Triple of strings. ( "one", "two", "three" ) // Tuple of unknown types. ( a, b ) // Tuple that has mixed types. ( "one", 1, 2.0 ) // Tuple of integer expressions. ( a + 1, b + 1)
#1 میتونیم از الگوی Matching برای دسترسی به عناصر tuple استفاده کنیم.
let print tuple1 = match tuple1 with | (a, b) -> printfn "Pair %A %A" a b
let (a, b) = (1, 2)
let c = fst (1, 2) // return 1 let d = snd (1, 2)// return 2
let third (_, _, c) = c
زمانی که یک تابع باید بیش از یک مقدار را بازگشت دهد از tupleها استفاده میکنیم. برای مثال
let divRem a b = let x = a / b let y = a % b (x, y)
نظرات مطالب
چند نکته کاربردی درباره Entity Framework
در حالت Detached (مثل ایجاد یک شیء CLR ساده)
در متد Updateایی که نوشتید، قسمت Find حتما اتفاق میافته. چون Tracking خاموش هست (مطابق تنظیماتی که عنوان کردید)، بنابراین Find چیزی رو از کشی که وجود نداره نمیتونه دریافت کنه و میره سراغ دیتابیس. ماخذ :
حالا تصور کنید که در یک حلقه میخواهید 100 آیتم رو ویرایش کنید. یعنی 100 بار رفت و برگشت خواهید داشت با این متد Update سفارشی که ارائه دادید. البته منهای کوئریهای آپدیت متناظر. این 100 تا کوئری فقط Find است.
قسمت Find متد Update شما در حالت detached اضافی است. یعنی اگر میدونید که این Id در دیتابیس وجود داره نیازی به Findاش نیست. فقط State اون رو تغییر بدید کار میکنه.
در حالت نه آنچنان Detached ! (دریافت یک لیست از Context ایی که ردیابی نداره)
با خاموش کردن Tracking حتما نیاز خواهید داشت تا متد context.ChangeTracker.DetectChanges رو هم پیش از ذخیره سازی یک لیست دریافت شده از بانک اطلاعاتی فراخوانی کنید. وگرنه چون این اطلاعات ردیابی نمیشوند، هر تغییری در آنها، وضعیت Unchanged رو خواهد داشت و نه Detached. بنابراین SaveChanges عمل نمیکنه؛ مگر اینکه DetectChanges فراخوانی بشه.
سؤال: این سربار که میگن چقدر هست؟ ارزشش رو داره که راسا خاموشش کنیم؟ یا بهتره فقط برای گزارشگیری این کار رو انجام بدیم؟
یک آزمایش:
نتیجه این آزمایش بعد از 20 بار اجرا و اندازه گیری:
در حالت کار با یک شیء ساده، به روز رسانی حالت منقطع بسیار سریعتر است (چون یکبار رفت و برگشت کمتری داره به دیتابیس).
در حالت کار با لیستی از اشیاء دریافت شده از بانک اطلاعاتی، به روز رسانی حالت متصل به Context سریعتر است.
در متد Updateایی که نوشتید، قسمت Find حتما اتفاق میافته. چون Tracking خاموش هست (مطابق تنظیماتی که عنوان کردید)، بنابراین Find چیزی رو از کشی که وجود نداره نمیتونه دریافت کنه و میره سراغ دیتابیس. ماخذ :
The Find method on DbSet uses the primary key value to attempt to find an entity tracked by the context. If the entity is not found in the context then a query will be sent to the database to find the entity there. Null is returned if the entity is not found in the context or in the database.
قسمت Find متد Update شما در حالت detached اضافی است. یعنی اگر میدونید که این Id در دیتابیس وجود داره نیازی به Findاش نیست. فقط State اون رو تغییر بدید کار میکنه.
در حالت نه آنچنان Detached ! (دریافت یک لیست از Context ایی که ردیابی نداره)
با خاموش کردن Tracking حتما نیاز خواهید داشت تا متد context.ChangeTracker.DetectChanges رو هم پیش از ذخیره سازی یک لیست دریافت شده از بانک اطلاعاتی فراخوانی کنید. وگرنه چون این اطلاعات ردیابی نمیشوند، هر تغییری در آنها، وضعیت Unchanged رو خواهد داشت و نه Detached. بنابراین SaveChanges عمل نمیکنه؛ مگر اینکه DetectChanges فراخوانی بشه.
سؤال: این سربار که میگن چقدر هست؟ ارزشش رو داره که راسا خاموشش کنیم؟ یا بهتره فقط برای گزارشگیری این کار رو انجام بدیم؟
یک آزمایش:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Diagnostics; using System.Linq; namespace EF_General.Models.Ex21 { public abstract class BaseEntity { public int Id { set; get; } } public class Factor : BaseEntity { public int TotalPrice { set; get; } } public class MyContext : DbContext { public DbSet<Factor> Factors { get; set; } public MyContext() { } public MyContext(bool withTracking) { if (withTracking) return; this.Configuration.ProxyCreationEnabled = false; this.Configuration.LazyLoadingEnabled = false; this.Configuration.AutoDetectChangesEnabled = false; } public void CustomUpdate<T>(T entity) where T : BaseEntity { if (entity == null) throw new ArgumentException("Cannot add a null entity."); var entry = this.Entry<T>(entity); if (entry.State != EntityState.Detached) return; /*var set = this.Set<T>(); // اینها اضافی است //متد فایند اگر اینجا باشه حتما به بانک اطلاعاتی رجوع میکنه در حالت منقطع از زمینه و در یک حلقه به روز رسانی کارآیی مطلوبی نخواهد داشت T attachedEntity = set.Find(entity.Id); if (attachedEntity != null) { var attachedEntry = this.Entry(attachedEntity); attachedEntry.CurrentValues.SetValues(entity); } else {*/ entry.State = EntityState.Modified; //} } } public class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } protected override void Seed(MyContext context) { if (!context.Factors.Any()) { for (int i = 0; i < 20; i++) { context.Factors.Add(new Factor { TotalPrice = i }); } } base.Seed(context); } } public class Performance { public TimeSpan ListDisabledTracking { set; get; } public TimeSpan ListNormal { set; get; } public TimeSpan DetachedEntityDisabledTracking { set; get; } public TimeSpan DetachedEntityNormal { set; get; } } public static class Test { public static void RunTests() { startDb(); var results = new List<Performance>(); var runs = 20; for (int i = 0; i < runs; i++) { Console.WriteLine("\nRun {0}", i + 1); var tsListDisabledTracking = PerformanceHelper.RunActionMeasurePerformance(() => updateListTotalPriceDisabledTracking()); var tsListNormal = PerformanceHelper.RunActionMeasurePerformance(() => updateListTotalPriceNormal()); var tsDetachedEntityDisabledTracking = PerformanceHelper.RunActionMeasurePerformance(() => updateDetachedEntityTotalPriceDisabledTracking()); var tsDetachedEntityNormal = PerformanceHelper.RunActionMeasurePerformance(() => updateDetachedEntityTotalPriceNormal()); results.Add(new Performance { ListDisabledTracking = tsListDisabledTracking, ListNormal = tsListNormal, DetachedEntityDisabledTracking = tsDetachedEntityDisabledTracking, DetachedEntityNormal = tsDetachedEntityNormal }); } var detachedEntityDisabledTrackingAvg = results.Average(x => x.DetachedEntityDisabledTracking.TotalMilliseconds); Console.WriteLine("detachedEntityDisabledTrackingAvg: {0} ms.", detachedEntityDisabledTrackingAvg); var detachedEntityNormalAvg = results.Average(x => x.DetachedEntityNormal.TotalMilliseconds); Console.WriteLine("detachedEntityNormalAvg: {0} ms.", detachedEntityNormalAvg); var listDisabledTrackingAvg = results.Average(x => x.ListDisabledTracking.TotalMilliseconds); Console.WriteLine("listDisabledTrackingAvg: {0} ms.", listDisabledTrackingAvg); var listNormalAvg = results.Average(x => x.ListNormal.TotalMilliseconds); Console.WriteLine("listNormalAvg: {0} ms.", listNormalAvg); } private static void updateDetachedEntityTotalPriceNormal() { using (var context = new MyContext(withTracking: true)) { var detachedEntity = new Factor { Id = 1, TotalPrice = 10 }; var attachedEntity = context.Factors.Find(detachedEntity.Id); if (attachedEntity != null) { attachedEntity.TotalPrice = 100; context.SaveChanges(); } } } private static void updateDetachedEntityTotalPriceDisabledTracking() { using (var context = new MyContext(withTracking: false)) { var detachedEntity = new Factor { Id = 2, TotalPrice = 10 }; detachedEntity.TotalPrice = 200; context.CustomUpdate(detachedEntity); // custom update with change tracking disabled. context.SaveChanges(); } } private static void updateListTotalPriceNormal() { using (var context = new MyContext(withTracking: true)) { foreach (var item in context.Factors) { item.TotalPrice += 10; // normal update with change tracking enabled. } context.SaveChanges(); } } private static void updateListTotalPriceDisabledTracking() { using (var context = new MyContext(withTracking: false)) { foreach (var item in context.Factors) { item.TotalPrice += 10; //نیازی به این دو سطر نیست //context.ChangeTracker.DetectChanges(); // هربار باید محاسبه صورت گیرد در غیراینصورت وضعیت تغییر نیافته گزارش میشود //context.CustomUpdate(item); // custom update with change tracking disabled. } context.ChangeTracker.DetectChanges(); // در غیراینصورت وضعیت تغییر نیافته گزارش میشود context.SaveChanges(); } } private static void startDb() { Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>()); // Forces initialization of database on model changes. using (var context = new MyContext()) { context.Database.Initialize(force: true); } } } public class PerformanceHelper { public static TimeSpan RunActionMeasurePerformance(Action action) { var stopwatch = new Stopwatch(); stopwatch.Start(); action(); stopwatch.Stop(); return stopwatch.Elapsed; } } }
detachedEntityDisabledTrackingAvg: 22.32089 ms. detachedEntityNormalAvg: 54.546815 ms. listDisabledTrackingAvg: 413.615445 ms. listNormalAvg: 393.194625 ms.
در حالت کار با لیستی از اشیاء دریافت شده از بانک اطلاعاتی، به روز رسانی حالت متصل به Context سریعتر است.