نظرات مطالب
کار با Kendo UI DataSource
سلام؛ من در حل این مشکل به یک نکته برخوردم و آن اینه که وقتی view من دقیقا مشابه آن چیزی هست که شما در جواب فوق آدرس دادید من موفق به دریافت پارامتر‌ها میگردم. اما اگر ویو من به شکل زیر باشد پارامتر‌ها را نال بر میگرداند.
   <script type="text/javascript">
        $(function () {
            //            var r = "12";
            var productsDataSource = new kendo.data.DataSource({
                transport: {
                    read: {
                        url: "@Url.Action("GetProducts", "Home")",
                        dataType: "json",
                        contentType: 'application/json; charset=utf-8',
                        type: 'GET',
                        data: { param1: "dfvdf", param2: "val2" } // ارسال اطلاعات اضافی و سفارشی به سرور در حین درخواست
                    },
                    create: {
                        url: "@Url.Action("PostProduct","Home")",
                        contentType: 'application/json; charset=utf-8',
                        type: "POST"
                    },
                    update: {
                        url:// function (product) {
                             "@Url.Action("UpdateProduct","Home")",//, +product.Id;
                        //},
                        contentType: 'application/json; charset=utf-8',
                        type: "PUT"
                    },
                    destroy: {
                        url: function (p) {
                            return "@Url.Action("DeleteProduct","Home")/" + p.Id;
                        },
                        contentType: 'application/json; charset=utf-8',
                        type: "DELETE"
                    },
                    parameterMap: function (options) {
                        return kendo.stringify(options);
                    }
                },
                schema: {
                    parse: function (data) {
                        return data;
                    },
                    data: "Data",
                    total: "Total",
                    model: {
                        id: "Id", // define the model of the data source. Required for validation and property types.
                        fields: {
                            "Id": { type: "number", editable: false }, //تعیین نوع فیلد برای جستجوی پویا مهم است
                            "Name": { type: "string", validation: { required: true }, editable: true },
                            "Discription": { type: "string", },
                            "Title": { type: "string", editable: false },
                            "GroupName": { type: "string", },
                            "Link": { type: "string" }

                        }

                    },
                    batch: false,


                },
                error: function (e) {
                    alert(e.errorThrown.stack);
                },
                pageSize: 5,
                sort: { field: "Id", dir: "desc" }

            });
            $("#report-grid").kendoGrid({
                dataSource: productsDataSource,
                autoBind: true,
                scrollable: false,
                pageable: true,
                sortable: true,
                columns: [
                { field: "Id", title: "#" },
                { field: "Name", title: "Product" }
                ]
            });
        });
    </script>
نظرات مطالب
EF Code First #10
وقتی کلاینت Java Script ای یا Silverlight ای در سمت کلاینت داره Change Tracking رو انحام می‌ده و برای Update داره Current و Original رو با هم برام ارسال می‌کنه، و تغییرات سمت سرور واقعا ناچیزه، واقعا می‌طلبه که نه تنها Proxy Creation رو غیر فعال کرد، که بنده حتی Automatic Change Tracking رو هم غیر فعال می‌کنم، فقط در Override کردن Save Changes در DbContext یک بار دستی
Change Tracker.Detect Changes رو فراخونی می‌کنم
موقع Load کردن اطلاعات برای ارسال به سمت کلاینت نیز همیشه از As No Tracking استفاده می‌کنم، و واقعا تا این لحظه به هیچ وجه حس نکردم که چیزی رو از دست دادم، Lazy Loading هم واقعا آیتم حیاتی ای نیست.
ولی از اون طرف من نه ساخته شدن Proxy رو دارم، نه فراخونی Detect Changes رو به صورت پشت صحنه ای در اکثر متدهای EF و نه سربار ساخته شدن Entry‌ها هنگام Load ( البته در بازگشت از کلاینت به سرور، Attach می‌کنم، که راه در رویی هم نداره )
چون این موارد همه در Repository قرار داده شدند، عملا کدنویسی خودم رو هم تحت الشعاع قرار ندادم 
به همه افراد توصیه می‌کتم که این کار رو انجام بدند
نظرات مطالب
معرفی Blazor Hybrid
ببینید NET MAUI App که از اساس با Xaml کار می‌کنه و البته Native UI محسوب می‌شه

