اشتراکها
Keycloak.AuthServices provides robust authentication mechanisms for both web APIs and web applications. For web APIs, it supports JWT Bearer token authentication, which allows clients to authenticate to the API by providing a JWT token in the Authorization header of their requests. For web applications, it supports OpenID Connect, a simple identity layer on top of the OAuth 2.0 protocol, which allows clients to verify the identity of the end-user, obtain basic profile information about the end-user, etc.
نظرات مطالب
برنامه ریزی به روش چابک
The winner in terms of serialize speed is again FlatBuffer which still has the lowest overhead, but you need to go through a more complex compiler chain to generate the de/serialization code. The new entry MemoryPack is second which is the newest serializer
هدف اصلی از انواع و اقسام مباحث caching اطلاعات، فراهم آوردن روشهایی جهت میسر ساختن دسترسی سریعتر به دادههایی است که به صورت متناوب در برنامه مورد استفاده قرار میگیرند، بجای مراجعه مستقیم به بانک اطلاعاتی و خواندن اطلاعات از دیسک سخت.
عموما در ORMها دو سطح کش میتواند وجود داشته باشد:
الف) سطح اول کش
که نمونه بارز آن در EF Code first استفاده از متد context.Entity.Find است. در بار اول فراخوانی این متد، مراجعهای به بانک اطلاعاتی صورت گرفته تا بر اساس primary key ذکر شده در آرگومان آن، رکورد متناظری بازگشت داده شود. در بار دوم فراخوانی متد Find، دیگر مراجعهای به بانک اطلاعاتی صورت نخواهد گرفت و اطلاعات از سطح اول کش (یا همان Context جاری) خوانده میشود.
بنابراین سطح اول کش در طول عمر یک تراکنش معنا پیدا میکند و به صورت خودکار توسط EF مدیریت میشود.
ب) سطح دوم کش
سطح دوم کش در ORMها طول عمر بیشتری داشته و سراسری است. هدف از آن کش کردن اطلاعات عمومی و پر مصرفی است که در دید تمام کاربران قرار دارد و همچنین تمام کاربران میتوانند به آن دسترسی داشته باشند. بنابراین محدود به یک Context نیست.
عموما پیاده سازی سطح دوم کش خارج از ORM مورد استفاده قرار میگیرد و توسط اشخاص و شرکتهای ثالث تهیه میشود.
در حال حاضر پیاده سازی توکاری از سطح دوم کش در EF Code first وجود ندارد و قصد داریم در مطلب جاری به یک پیاده سازی نسبتا خوب از آن برسیم.
تلاشهای صورت گرفته
تا کنون دو پیاده سازی نسبتا خوب از سطح دوم کش در EF صورت گرفته:
Entity Framework Code First Caching
Caching the results of LINQ queries
مورد اول برای ایده گرفتن خوب است. بحث اصلی پیاده سازی سطح دوم کش، یافتن کلیدی است که معادل کوئری LINQ در حال فراخوانی است. سطح دوم کش را به صورت یک Dictionary تصور کنید. هر آیتم آن تشکیل شده است از یک کلید و یک مقدار. از کلید برای یافتن مقدار متناظر استفاده میشود.
اکنون مشکل چیست؟ در یک برنامه ممکن است صدها کوئری لینک وجود داشته باشد. چطور باید به ازای هر کوئری LINQ یک کلید منحصربفرد تولید کرد؟
در مطلب «Entity Framework Code First Caching» از متد ToString استفاده شده است. اگر این متد، بر روی یک عبارت LINQ در EF Code first فراخوانی شود، معادل SQL آن نمایش داده میشود. بنابراین یک قدم به تولید کلید منحصربفرد متناظر با یک کوئری نزدیک شدهایم. اما ... مشکل اینجا است که متد ToString پارامترها را لحاظ نمیکند. بنابراین این روش اصلا قابل استفاده نیست. چون کاربر به ازای تمام پارامترهای ارسالی، همواره یک نتیجه را دریافت خواهد کرد.
در مقاله «Caching the results of LINQ queries» این مشکل برطرف شده است. با parse کامل expression tree یک عبارت LINQ کلید منحصربفرد معادل آن یافت میشود. سپس بر این اساس میتوان نتیجه کوئری را به نحو صحیحی کش کرد. در این روش پارامترها هم لحاظ میشوند و مشکل مقاله قبلی را ندارد.
اما این مقاله دوم یک مشکل مهم را به همراه دارد: روشی را برای حذف آیتمها از کش ارائه نمیدهد. فرض کنید مقالات سایت را در سطح دوم کش قرار دادهاید. اکنون یک مقاله جدید در سایت ثبت شده است. اصطلاحا برای invalidating کش در این روش، راهکاری پیشنهاد نشده است.
پیاده سازی بهتری از سطح دوم کش در EF Code fist
میتوان از همان روش یافتن کلید منحصربفرد معادل با یک کوئری LINQ، که در مقاله دوم فوق، یاد شد، کار را شروع کرد و سپس آنرا به مرحلهای رساند که مباحث حذف کش نیز به صورت خودکار مدیریت شود. پیاده سازی آن را برای برنامههای وب در ذیل ملاحظه میکنید:
توضیحات کدهای فوق
در اینجا یک متدالحاقی به نام Cacheable توسعه داده شده است که میتواند در انتهای کوئریهای LINQ شما قرار گیرد. مثلا:
کاری که در این متد انجام میشود به این شرح است:
الف) ابتدا کلید منحصربفرد معادل کوئری LINQ فراخوانی شده محاسبه میشود.
ب) بر اساس نام کامل نوع Entity در حال استفاده، کلید دیگری به نام rootCacheKey تولید میگردد.
شاید بپرسید اهمیت این کلید چیست؟
فرض کنید در حال حاضر 1000 آیتم در کش وجود دارند. چه روشی را برای حذف آیتمهای مرتبط با کش Entity1 پیشنهاد میدهید؟ احتمالا خواهید گفت تمام کش را بررسی کرده و آیتمها را یکی یکی حذف میکنیم.
این روش بسیار کند است (و جواب هم نمیدهد؛ چون کلیدی که در اینجا تولید شده، هش MD5 معادل کوئری است و نمیتوان آنرا به موجودیتی خاص ربط داد) و ... نکته جالبی در متد HttpRuntime.Cache.Insert برای مدیریت آن پیش بینی شده است: استفاده از CacheDependency.
توسط CacheDependency میتوان گروهی از آیتمهای همخانواده را تشکیل داد. سپس برای حذف کل این گروه کافی است کلید اصلی CacheDependency را حذف کرد. به این ترتیب به صورت خودکار کل کش مرتبط خالی میشود.
ج) مراحل بعدی آن هم یک سری اعمال متداول هستند. ابتدا توسط HttpRuntime.Cache.Get بررسی میشود که آیا بر اساس کلید متناظر با کوئری جاری، اطلاعاتی در کش وجود دارد یا خیر. اگر بله، نتیجه از کش خوانده میشود. اگر خیر، کوئری اصطلاحا materialized میشود تا بر روی بانک اطلاعاتی اجرا شده و نتیجه بازگشت داده شود. سپس این نتیجه را در کش قرار میدهیم.
مورد بعدی که باید به آن دقت داشت، خالی کردن کش، پس از به روز رسانی اطلاعات توسط کاربران است. این کار در متد InvalidateSecondLevelCache صورت میگیرد. به کمک ChangeTracker میتوان نام نوعهای موجودیتهای تغییر کرده را یافت. چون کلید اصلی CacheDependency را بر مبنای همین نام نوعهای موجودیتها تعیین کردهایم، به سادگی میتوان کش مرتبط با موجودیت یافت شده را خالی کرد.
استفاده از متد InvalidateSecondLevelCache یاد شده به نحو زیر است:
در اینجا با تحریف متد SaveChanges، میتوان درست در زمان اعمال تغییرات به بانک اطلاعاتی، قسمتی از کش را غیرمعتبر کرد.
نحوه استفاده از سطح دوم کش توسعه داده شده
مثالی از کاربرد متدهای الحاقی توسعه داده شده را در ذیل مشاهده میکنید:
در این حالت اگر برنامه را اجرا کنیم به یک چنین خروجی در پنجره Debug ویژوال استودیو خواهیم رسید:
توضیحات:
در زمان تولید list1 چون اطلاعاتی در کش سطح دوم وجود ندارد، پیغام Adding new data قابل مشاهده است. اطلاعات از بانک اطلاعاتی دریافت شده و سپس در کش قرار داده میشود.
حین فراخوانی list2 که دقیقا همان کوئری list1 را یکبار دیگر فراخوانی میکند، به عبارت Fetching object خواهیم رسید که بر دریافت اطلاعات از کش سطح دوم بجای مراجعه به بانک اطلاعاتی دلالت دارد.
در list4 چون پارامترهای کوئری تغییر کردهاند، بنابراین دیگر کلید منحصربفرد معادل آن با list1 و lis2 یکی نیست و اینبار پیغام Adding new data مشاهده میشود؛ چون برای دریافت اطلاعات آن نیاز است که به بانک اطلاعاتی مراجعه شود.
در ادامه یک context دیگر باز شده و در آن رکوردی به بانک اطلاعاتی اضافه میشود. به همین دلیل اینبار پیام Removing items with dependencyKey قابل مشاهده است. به عبارتی متد InvalidateSecondLevelCache وارد عمل شده است و بر اساس تغییری که صورت گرفته، کش را غیرمعتبر کرده است.
سپس در context بعدی تعریف شده، دوبار متد FirstOrDefault فراخوانی شده است. اولین مورد Adding new data است و دومین فراخوانی به Fetching object ختم شده است (دریافت اطلاعات از کش).
کدهای کامل این پروژه را از اینجا میتوانید دریافت کنید:
EfSecondLevelCaching.zip
عموما در ORMها دو سطح کش میتواند وجود داشته باشد:
الف) سطح اول کش
که نمونه بارز آن در EF Code first استفاده از متد context.Entity.Find است. در بار اول فراخوانی این متد، مراجعهای به بانک اطلاعاتی صورت گرفته تا بر اساس primary key ذکر شده در آرگومان آن، رکورد متناظری بازگشت داده شود. در بار دوم فراخوانی متد Find، دیگر مراجعهای به بانک اطلاعاتی صورت نخواهد گرفت و اطلاعات از سطح اول کش (یا همان Context جاری) خوانده میشود.
بنابراین سطح اول کش در طول عمر یک تراکنش معنا پیدا میکند و به صورت خودکار توسط EF مدیریت میشود.
ب) سطح دوم کش
سطح دوم کش در ORMها طول عمر بیشتری داشته و سراسری است. هدف از آن کش کردن اطلاعات عمومی و پر مصرفی است که در دید تمام کاربران قرار دارد و همچنین تمام کاربران میتوانند به آن دسترسی داشته باشند. بنابراین محدود به یک Context نیست.
عموما پیاده سازی سطح دوم کش خارج از ORM مورد استفاده قرار میگیرد و توسط اشخاص و شرکتهای ثالث تهیه میشود.
در حال حاضر پیاده سازی توکاری از سطح دوم کش در EF Code first وجود ندارد و قصد داریم در مطلب جاری به یک پیاده سازی نسبتا خوب از آن برسیم.
تلاشهای صورت گرفته
تا کنون دو پیاده سازی نسبتا خوب از سطح دوم کش در EF صورت گرفته:
Entity Framework Code First Caching
Caching the results of LINQ queries
مورد اول برای ایده گرفتن خوب است. بحث اصلی پیاده سازی سطح دوم کش، یافتن کلیدی است که معادل کوئری LINQ در حال فراخوانی است. سطح دوم کش را به صورت یک Dictionary تصور کنید. هر آیتم آن تشکیل شده است از یک کلید و یک مقدار. از کلید برای یافتن مقدار متناظر استفاده میشود.
اکنون مشکل چیست؟ در یک برنامه ممکن است صدها کوئری لینک وجود داشته باشد. چطور باید به ازای هر کوئری LINQ یک کلید منحصربفرد تولید کرد؟
در مطلب «Entity Framework Code First Caching» از متد ToString استفاده شده است. اگر این متد، بر روی یک عبارت LINQ در EF Code first فراخوانی شود، معادل SQL آن نمایش داده میشود. بنابراین یک قدم به تولید کلید منحصربفرد متناظر با یک کوئری نزدیک شدهایم. اما ... مشکل اینجا است که متد ToString پارامترها را لحاظ نمیکند. بنابراین این روش اصلا قابل استفاده نیست. چون کاربر به ازای تمام پارامترهای ارسالی، همواره یک نتیجه را دریافت خواهد کرد.
در مقاله «Caching the results of LINQ queries» این مشکل برطرف شده است. با parse کامل expression tree یک عبارت LINQ کلید منحصربفرد معادل آن یافت میشود. سپس بر این اساس میتوان نتیجه کوئری را به نحو صحیحی کش کرد. در این روش پارامترها هم لحاظ میشوند و مشکل مقاله قبلی را ندارد.
اما این مقاله دوم یک مشکل مهم را به همراه دارد: روشی را برای حذف آیتمها از کش ارائه نمیدهد. فرض کنید مقالات سایت را در سطح دوم کش قرار دادهاید. اکنون یک مقاله جدید در سایت ثبت شده است. اصطلاحا برای invalidating کش در این روش، راهکاری پیشنهاد نشده است.
پیاده سازی بهتری از سطح دوم کش در EF Code fist
میتوان از همان روش یافتن کلید منحصربفرد معادل با یک کوئری LINQ، که در مقاله دوم فوق، یاد شد، کار را شروع کرد و سپس آنرا به مرحلهای رساند که مباحث حذف کش نیز به صورت خودکار مدیریت شود. پیاده سازی آن را برای برنامههای وب در ذیل ملاحظه میکنید:
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Data.Objects; using System.Diagnostics; using System.Linq; using System.Web; using System.Web.Caching; namespace EfSecondLevelCaching.Core { public static class EfHttpRuntimeCacheProvider { #region Methods (6) // Public Methods (2) public static IList<TEntity> ToCacheableList<TEntity>( this IQueryable<TEntity> query, int durationMinutes = 15, CacheItemPriority priority = CacheItemPriority.Normal) { return query.Cacheable(x => x.ToList(), durationMinutes, priority); } /// <summary> /// Returns the result of the query; if possible from the cache, otherwise /// the query is materialized and the result cached before being returned. /// The cache entry has a one minute sliding expiration with normal priority. /// </summary> public static TResult Cacheable<TEntity, TResult>( this IQueryable<TEntity> query, Func<IQueryable<TEntity>, TResult> materializer, int durationMinutes = 15, CacheItemPriority priority = CacheItemPriority.Normal) { // Gets a cache key for a query. var queryCacheKey = query.GetCacheKey(); // The name of the cache key used to clear the cache. All cached items depend on this key. var rootCacheKey = typeof(TEntity).FullName; // Try to get the query result from the cache. printAllCachedKeys(); var result = HttpRuntime.Cache.Get(queryCacheKey); if (result != null) { debugWriteLine("Fetching object '{0}__{1}' from the cache.", rootCacheKey, queryCacheKey); return (TResult)result; } // Materialize the query. result = materializer(query); // Adding new data. debugWriteLine("Adding new data: queryKey={0}, dependencyKey={1}", queryCacheKey, rootCacheKey); storeRootCacheKey(rootCacheKey); HttpRuntime.Cache.Insert( key: queryCacheKey, value: result, dependencies: new CacheDependency(null, new[] { rootCacheKey }), absoluteExpiration: DateTime.Now.AddMinutes(durationMinutes), slidingExpiration: Cache.NoSlidingExpiration, priority: priority, onRemoveCallback: null); return (TResult)result; } /// <summary> /// Call this method in `public override int SaveChanges()` of your DbContext class /// to Invalidate Second Level Cache automatically. /// </summary> public static void InvalidateSecondLevelCache(this DbContext ctx) { var changedEntityNames = ctx.ChangeTracker .Entries() .Where(x => x.State == EntityState.Added || x.State == EntityState.Modified || x.State == EntityState.Deleted) .Select(x => ObjectContext.GetObjectType(x.Entity.GetType()).FullName) .Distinct() .ToList(); if (!changedEntityNames.Any()) return; printAllCachedKeys(); foreach (var item in changedEntityNames) { item.removeEntityCache(); } printAllCachedKeys(); } // Private Methods (4) private static void debugWriteLine(string format, params object[] args) { if (!Debugger.IsAttached) return; Debug.WriteLine(format, args); } private static void printAllCachedKeys() { if (!Debugger.IsAttached) return; debugWriteLine("Available cached keys list:"); int count = 0; var enumerator = HttpRuntime.Cache.GetEnumerator(); while (enumerator.MoveNext()) { if (enumerator.Key.ToString().StartsWith("__")) continue; // such as __System.Web.WebPages.Deployment debugWriteLine("queryKey: {0}", enumerator.Key.ToString()); count++; } debugWriteLine("count: {0}", count); } private static void removeEntityCache(this string rootCacheKey) { if (string.IsNullOrWhiteSpace(rootCacheKey)) return; debugWriteLine("Removing items with dependencyKey={0}", rootCacheKey); // Removes all cached items depend on this key. HttpRuntime.Cache.Remove(rootCacheKey); } private static void storeRootCacheKey(string rootCacheKey) { // The cacheKeys of a cacheDependency that are not already in cache ARE NOT inserted into the cache // on the Insert of the item in which the dependency is used. if (HttpRuntime.Cache.Get(rootCacheKey) != null) return; HttpRuntime.Cache.Add( rootCacheKey, rootCacheKey, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.Default, null); } #endregion Methods } }
توضیحات کدهای فوق
در اینجا یک متدالحاقی به نام Cacheable توسعه داده شده است که میتواند در انتهای کوئریهای LINQ شما قرار گیرد. مثلا:
var data = context.Products.AsQueryable().Cacheable(x => x.FirstOrDefault());
کاری که در این متد انجام میشود به این شرح است:
الف) ابتدا کلید منحصربفرد معادل کوئری LINQ فراخوانی شده محاسبه میشود.
ب) بر اساس نام کامل نوع Entity در حال استفاده، کلید دیگری به نام rootCacheKey تولید میگردد.
شاید بپرسید اهمیت این کلید چیست؟
فرض کنید در حال حاضر 1000 آیتم در کش وجود دارند. چه روشی را برای حذف آیتمهای مرتبط با کش Entity1 پیشنهاد میدهید؟ احتمالا خواهید گفت تمام کش را بررسی کرده و آیتمها را یکی یکی حذف میکنیم.
این روش بسیار کند است (و جواب هم نمیدهد؛ چون کلیدی که در اینجا تولید شده، هش MD5 معادل کوئری است و نمیتوان آنرا به موجودیتی خاص ربط داد) و ... نکته جالبی در متد HttpRuntime.Cache.Insert برای مدیریت آن پیش بینی شده است: استفاده از CacheDependency.
توسط CacheDependency میتوان گروهی از آیتمهای همخانواده را تشکیل داد. سپس برای حذف کل این گروه کافی است کلید اصلی CacheDependency را حذف کرد. به این ترتیب به صورت خودکار کل کش مرتبط خالی میشود.
ج) مراحل بعدی آن هم یک سری اعمال متداول هستند. ابتدا توسط HttpRuntime.Cache.Get بررسی میشود که آیا بر اساس کلید متناظر با کوئری جاری، اطلاعاتی در کش وجود دارد یا خیر. اگر بله، نتیجه از کش خوانده میشود. اگر خیر، کوئری اصطلاحا materialized میشود تا بر روی بانک اطلاعاتی اجرا شده و نتیجه بازگشت داده شود. سپس این نتیجه را در کش قرار میدهیم.
مورد بعدی که باید به آن دقت داشت، خالی کردن کش، پس از به روز رسانی اطلاعات توسط کاربران است. این کار در متد InvalidateSecondLevelCache صورت میگیرد. به کمک ChangeTracker میتوان نام نوعهای موجودیتهای تغییر کرده را یافت. چون کلید اصلی CacheDependency را بر مبنای همین نام نوعهای موجودیتها تعیین کردهایم، به سادگی میتوان کش مرتبط با موجودیت یافت شده را خالی کرد.
استفاده از متد InvalidateSecondLevelCache یاد شده به نحو زیر است:
using System.Data.Entity; using EfSecondLevelCaching.Core; using EfSecondLevelCaching.Test.Models; namespace EfSecondLevelCaching.Test.DataLayer { public class ProductContext : DbContext { public DbSet<Product> Products { get; set; } public override int SaveChanges() { this.InvalidateSecondLevelCache(); return base.SaveChanges(); } } }
در اینجا با تحریف متد SaveChanges، میتوان درست در زمان اعمال تغییرات به بانک اطلاعاتی، قسمتی از کش را غیرمعتبر کرد.
نحوه استفاده از سطح دوم کش توسعه داده شده
مثالی از کاربرد متدهای الحاقی توسعه داده شده را در ذیل مشاهده میکنید:
using System.Data.Entity; using System.Linq; using EfSecondLevelCaching.Core; using EfSecondLevelCaching.Test.DataLayer; using EfSecondLevelCaching.Test.Models; using System; namespace EfSecondLevelCaching { public static class TestUsages { public static void RunQueries() { using (ProductContext context = new ProductContext()) { var isActive = true; var name = "Product1"; // reading from db var list1 = context.Products .OrderBy(one => one.ProductNumber) .Where(x => x.IsActive == isActive && x.ProductName == name) .ToCacheableList(); // reading from cache var list2 = context.Products .OrderBy(one => one.ProductNumber) .Where(x => x.IsActive == isActive && x.ProductName == name) .ToCacheableList(); // reading from cache var list3 = context.Products .OrderBy(one => one.ProductNumber) .Where(x => x.IsActive == isActive && x.ProductName == name) .ToCacheableList(); // reading from db var list4 = context.Products .OrderBy(one => one.ProductNumber) .Where(x => x.IsActive == isActive && x.ProductName == "Product2") .ToCacheableList(); } // removes products cache using (ProductContext context = new ProductContext()) { var p = new Product() { IsActive = false, ProductName = "P4", ProductNumber = "004" }; context.Products.Add(p); context.SaveChanges(); } using (ProductContext context = new ProductContext()) { var data = context.Products.AsQueryable().Cacheable(x => x.FirstOrDefault()); var data2 = context.Products.AsQueryable().Cacheable(x => x.FirstOrDefault()); context.SaveChanges(); } } } }
در این حالت اگر برنامه را اجرا کنیم به یک چنین خروجی در پنجره Debug ویژوال استودیو خواهیم رسید:
Adding new data: queryKey=72AF5DA1BA9B91E24DCCF83E88AD1C5F, dependencyKey=EfSecondLevelCaching.Test.Models.Product Available cached keys list: queryKey: EfSecondLevelCaching.Test.Models.Product queryKey: 72AF5DA1BA9B91E24DCCF83E88AD1C5F count: 2 Fetching object 'EfSecondLevelCaching.Test.Models.Product__72AF5DA1BA9B91E24DCCF83E88AD1C5F' from the cache. Available cached keys list: queryKey: EfSecondLevelCaching.Test.Models.Product queryKey: 72AF5DA1BA9B91E24DCCF83E88AD1C5F count: 2 Fetching object 'EfSecondLevelCaching.Test.Models.Product__72AF5DA1BA9B91E24DCCF83E88AD1C5F' from the cache. Available cached keys list: queryKey: EfSecondLevelCaching.Test.Models.Product queryKey: 72AF5DA1BA9B91E24DCCF83E88AD1C5F count: 2 Adding new data: queryKey=11A2C33F9AD7821A0A31003BFF1DF886, dependencyKey=EfSecondLevelCaching.Test.Models.Product Available cached keys list: queryKey: 72AF5DA1BA9B91E24DCCF83E88AD1C5F queryKey: 11A2C33F9AD7821A0A31003BFF1DF886 queryKey: EfSecondLevelCaching.Test.Models.Product count: 3 Removing items with dependencyKey=EfSecondLevelCaching.Test.Models.Product Available cached keys list: count: 0 Available cached keys list: count: 0 Adding new data: queryKey=02E6FE403B461E45C5508684156C1D10, dependencyKey=EfSecondLevelCaching.Test.Models.Product Available cached keys list: queryKey: 02E6FE403B461E45C5508684156C1D10 queryKey: EfSecondLevelCaching.Test.Models.Product count: 2 Fetching object 'EfSecondLevelCaching.Test.Models.Product__02E6FE403B461E45C5508684156C1D10' from the cache.
توضیحات:
در زمان تولید list1 چون اطلاعاتی در کش سطح دوم وجود ندارد، پیغام Adding new data قابل مشاهده است. اطلاعات از بانک اطلاعاتی دریافت شده و سپس در کش قرار داده میشود.
حین فراخوانی list2 که دقیقا همان کوئری list1 را یکبار دیگر فراخوانی میکند، به عبارت Fetching object خواهیم رسید که بر دریافت اطلاعات از کش سطح دوم بجای مراجعه به بانک اطلاعاتی دلالت دارد.
در list4 چون پارامترهای کوئری تغییر کردهاند، بنابراین دیگر کلید منحصربفرد معادل آن با list1 و lis2 یکی نیست و اینبار پیغام Adding new data مشاهده میشود؛ چون برای دریافت اطلاعات آن نیاز است که به بانک اطلاعاتی مراجعه شود.
در ادامه یک context دیگر باز شده و در آن رکوردی به بانک اطلاعاتی اضافه میشود. به همین دلیل اینبار پیام Removing items with dependencyKey قابل مشاهده است. به عبارتی متد InvalidateSecondLevelCache وارد عمل شده است و بر اساس تغییری که صورت گرفته، کش را غیرمعتبر کرده است.
سپس در context بعدی تعریف شده، دوبار متد FirstOrDefault فراخوانی شده است. اولین مورد Adding new data است و دومین فراخوانی به Fetching object ختم شده است (دریافت اطلاعات از کش).
کدهای کامل این پروژه را از اینجا میتوانید دریافت کنید:
EfSecondLevelCaching.zip
اشتراکها
مقدمه ای بر Bot Framework مایکروسافت
Essentially, a bot is an application that can automate repetitive tasks or serve information to a user and, more and more, can interact with a user using natural language. As these bots get more intelligent and better at understanding us, this is providing a wide spectrum of use cases for bots to be used by and assist almost everyone.
در این مقاله در ادامهی مطلبی که تحت عنوان «آموزش مفاهیم Data Warehouse» توسط آقای شاه قلی منتشر شده بود، به بررسی بیشتر مفهوم انبار داده ( Data Warehouse ) پرداخته میشود.
مقدمه
در سازمان ها، دادهها و اطلاعات معمولاً به دو شکل در سیستمها پیاده سازی میگردد:
این نکته حائز اهمیت است که سیستمهای اطلاعاتی یک سیستم یا محصول نیستند که بتوان آنها را خریداری کرد. بلکه یک راهبرد (Solution, Approach) هستند و در حقیقت هر راهبردی مربوط به یک نوع کسب و کار (Business) و یا سازمان میباشد و نمیتوان فرمول واحدی را برای حتی سازمانهای مشابه، ارائه نمود.
گارتنر در ابتدای سال 2011 گزارشی را منتشر کرده که نشان میدهد بازار BI با 9.7 % رشد، ارزشی بالغ بر 10.8 بیلیون دلار داشته، ولی متاسفانه پروژههای آن به طور متوسط با 75% شکست مواجه شده است. در حالیکه 4 سال پیش، این رقم حدود 50% بود. این موسسه BI را پنجمین اولویت مدیران IT ذکر کرده است.
مفاهیم و مباحث مربوط به Data Warehouse به اواسط دهه 1980 برمی گردد، به زمانی که IBM تحقیقاتی را در این زمینه شروع کرد و نتیجه آنرا «Information Warehouse» نامید و هنوز هم در برخی منابع از این واژه بجای Data Warehouse استفاده میشود. از این پس برای راحتی از اختصار DW بجای Data Warehouse استفاده میشود. انبارهای داده جهت رفع نیاز رو به رشد مدیریت دادهها و اطلاعات سازمانی که توسط پایگاههای داده سیستمهای عملیاتی غیر ممکن بود، ساخته شدند.
انبار داده به مجموعه ای از دادهها گفته میشود که از منابع مختلف اطلاعاتی سازمان جمع آوری، دسته بندی و ذخیره میشود. در واقع یک انبار داده مخزن اصلی کلیه دادههای حال و گذشته یک سازمان میباشد که برای همیشه جهت انجام عملیات گزارش گیری و آنالیز در دسترس مدیران میباشد. انبارههای داده حاوی داده هایی هستند که به مرور زمان از سیستمهای عملیاتی آنلاین سازمان، استخراج میشوند. بنابراین سوابق کلیه اطلاعات و یا بخش عظیمی از آنها را میتوان در انباره دادهها مشاهده نمود.
از آنجائیکه انجام عملیات آماری و گزارشات پیچیده دارای بار کاری بسیار سنگینی برای سرورهای پایگاه داده میباشند، وجود انبار داده سبب میگردد که این گونه عملیات تاثیری بر فعالیت برنامههای کاربردی سازمان نداشته باشد.
همانگونه که پایگاه داده سیستمهای عملیاتی سازمان (برنامههای کاربردی) به گونه ای طراحی میشوند که انجام تغییر، حذف و اضافه داده به سرعت صورت پذیرد، در مقابل انبار دادهها دارای معماری ویژه ای میباشند که موجب تسریع انجام عملیات آماری و گزارش گیری میشود. در حقیقت میتوان اینگونه بیان نمود که انباره داده یک مخزن فعال و هوشمند از اطلاعات است که قادر است اطلاعات را از محیطهای گوناگون جمع آوری و مدیریت کرده و نهایتا پخش نماید و در صورت لزوم نیز سیاستهای تجاری را روی آنها اجرا نماید.
Bill Inmon:
او را پدر DW مینامند، از دیدگاه او DW هسته مرکزی چیزی است که او آنرا CIF اختصار (Corporate Information Factory) مینامد، که پایه و اساس BI بر مبنای آن قرار دارد. وی از طرفداران Top-Down Design میباشد که معتقد است در زمان طراحی باید با دیدی سازمانی، CIF را مدل سازی، ولی بصورت دپارتمانی پیاده سازی کرد (Think Globally, Implement Locally). در این نوع طراحی از DW به Data Mart خواهیم رسید.
Ralph Kimball Ph.D:
به نظر وی DW چیزی نیست جز یک کپی از دادههای عملیاتی که به طرز خاصی برای گزارشات و تحلیلهای آماری، آماده و ساختمند شده است. به بیان دیگر DW سیستمی است جهت استخراج، پالایش، تطبیق و تحویل اطلاعات منابع داده ای به یک بانک اطلاعاتی Dimensional و اجرای Query و گزارشات آماری و تحلیلی برای اهداف تصمیم گیری و استراتژیک سازمان.
وی معرفی کننده یکی از اساسیترین مفاهیم طراحی یعنی Dimensional Modeling است؛ ماحصل چنین ایده ای، اساس شکل گیری مدلی است که امروزه کارشناسان آنرا به نام Cube میشناسند. وی از طرفداران Bottom-Up Design است که در این نگرش از Data Mart به DW میرسیم. این روش به نظر عملیتر از روشی میباشد که به یکباره DW جامع و کامل برای اهداف سازمانی طراحی و پیاده سازی گردد.
تعریف انبار داده:
W.H.Inmon پدر DW آنرا چنین تعریف میکند:
مقدمه
در سازمان ها، دادهها و اطلاعات معمولاً به دو شکل در سیستمها پیاده سازی میگردد:
• سیستمهای عملیاتی OLTP:
این سیستمها باعث میگردند تا چرخ کسب و کار بگردد. وجود این سیستمها سبب میشود تا دادههای مربوط به کسب و کار، به بانک اطلاعاتی وارد شوند. این سیستمها عموماً:
o به دلیل کوتاهی عملیات دارای سرعت قابل توجهی میباشند.
o محیطی جهت ورود دادهها میباشند.
o معمولاً اپراتورها، استفاده کنندههای آن هستند.
• سیستمهای اطلاعاتی OLAP ، DW/BI، DSS :
این سیستمها باعث میگردند تا چرخش کسب و کار را بنگرید. فلسفه بکارگیری این سیستمها در سازمان این است که اطلاعات مورد نیاز مدیران، از درون دادههای سیستمهای عملیاتی موجود، استخراج گردد. این سیستمها عموماً:
سیستمهای عملیاتی در جامعه ما سابقه بیشتری داشته و متخصصین فناوری اطلاعات عموماً با طراحی و تولید چنین سیستم هایی آشنایی کافی دارند. متاسفانه جایگاه سیستمهای اطلاعاتی در جامعه ما کمتر شناخته شده و متخصصین فناوری اطلاعات بندرت با مفاهیم و نحوه پیاده سازی آن آشنایی دارند.o به دلیل آنالیز حجم انبوهی از داده ها، معمولاً کندتر از سیستمهای عملیاتی میباشند.
o محیطی جهت تولید گزارشات تحلیلی و آماری میباشند.
o معمولاً مدیران و تصمیم گیرندگان سازمان ها، استفاده کنندگان آن میباشند.
این نکته حائز اهمیت است که سیستمهای اطلاعاتی یک سیستم یا محصول نیستند که بتوان آنها را خریداری کرد. بلکه یک راهبرد (Solution, Approach) هستند و در حقیقت هر راهبردی مربوط به یک نوع کسب و کار (Business) و یا سازمان میباشد و نمیتوان فرمول واحدی را برای حتی سازمانهای مشابه، ارائه نمود.
گارتنر در ابتدای سال 2011 گزارشی را منتشر کرده که نشان میدهد بازار BI با 9.7 % رشد، ارزشی بالغ بر 10.8 بیلیون دلار داشته، ولی متاسفانه پروژههای آن به طور متوسط با 75% شکست مواجه شده است. در حالیکه 4 سال پیش، این رقم حدود 50% بود. این موسسه BI را پنجمین اولویت مدیران IT ذکر کرده است.
مفاهیم و مباحث مربوط به Data Warehouse به اواسط دهه 1980 برمی گردد، به زمانی که IBM تحقیقاتی را در این زمینه شروع کرد و نتیجه آنرا «Information Warehouse» نامید و هنوز هم در برخی منابع از این واژه بجای Data Warehouse استفاده میشود. از این پس برای راحتی از اختصار DW بجای Data Warehouse استفاده میشود. انبارهای داده جهت رفع نیاز رو به رشد مدیریت دادهها و اطلاعات سازمانی که توسط پایگاههای داده سیستمهای عملیاتی غیر ممکن بود، ساخته شدند.
انبار داده به مجموعه ای از دادهها گفته میشود که از منابع مختلف اطلاعاتی سازمان جمع آوری، دسته بندی و ذخیره میشود. در واقع یک انبار داده مخزن اصلی کلیه دادههای حال و گذشته یک سازمان میباشد که برای همیشه جهت انجام عملیات گزارش گیری و آنالیز در دسترس مدیران میباشد. انبارههای داده حاوی داده هایی هستند که به مرور زمان از سیستمهای عملیاتی آنلاین سازمان، استخراج میشوند. بنابراین سوابق کلیه اطلاعات و یا بخش عظیمی از آنها را میتوان در انباره دادهها مشاهده نمود.
از آنجائیکه انجام عملیات آماری و گزارشات پیچیده دارای بار کاری بسیار سنگینی برای سرورهای پایگاه داده میباشند، وجود انبار داده سبب میگردد که این گونه عملیات تاثیری بر فعالیت برنامههای کاربردی سازمان نداشته باشد.
همانگونه که پایگاه داده سیستمهای عملیاتی سازمان (برنامههای کاربردی) به گونه ای طراحی میشوند که انجام تغییر، حذف و اضافه داده به سرعت صورت پذیرد، در مقابل انبار دادهها دارای معماری ویژه ای میباشند که موجب تسریع انجام عملیات آماری و گزارش گیری میشود. در حقیقت میتوان اینگونه بیان نمود که انباره داده یک مخزن فعال و هوشمند از اطلاعات است که قادر است اطلاعات را از محیطهای گوناگون جمع آوری و مدیریت کرده و نهایتا پخش نماید و در صورت لزوم نیز سیاستهای تجاری را روی آنها اجرا نماید.
Bill Inmon:
او را پدر DW مینامند، از دیدگاه او DW هسته مرکزی چیزی است که او آنرا CIF اختصار (Corporate Information Factory) مینامد، که پایه و اساس BI بر مبنای آن قرار دارد. وی از طرفداران Top-Down Design میباشد که معتقد است در زمان طراحی باید با دیدی سازمانی، CIF را مدل سازی، ولی بصورت دپارتمانی پیاده سازی کرد (Think Globally, Implement Locally). در این نوع طراحی از DW به Data Mart خواهیم رسید.
Ralph Kimball Ph.D:
به نظر وی DW چیزی نیست جز یک کپی از دادههای عملیاتی که به طرز خاصی برای گزارشات و تحلیلهای آماری، آماده و ساختمند شده است. به بیان دیگر DW سیستمی است جهت استخراج، پالایش، تطبیق و تحویل اطلاعات منابع داده ای به یک بانک اطلاعاتی Dimensional و اجرای Query و گزارشات آماری و تحلیلی برای اهداف تصمیم گیری و استراتژیک سازمان.
وی معرفی کننده یکی از اساسیترین مفاهیم طراحی یعنی Dimensional Modeling است؛ ماحصل چنین ایده ای، اساس شکل گیری مدلی است که امروزه کارشناسان آنرا به نام Cube میشناسند. وی از طرفداران Bottom-Up Design است که در این نگرش از Data Mart به DW میرسیم. این روش به نظر عملیتر از روشی میباشد که به یکباره DW جامع و کامل برای اهداف سازمانی طراحی و پیاده سازی گردد.
تعریف انبار داده:
W.H.Inmon پدر DW آنرا چنین تعریف میکند:
The Data Warehouse is a collection of Integrated, Subject-Oriented databases designed to support the DSS function, where each unit of data is Non-Volatile and relevant to some moment in Time
از تعریف فوق دو مورد دیگر نیز به طور ضمنی استنباط میشود:
به بیان دیگر DW راهبردی است که دسترسی آسان به اطلاعات درست (Right Information)، در زمانی درست (Right Time) ، به کاربران درست (Right Users)، را فراهم میآورد تا «تصمیم گیری سازمانی» قابل انجام باشد. DW صرفاً یک محصول نرم افزاری و یا سخت افزاری نیست که بتوان آنرا خریداری نمود بلکه فراتر از آن و در حقیقت یک محیط پردازشی میباشد که کاربران میتوانند از درون آن اطلاعات مورد نیاز خود را بیابند.
DW اطلاعات خود را از سایر بانکهای اطلاعاتی از نوع OLTP و یا سایر DWهای لایه پایینتر و به صورت دسته ای (Batch) و یا انبوه (Bulk Loading) جمع آوری میکند. یک DW به صورت سنتی باید شامل دادههای Historic سازمان باشد و میتوان اینگونه بیان نمود که در DW هرچه دادههای قدیمیتری موجود باشد، اعتبار تحلیلهای آماری سیستم افزایش خواهد یافت.
دادههای سیستم عملیاتی را نمیتوان بلافاصله درون بانک اطلاعاتی DW لود نمود، چنین داده هایی باید آماده سازی، پالایش و همگون گردند تا شرایط لود در DW را داشته باشند. حداقل کاری که انتظار داریم یک DW در مورد دادهها برای ما برآورده سازد شامل موارد زیر است:
ویژگیهای دادههای درون DW
دادههای DW از نگاه Inmon دارای 4 ویژگی اصلی زیر هستند:
o انبار داده به طور فیزیکی، کاملاً جدا از سایر سیستمهای عملیاتی است.
o دادههای DW مجموعه ای Aggregated و Atomic از دادههای تراکنشهای سیستمهای عملیاتی است که سوای کاربرد آنها در سیستمهای عملیاتی، برای مقاصد مدیریتی نیز استفاده خواهد شد.
به بیان دیگر DW راهبردی است که دسترسی آسان به اطلاعات درست (Right Information)، در زمانی درست (Right Time) ، به کاربران درست (Right Users)، را فراهم میآورد تا «تصمیم گیری سازمانی» قابل انجام باشد. DW صرفاً یک محصول نرم افزاری و یا سخت افزاری نیست که بتوان آنرا خریداری نمود بلکه فراتر از آن و در حقیقت یک محیط پردازشی میباشد که کاربران میتوانند از درون آن اطلاعات مورد نیاز خود را بیابند.
DW اطلاعات خود را از سایر بانکهای اطلاعاتی از نوع OLTP و یا سایر DWهای لایه پایینتر و به صورت دسته ای (Batch) و یا انبوه (Bulk Loading) جمع آوری میکند. یک DW به صورت سنتی باید شامل دادههای Historic سازمان باشد و میتوان اینگونه بیان نمود که در DW هرچه دادههای قدیمیتری موجود باشد، اعتبار تحلیلهای آماری سیستم افزایش خواهد یافت.
دادههای سیستم عملیاتی را نمیتوان بلافاصله درون بانک اطلاعاتی DW لود نمود، چنین داده هایی باید آماده سازی، پالایش و همگون گردند تا شرایط لود در DW را داشته باشند. حداقل کاری که انتظار داریم یک DW در مورد دادهها برای ما برآورده سازد شامل موارد زیر است:
o استخراج دادهها از منابع مختلف (مبدإ)با هر با اجرای پروسه فوق یکی از سه مورد زیر، بسته به نیاز طراحی و محدودیتهای تکنولوژی رخ خواهد داد:
o تبدیل دادهها به فرمتی یکسان
o لود دادهها به جداول مربوطه (مقصد)
o تمام دادهها در DW با دادههای جدید جایگزین خواهند گردید(Full Load, Initial Load, Full Refresh).
o دادههای جدید به دادههای موجود اضافه خواهند گردید (Incremental Load (Inserted data.
o نسخه جدیدی از دادههای کنونی به سیستم اضافه خواهند گردید (Incremental Load (Updated data.
ویژگیهای دادههای درون DW
دادههای DW از نگاه Inmon دارای 4 ویژگی اصلی زیر هستند:
o فقط خواندنی (Non-Volatile):
هیچ رکوردی و یا داده ای Update نخواهد شد و صرفاً رکوردهایی که محتوای مقادیر جدید دادهها هستند، به سیستم اضافه خواهند شد.
o موضوع گرا (Subject-Oriented):
منظور از «موضوع» پایههای اساسی یک کسب و کار هستند، به شکلی که با حذف یکی از این پایه ها، شاید ماهیت آن کسب و کار از ریشه دگرگون شود. برای مثال موضوعاتی چون «مشتری» و یا «بیمه نامه» برای شرکتهای بیمه.
o جامع (Integrated):
باید تمامی کدهایی که در سیستمهای عملیاتی وجود دارند و معانی یکسانی دارند، برای مثال کد جنسیت، در DW به یک روش ذخیره و نمایش داده شوند.
o زمانگرا (Time Variant):
هر رکورد باید حاوی فیلد و یا کلیدی باشد که نمایانگر این باشد که این رکورد در چه زمانی ایجاد، استخراج و ذخیره شده است. از آنجا که دادههای درون سیستمهای عملیاتی آخرین و به روزترین داده هر سیستم میباشد، نیازی به وجود چنین عنصری در سیستمهای OLTP احساس نمیگردد، ولی چون در DW تمام دادههای نسخ قدیمی دادههای سیستمهای عملیاتی موجود میباشد، باید حتماً مشخص گردد که هر داده ای در سیستمهای عملیاتی در چه زمانی، چه مقادیری داشته است. این عنصر زمانی کمک میکند تا بتوانیم:
منبع: کتاب آقای خشایار جام سحر با عنوان بانک داده تجمیعیo گذشته را آنالیز کنیم.
o اطلاعات مربوط به حال حاضر را بدست آوریم.
o آینده را پیش بینی کنیم.
Comparison Kimball vs. Inmon
Inmon
Continuous & Discrete Dimension Management
Define data management via dates in your data
Continuous time
Kimball
Slowly Changing Dimension Management
Define data management via versioning
Type I
Inmon
Continuous & Discrete Dimension Management
Define data management via dates in your data
Continuous time
When is a record activeDiscrete time
Start and end dates
A point in time
Snapshot
Kimball
Slowly Changing Dimension Management
Define data management via versioning
Type I
Change record as requiredType II
No History
Manage all changesType III
History is recorded
Some history is parallel
Limit to defined history
Kimball | Inmon |
Business-Process-Oriented Stresses Dimensional Model, Not E-R | Subject-Oriented Integrated Non-Volatile Time-Variant |
Bottom-Up and Evolutionary | Top-Down |
Integration Achieved via Conformed Dimensions | Integration Achieved via an Assumed Enterprise Data Model |
Star Schemas Enforce Query Semantics | Characterizes Data marts as Aggregates |
Kimball | Inmon | |
Bottom-up | Top-down | Overall approach |
Data marts model a business process;enterprise is achieved with conformed dims | Enterprise-wide DW feeds departmental DBs | Architectural structure |
Fairly simple | Quite complex | Complexity of method |
Process oriented | Subject or data driven | Data orientation |
Dimensional modeling; departs from traditional relational modeling | Traditional ERDs and DIS | Tools |
High | Low | End user accessibility |
Slowly Changing | Continuous & Discrete | Timeframe |
Dimension keys | Timestamps | Methods |
اشتراکها
کتاب FakeItEasy Succinctly
اشتراکها
مهاجرت به RyuJIT تکمیل شد
Overall, our RyuJIT investments have been focused on evolving the code base towards enabling better support for:
- Multiple code generation targets (instruction sets and operating systems),
- Improved optimizations,
- Better and more flexible code generation, and
- Open, flexible, and robust design and implementation.
راهنماهای پروژهها
تبدیل URL های داخل رشته به لینک توسط متد الحاقی Linkify
یکی از نیازهای سیستمهای مدیریت محتوای تحت وب، تبدیل URLهای داخل رشته به Hyperlink است. در زیر نحوه استفاده از متد الحاقی Linkify را مشاهده میکنید:
Console.WriteLine("This goes to the https://www.dntips.ir website".Linkify() ); Console.WriteLine("This goes to the https://www.dntips.ir website".Linkify("_blank") ); Console.WriteLine("This goes to the www.dotnettips.info website".Linkify() ); Console.WriteLine("This goes to the dotnettips.info website".Linkify("_blank") ); Console.WriteLine("This goes to the dotnettips.info/page.html page".Linkify() ); Console.WriteLine("This goes to the https://www.dntips.ir/folder/page.html page".Linkify("_blank") );
این متد در کلاس WebExtensions پیاده سازی شده است.