Blazor Hybrid یا همون NET MAUI Blazor App راهکار Blazor هست که HTML/CSS برای UI استفاده می‌شه، ولی C# .NET اش دسترسی کامل به سیستم عامل داره (بر خلاف Blazor Web Assembly که محدود به Sandbox مرورگر هست)

پس با Blazor Web Assembly & Server می‌تونیم PWA / SPA / Web Site داشته باشیم و طبیعتا محدود به Sandbox مرورگر هست
با Blazor Hybird هم Android / iOS / Windows app داریم که دسترسی به امکانات سیستم عامل هست که UI با HTML / CSS زده می‌شه

یا Blazor Native شما دیگه UI تون HTML/CSS نیست و برای داشتن TextBox به جای input type=text، از Entry استفاده می‌کنید برای مثال که پشت صحنه مپ می‌شه به کنترل‌های Native در Android / iOS / Windows

توصیه من این هست که Blazor رو به صورت Multi Mode تنظیم کنید، به صورتی که UI رو با HTML / CSS بزنید، و هم خروجی Android بگیرید و هم iOS و Web و ویندوز
برای درک بهتر این مسئله می‌تونید وبینارم رو در رابطه با 
what's new in dotnet 6  ببینید
مطالب
مدیریت محل اعمال Google analytics در ASP.NET MVC
ذخیره سازی اطلاعات بازدیدهای کاربران، در طول زمان حجم بالایی از بانک اطلاعاتی را به خود اختصاص خواهد داد؛ به علاوه کند شدن کوئری‌های مرتبط با آن، به همراه مصرف بالای منابع سیستم. به همین جهت اکثر سایت‌ها از Google analytics برای مدیریت جمع آوری بازدیدهای کاربران خود استفاده می‌کنند و این ابزار واقعا عالی و حرفه‌ای طراحی شده و پیاده سازی همانند آن شاید در حد یک پروژه‌ی‌ چندساله باشد.
اضافه کردن Google analytics به یک سایت، بسیار ساده است. در آن ثبت نام می‌کنید؛ سپس آدرس دومین خود را وارد کرده و یک قطعه کد جاوا اسکریپتی را دریافت خواهید کرد که باید به انتهای تمام صفحات سایت خود اضافه نمائید و ... همین.
اضافه کردن این کد در ASP.NET MVC می‌تواند در فایل layout یا همان master page سایت انجام شود تا به صورت خودکار به تمام صفحات اعمال گردد.

مشکل!
من نمی‌خواهم که صفحات غیرعمومی سایت نیز دارای کدهای Google analytics باشند و بی‌جهت Google به این‌جاها نیز سرکشی زاید کند! چکار باید کرد؟
احتمالا عنوان می‌کنید که باید یک if و else به همراه آرایه‌ای از نام‌ها و آدرس‌های صفحات غیرعمومی سایت تهیه کرد و بر این اساس کدهای Google analytics را در master page درج کرد یا خیر.
بله. این روش کار می‌کنه ولی بهینه نیست و همچنین نگهداری آن در طول زمان مشکل است. سایت توسعه خواهد یافت، صفحات غیرعمومی بیشتر خواهند شد و ممکن است در این بین فراموش شود که کدهای مرتبط به روز شوند.

روش بهتر:
آیا می‌توان در یک View مشخص کرد که فیلتر Authorize در اکشن متد متناظری که آن‌را رندر کرده است بکار گرفته شده است یا خیر؟
صفحات غیرعمومی سایت در ASP.NET MVC با فیلتر Authorize محافظت می‌شوند. این فیلتر را می‌توان به کل یک کنترلر اعمال کرد تا به تمام اکشن متدهای آن اعمال شود؛ یا فقط به یک اکشن متد خاص که Viewایی خاص را رندر می‌کند.

نحوه پیاده سازی تشخیص وجود فیلتر Authorize را در یک View رندر شده، در متد کمکی زیر می‌توان مشاهده کرد:
@helper IncludeGoogleAnalytics(WebViewPage page)
    {
        var controller = page.ViewContext.Controller;
        var controllerHasAuthorizeAttribute = controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true).Any();
        var currentActionName = page.ViewContext.Controller.ValueProvider.GetValue("action").RawValue.ToString();
        var actionHasAuthorizeAttribute = controller.GetType().GetMethods()
                                                    .Where(x => x.Name == currentActionName &&
                                                                x.GetCustomAttributes(typeof(AuthorizeAttribute), true).Any())                                                    
                                                    .Any();
        if (!controllerHasAuthorizeAttribute && !actionHasAuthorizeAttribute)
        {
            string trackingId = ConfigurationManager.AppSettings["GoogleAnalyticsID"];
    <script type="text/javascript">
        var _gaq = _gaq || [];
        _gaq.push(['_setAccount', '@trackingId']);
        _gaq.push(['_trackPageview']);
        (function () {
            var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
            ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();    
    </script>
        }

  توضیحات:
این متد در فایلی به نام HtmlUtils قرار گرفته در پوشه app_code تعریف شده است و بکارگیری آن در یک فایل master page به نحو زیر خواهد بود:
@HtmlUtils.IncludeGoogleAnalytics(this)

در این متد به کمک خاصیت page.ViewContext.Controller می‌توان به کنترلری که در حال رندر کردن View جاری است دسترسی یافت. اکنون که به کنترلر دسترسی داریم، به کمک Reflection، ویژگی‌ها یا Attributes آن‌را یافته و بررسی می‌کنیم که آیا دارای AuthorizeAttribute است یا خیر. بر این اساس می‌توان تصمیم گرفت که آیا View در حال نمایش عمومی است یا خصوصی. اگر عمومی بود، کدهای اسکریپتی Google analytics به صورت خودکار به صفحه تزریق می‌شوند.
همچنین در اینجا فرض بر این است که Id منتسب به دومین جاری در کلیدی به نام GoogleAnalyticsID در فایل کانفیگ برنامه در قسمت app settings آن تعریف شده است.
 
نظرات مطالب
رسم نمودار توسط Kendo Chart
از خاصیت چرخش آن باید استفاده کنید:
<div id="chart"></div>
<script>
$("#chart").kendoChart({
  series: [{
    data: [1, 2, 3]
  }],
  valueAxis: {
    notes: {
      label: {
        rotation: 90
      },
      data: [{ value: 1 }]
    }
  }
});
</script>
مطالب
بازسازی کد: جایگزینی آرایه با شیء (Replace array with object)
از آرایه برای ذخیره سازی آیتم‌های مشابه استفاده می‌شود. این تشابه باید علاوه بر اینکه در نوع داده‌ای آیتم‌ها رعایت شود، باید از نظر مفهومی نیز رعایت شود.
زمانیکه از یک آرایه برای نگهداری المنت‌های غیر مشابه استفاده می‌شود، نیاز به چنین بازسازی کدی است. به طور مثال آرایه‌ای که آیتم اول آن "نام" و آیتم دوم آن "امتیاز" است. قطعا کار با چنین آرایه‌ای بسیار مشکل خواهد بود. زمانیکه یک آرایه را از نوع داده‌ای عمومی‌تری (مثلا object در سی شارپ) تعریف و انواع داده‌ای متفاوت را در آیتم‌های آن نگهداری کنیم، اوضاع بسیار بدتر خواهد شد. 
محور اصلی بازسازی کد "جایگزینی آرایه با شیء" ایجاد یک کلاس، برای ذخیره اطلاعات آرایه است. به این صورت که برای هر آیتم آرایه، یک خصوصیت در کلاس مربوطه ایجاد می‌شود. 
به طور مثال به آرایه زیر توجه نمایید:
var row = new string[3]; 
row[0] = "Liverpool"; 
row[0] = "15";
در آرایه بالا، آیتم اول نشان دهنده نام تیم و آیتم دوم نشان دهنده امتیاز تیم است. با وجود اینکه این مثال کمی غیر واقعی به نظر میرسد، اما چنین مثال‌هایی در برنامه نویسی روزمره ممکن است به اشکال مختلفی مشاهده شود. مانند استفاده از dictionary برای دریافت اطلاعات فرم وب، استفاده از Tuple (در زبان سی شارپ) برای انتقال اطلاعات و … 
در این مثال طراحی بهتر، ایجاد یک کلاس یا ساختار (بسته به شرایط کلی مسئله) برای نشان دادن امتیاز تیم است:  
public class Performance 
{ 
       public string TeamName { get; set; } 
       public int Score { get; set; } 
}
همانطور که مشاهده می‌کنید، به ازای هر یک از آیتم‌های آرایه، خصوصیتی در کلاس جدید ایجاد شده‌است. همچنین انتخاب انواع داده‌ای نیز در طراحی جدید، ساده‌تر و اصولی‌تر انجام خواهد شد.
تمامی استفاده‌ها از آرایه‌ها، در دسته بندی این نوشتار برای بازسازی کد قرار نمی‌گیرند. آرایه‌هایی که اصل مشابه بودن آیتم‌ها را رعایت می‌کنند، معمولا نیازی به بازسازی کد ندارند. به طور مثال در نرم افزارهای فروشگاه اینترنتی، خصوصیات کالا به صورت داینامیک ذخیره شده و احتمالا برای دسترسی و مدیریت آن، از آرایه یا لیست استفاده می‌شود. اما با کمی دقت خواهیم دید، این استفاده از آرایه، با تعریف مشابه بودن آیتم‌ها همخوانی دارد. زیرا تمامی آیتم‌های آرایه به طور مثال از نوع خصوصیت کالا هستند. همچنین عملا امکان بازسازی و ایجاد کلاس در این مثال وجود ندارد؛ زیرا خصوصیات کالاها در زمان توسعه مشخص نیستند و در زمان اجرای برنامه تنظیم می‌شوند. 
مطالب
مدل EAV چیست؟

EAV مخفف ( Entity Attribute Value ) می‌باشد، مدلی از طراحی دیتابیس که کاربر را به آیتم‌های ثابت محدود نمی‌کند، فرض کنید در یک فروشگاه می‌خواهید چندین کالا بفروشید هر کالا هم برای خودش ویژگی‌های منحصر به فرد دارد، آیا با ویژگی‌های ثابت برای کالاهای متفاوت می‌توان پاسخگوی نیاز مشتری بود؟ یقینا پاسخ منفی خواهد بود.

موجودیت ( Entity ): در یک سیستم می‌تواند کالا، مشتری، فروشنده و... باشد.

ویژگی ( Attribute ):برای کالا: رنگ، وزن و... برای مشتری:نام، تلفن،آدرس و... می‌باشد

مقدار( value ) : هر ویژگی برابر مقداری می‌باشد مثلا برای رنگ‌ها آبی، قرمز و.. می‌باشد

جداول پایه طراحی شده:

مدل EAV

ورود داده ها:

شیوه ورود داده‌ها را برای موجودیت کالا بیان می‌کنیم
ابتدا کالا در جدول موجودیت ثبت می‌گردد
سپس عنوان ویژگی‌های آن مانند رنگ، وزن و... در جدول ویژگی‌ها ثبت می‌گردد.

مقدار هر ویژگی  هم در جدول مقدار‌ها ثبت می‌شود.

در زیر شیوه ذخیره به صورت شکل مشاهده می‌کنید.


شیوه خواندن داده ها:

این قسمت هم به راحتی با 2 inner join می‌توان به کالا، ویژگی‌ها و مقادیر آن دست پیدا کرد.

نکات:

نکته1: این 3 جدول را باید برای هر موجودیت قابل توسعه ایجاد کرد، مثلا برای کالا، مشتری و...

نکته2: می‌توان برای گروه بندی کالا‌ها و همچنین ویژگی‌ها جداول جداگانه ایی تعریف کرد.

نکته3: از مهمترین ویژگی‌های این تفکر قابل گسترش بودن سیستم می‌باشد.

نکته4: می‌توان برای آیتم هایی مثل نمایش داده شود یا خیر، چیدمان نمایش و...آیتم هایی به جدول ویژگی‌ها اضافه کرد.

نکته5: این مدل در نرم افزار magento استفاده شده است.

همچنین جهت مطالعه بیشتر ساختار دیتابیس مجنتو در لینک زیر می‌باشد.

MAGENTO_v1.0.19700---Database-Diagram.zip 
منابع: Entity–attribute–value model

مطالب
پیاده سازی کنترلرهای Angular با استفاده از Typescript
پیشتر با ویژگی ها  و نحوه کد نویسی این زبان آشنا شدید. از طرفی دیگر، نحوه تعریف کنترلرها در Angular نیز آموزش داده شد. در این پست قصد دارم طی یک مثال ساده با استفاده از زبان Typescript یک کنترلر Angular را ایجاد  و سپس از آن در یک پروژه Asp.Net MVC استفاده نمایم. از آن جا که به صورت پیش فرض در VS.Net امکانات TypeScript نصب نشده است، برای شروع ابتدا TypeScript را از اینجا دانلود نمایید. بعد از نصب یک پروژه Asp.Net MVC ایجاد نمایید و سپس با استفاده از nuget فایل‌های مربوط به AngularJs  را نصب نمایید. در این پست به تفصیل این مورد بررسی شده است (عملیات BundleConfig فایل‌های مورد نیاز به عهده خودتان). در پوشه scripts یک فولدر به نام app ساخته، سپس یک فایل TypeScript به نام ProductController.ts ایجاد کنید. (بعد از نصب TypeScript گزینه TypeScript File مشاهده خواهد شد)
 

در فایل ProductController.ts کد‌های زیر را کپی نمایید: 

module Product {
    export interface Scope {
        message: string;
    }

    export class Controller {
        constructor($scope: Scope) {
            $scope.message = "Hello from Masoud";
        }
    }
}
توضیح کد‌ها بالا :

ابتدا یک ماژول به نام Product ایجاد می‌کنیم. سپس یک اینترفیس برای پیاده سازی آبجکت Scope که جهت مقید سازی عناصر DOM به آبجکت‌های کنترلر مورد استفاده قرار می‌گیرد، ایجاد می‌کنیم. در داخل این اینترفیس متغیری به نام message از نوع string داریم. قصد داریم این متغیر را به یک  عنصر مقید کنیم. حال یک کلاس به نام کنترلر ایجاد می‌کنیم که در تابع سازنده آن تزریق وابستگی برای scope$ از نوع اینترفیس Scope تعیین شده است. در نتیجه در بدنه سازنده می‌توانیم به متغیر message مقدار مورد نظر را نسبت دهیم .

کلمه کلیدی export برای تعریف عمومی کلاس استفاده شده است .
یک View ایجاد و کد‌های زیر را در آن کپی کنید :

<script type="text/javascript" src="~/scripts/app/ProductController.js"></script>
<div ng-app>
    <div ng-controller="Product.Controller">
        <p>{{message}}</p>
    </div>
</div>

اولین نکته در تگ script است که فراخوانی فایل TypeScript باید با پسوند   js. انجام گیرد. به دلیل اینکه فایل‌های TypeScript بعد از کامپایل تبدیل به فایل‌های JavaScript خواهند شد؛ در نتیجه پسوند آن نیز js. است. دومین نکته در فراخوانی کنترلر مورد نظر است که  از ترکیب نام ماژول و نام کلاس است. بعد از اجرای پروژه خروجی به صورت زیر خواهد بود :

 

مطالب
استفاده از Workflow برای تغییر سطح دسترسی به یک سند پس از تایید آن
در این پست قصد دارم نحوه تغییر دسترسی یک سند را بیان کنم . مسئله : فرض کنید قرار است سندی در کتابخانه بارگذاری شود و پس از تغییرات و پالایش توسط فردی تایید (Approve) شود و پس از تایید ، دیگر تهیه کننده اولیه سند نمی‌تواند آن را ویرایش کند . با استفاده از workflow این روند را با هم پیش می‌گیریم .

یک کتابخانه به نام FinalApprovedLib در شیرپوینت ساخته و در شیرپوینت دیزاینر ان را انتخاب می‌کنیم : 

سپس وارد تنظیمات کتابخانه شده و روی List Workflow کلیک می‌کنیم : 

در پنجره نامی را برای چرخه کاری انتخاب می‌کنیم :  

درگام اول چرخه کاری ، یک Start Approval Process انتخاب می‌کنیم (از نوار action ریبون بالای پنجره قابل مشاهده است ؛ همچنین می‌توانید چند تا از حروف این گام را تایپ کنید تا به صورت خودکار لود شود) : 

سپس روی these users کلیک کرده تا تایید کننده را مشخص کنیم :

در این مرحله پنجره زیر باز شده و عنوان پیغام و محتوای آن را می‌نویسیم : 

سپس در بخش participants نام فرد ، گروه یا افرادی که می‌توانند این چرخه را تایید کنند را مشخص میکنیم :

 

در اینجا کاربری با نام usr1 باید بتواند این سند را تایید کند : 

روی add و سپس روی OK کلیک می‌کنیم : 

اگر در همین مرحله چرخه را publish کنیم می‌توانیم آن را در لیست چرخه‌های کاری لیست مشاهده کنیم :

 

در گام بعدی نیاز به یک impersonation Step داریم (اطلاعات بیشتر درباره این گام در اینجا و اینجا ) :

در ابتدا یک If current item field equals value انتخاب می‌کنیم و مانند زیر آن را تنظیم می‌کنیم :
 

سپس واژه equal که پیش فرض است و سپس عبارت سمت راست تساوی که وضعیت چرخه است :

 

حال می‌خواهیم زمانی که سند تایید شده (شرط true بود) ، عملیات تغییر دسترسی اعمال شود :

 

گزینه replace list item permission را انتخاب می‌کنیم و مانند زیر آن را تنظیم می‌کنیم :

 باید permission مورد نظر را اتتخاب کنیم و تعریف کنیم که این دسترسی برای چه کسی و به چه نوعی تغییر کند :
ابتدا فردی که باید دسترسی او تغییر کند (در اینجا سازنده سند جاری ) : 

سپس دسترسی جدید او : 

و تایید تنظیمات اعمال شده (می توانید تغییرات را در گام تعریف شده مشاهده کنید) :

 

حال چرخه کاری را ذخیره و publish می‌کنیم:

 

حال اگر workflow‌های کتابخانه را بررسی کنیم ، می‌توانیم ذخیره شدن این چرخه را در کتابخانه ببینیم :

 

فقط نکته ای که نباید فراموش شود ، نحوه start شدن workflow است که باید ان را نیز تنظیم کنیم :

در همان شیرپوینت دیزاینر روی چرخه کاری کلیک راست می‌کنیم و در تنظیمات چرخه کاری ، مانند زیر عمل می‌کنیم تا به محض افزودن یا اعمال تغییرات در سند ، چرخه کاری فراخوانی شود :

 

 (بعد از این تغییرات مجدد عمل publish را انجام دهید)

حال سندی را بارگذاری می‌کنیم تا این چرخه را تست کنیم : 

با نام کاربری usr2 این سند را بارگذاری می‌کنیم :

 

اگر به Task List سایت مراجعه کنیم می‌توانیم آغاز شدن چرخه کاری و پیغام فرستاده شده آن را ببینیم :

 

حال اگر کاربر usr1 که در بالا عملیات تایید به او انتساب داده شد ، آیتم Task list خود را باز کند ، پنجره زیر را خواهد دید :

 

و پس از تایید او دکمه ویرایش برای usr2 (که سند را ایجاد کرده بود) غیر فعال می‌شود :

- نکته اینکه در صورتی که usr2 مجدد سندی را با همان نوع و نام بارگذاری کند ، سیستم به او پیغام می‌دهد که دسترسی برای این کار را ندارد 

موفق باشید