public class CustomerInfo { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } }
public interface IModelBinder { object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext); }
using System; using System.Web; using System.Web.Mvc; using ModelBinderExample.Models; using Persia; namespace ModelBinderExample.CustomModelBinder { // Article written for www.dotnettips.info public class CustomerInfoModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { HttpRequestBase request = controllerContext.HttpContext.Request; string firstName = request.Form.Get("FirstName"); string lastName = request.Form.Get("LastName"); DateTime birthDate = this.GetMiladiDate(request); return new CustomerInfo() { FirstName = firstName, LastName = lastName, BirthDate = birthDate }; } private DateTime GetMiladiDate(HttpRequestBase request) { int day = int.Parse(request.Form.Get("Day")); int month = int.Parse(request.Form.Get("Month")); int years = int.Parse(request.Form.Get("Years")); //Convert shamsi to miladi return Persia.Calendar.ConvertToGregorian(years, month, day, DateType.Gerigorian); } } }
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); //Register New ModelBinder ModelBinders.Binders.Add(typeof(CustomerInfo), new CustomerInfoModelBinder()); }
[HttpPost] public ActionResult Create([ModelBinder(typeof (CustomerInfoModelBinder))] CustomerInfo customerInfo) { if (ModelState.IsValid) { ViewBag.FirstName = customerInfo.FirstName; ViewBag.LastName = customerInfo.LastName; ViewBag.BirthDate = customerInfo.BirthDate; } return View(); }
حالت forms authentication در ASP.Net ، امکان تعریف کوکیهایی ماندگار را نیز جهت ورود خودکار کاربران در دفعات بعدی بازدید آنها فراهم میکند. اما زمان منقضی شدن این کوکیهای ماندگار در ASP.Net 1.1 و ASP.Net 2.0 به بعد کاملا با هم متفاوت بوده و اگر برنامه نویس از این تغییر حاصل شده مطلع نباشد ممکن است بارها و بارها برنامه را آزمایش کند اما نتیجهای نگیرد.
مدت زمان منقضی شدن کوکیهای ماندگار forms authentication در ASP.Net 1.1 به صورت زیر است (بود):
زمان منقضی شدن کوکی سشن = زمان جاری + زمان منقضی شدن سشن کاربر
زمان منقضی شدن کوکی ماندگار (persistent) = زمان جاری + 50 سال
این عدد 50 سال بسیار غیرمنطقی بوده و در ASP.Net 2.0 به بعد به صورت زیر اصلاح شده است:
زمان منقضی شدن کوکی سشن = زمان جاری + زمان منقضی شدن سشن کاربر
زمان منقضی شدن کوکی ماندگار (persistent) = زمان جاری + زمان منقضی شدن سشن کاربر (پیش فرض آن 30 دقیقه است)
و جهت بیشتر کردن طول عمر این ماندگاری باید زمان مورد نظر را در وب کانفیگ به صورت زیر اعمال کرد:
<authentication mode="Forms">
<forms
…
name="MyCookieName"
slidingExpiration="true"
timeout="43200"
/>
</authentication>
همچنین نکتهی مهم دیگری را که باید رعایت کرد، name ایی است که در این فایل config عنوان میکنید (در قسمت تنظیمات form authentication). اگر بر روی یک وب سرور، چندین برنامه وب ASP.Net را در حال اجرا دارید، باید برای هر کدام از اینها نامی جداگانه و منحصربفرد انتخاب کنید، در غیراینصورت تداخل رخ داده و باز هم گزینه مرا به خاطر بسپار شما کار نخواهد کرد.
کار slidingExpiration که در اینجا تنظیم شده است نیز به صورت زیر میباشد:
اگر لاگین موفقیت آمیزی ساعت 5 عصر صورت گیرد و timeout شما به عدد 10 تنظیم شده باشد، این لاگین به صورت خودکار در 5:10 منقضی خواهد شد. اما اگر در این حین در ساعت 5:05 ، کاربر یکی از صفحات سایت شما را مرور کند، زمان منقضی شدن کوکی ذکر شده به 5:15 تنظیم خواهد شد. (مفهوم تنظیم slidingExpiration)
لازم به ذکر است که اگر کاربر پیش از نصف زمان منقضی شدن کوکی (مثلا در 5:04)، یکی از صفحات را مرور کند، تغییری در این زمان نهایی منقضی شدن رخ نخواهد داد.
لینکهای هفته اول دی
وبلاگها و سایتهای ایرانی
امنیت
ASP. Net
طراحی وب
PHP
- خبرهایی در مورد مایکروسافت و PHP
اسکیوال سرور
سی شارپ
عمومی دات نت
مسایل اجتماعی و انسانی برنامه نویسی
کتابهای رایگان جدید
متفرقه
- آهن بجای کروم! (یک برنامه نویس آلمانی قسمتهایی از مرورگر کروم را که در جهت جمع آوری اطلاعات برای گوگل بکار میرفته، حذف کرده و مرورگر دیگری به نام آهن را ارائه داده است!)
مثال 12: محاسبه کنید در سال 2012 و به ازای هر ماه مجزای آن، چه تعداد slots رزرو شدهاند؛ قسمت دوم.
این مثال را در قسمت قبل (مثال 6 آن) نیز بررسی کردیم. در اینجا میخواهیم در گزارش نهایی تولید شده، پس از اتمام ردیفهای یک ماه به ازای یک امکان خاص، جمع کل آن نیز درج شود و همچنین در پایان تمام ردیفها، جمع کل نهایی ذکر شود؛ چیزی شبیه به تصویر زیر که در آن 910، جمع کل slots ماه 8 است و 9191، جمع کل سال.
روش پیشنهادی حل این مساله استفاده از مفهومی به نام «GROUP BY ROLLUP» است:
SELECT facid, DATEPART(month, [StartTime]) AS month, sum(slots) AS slots FROM bookings WHERE starttime >= '2012-01-01' AND starttime < '2013-01-01' GROUP BY ROLLUP(facid, DATEPART(month, [StartTime])) ORDER BY facid, month;
ابتدا جمع slots را گروه بندی شده بر اساس هر ماه سال محاسبه میکنیم. این قسمت توسط LINQ to Entities قابل انجام است؛ همان مثال 6 قسمت قبل است.
سپس این اطلاعات که اکنون در سمت کلاینت (یعنی برنامهی ما) در حافظه موجود هستند، نیاز دارند به ازای هر گروه، یک جمع کل (sub total) و به ازای کل سال نیز یک جمع کل (grand total یا total) پیدا کنند.
ROLLUP(facid, month) اطلاعات تجمعی سلسه مراتبی پارامترهای ارسالی به آن را تولید میکند. یعنی (facid, month), (facid) و (). پیاده سازی LINQ to Objects این تابع را در اینجا میتوانید مشاهده کنید: Utils\GroupingExtensions.cs
بنابراین راه حل این مساله به صورت زیر خواهد بود:
var date1 = new DateTime(2012, 01, 01); var date2 = new DateTime(2013, 01, 01); var facilities = context.Bookings .Where(booking => booking.StartTime >= date1 && booking.StartTime < date2) .GroupBy(booking => new { booking.FacId, booking.StartTime.Month }) .Select(group => new { group.Key.FacId, group.Key.Month, TotalSlots = group.Sum(booking => booking.Slots) }) .OrderBy(result => result.FacId) .ThenBy(result => result.Month) .ToList() //This is new .GroupByWithRollup( item => item.FacId, item => item.Month, (primaryGrouping, secondaryGrouping) => new { FacId = primaryGrouping.Key, Month = secondaryGrouping.Key, TotalSlots = secondaryGrouping.Sum(item => item.TotalSlots) }, item => new { FacId = item.Key, Month = -1, TotalSlots = item.SubTotal(subItem => subItem.TotalSlots) }, items => new { FacId = -1, Month = -1, TotalSlots = items.GrandTotal(subItem => subItem.TotalSlots) });
در اینجا سلولهایی که اطلاعاتی ندارند، با منهای یک مشخص شدهاند؛ در گزارش اصلی با null مقدار دهی شده بودند.
مثال 13: به ازای نام هر کدام از امکانات موجود، جمع کل تعداد ساعات رزرو شدهی آنها را محاسبه کنید.
هر slot تنها نیم ساعت است و گزارش نهایی باید به همراه ستونهای facid, name, Total Hours باشد؛ مرتب شده بر اساس facid.
var items = context.Bookings .GroupBy(booking => new { booking.FacId, booking.Facility.Name }) .Select(group => new { group.Key.FacId, group.Key.Name, TotalHours = group.Sum(booking => booking.Slots) / 2M }) .OrderBy(result => result.FacId) .ToList();
مثال 14: گزارشی را از اولین رزرو کاربران پس از September 1st 2012، تهیه کنید.
این گزارش باید به همراه ستونهای surname, firstname, memid, starttime باشد؛ مرتب شده بر اساس memid.
var date1 = new DateTime(2012, 09, 01); var items = context.Bookings .Where(booking => booking.StartTime >= date1) .GroupBy(booking => new { booking.Member.Surname, booking.Member.FirstName, booking.Member.MemId }) .Select(group => new { group.Key.Surname, group.Key.FirstName, group.Key.MemId, StartTime = group.Min(booking => booking.StartTime) }) .OrderBy(result => result.MemId) .ToList();
مثال 15: گزارشی را از کاربران تهیه کنید که هر ردیف آن، به همراه تعداد کل کاربران باشد.
این گزارش باید به همراه ستونهای count, firstname, surname باشد؛ مرتب شده بر اساس joindate.
var members = context.Members .OrderBy(member => member.JoinDate) .Select(member => new { Count = context.Members.Count(), member.FirstName, member.Surname }) .ToList();
SELECT COUNT(*) FROM [Members] AS [m]; SELECT [m].[FirstName], [m].[Surname], @__Count_0 AS [Count] FROM [Members] AS [m] ORDER BY [m].[JoinDate];
باید بخاطر داشت که ID کاربران پشت سرهم نیست و همچنین این گزارش باید به همراه ستونهای row_number, firstname, surname باشد؛ مرتب شده بر اساس joindate.
هدف اصلی از این مثال، کار با مفهوم window functionها و تابع row_number است:
SELECT row_number() OVER (ORDER BY joindate) AS row_number, firstname, surname FROM members ORDER BY joindate;
var members = context.Members .OrderBy(member => member.JoinDate) .Select(member => new { member.FirstName, member.Surname }) .ToList() /* SELECT [m].[FirstName], [m].[Surname] FROM [Members] AS [m] ORDER BY [m].[JoinDate] */ // Now using LINQ to Objects .Select((member, index) => new { RowNumber = index + 1, member.FirstName, member.Surname }) .ToList();
مثال 17: کدامیک از امکانات موجود، بیشترین slots رزرو شده را دارد؟ قسمت دوم.
این مورد همان مثال 11 قسمت قبل است که پاسخ آنرا یافتیم (و از تکرار مجدد آن صرفنظر میکنیم) و هدف اصلی آن رسیدن به کوئری window function دار زیر است که تنها از طریق اجرای یک raw sql در EF-Core قابل اجرا است:
SELECT facid, total FROM (SELECT facid, sum(slots) AS total, rank() OVER (ORDER BY sum(slots) DESC) AS rank FROM bookings GROUP BY facid) AS ranked WHERE rank = 1;
مثال 18: به کاربران بر اساس تعداد ساعات رزرو آنها، امتیاز دهی (رتبه بندی) کنید.
این گزارش باید به همراه ستونهای firstname, surname, hours, rank باشد؛ مرتب شده بر اساس rank, surname.
هدف اصلی از این مثال، رسیدن به کوئری rank دار زیر است:
SELECT mems.firstname, mems.surname, ((sum(bks.slots) + 10) / 20) * 10 AS hours, rank() OVER (ORDER BY ((sum(bks.slots) + 10) / 20) * 10 DESC) AS rank FROM bookings AS bks INNER JOIN members AS mems ON bks.memid = mems.memid GROUP BY mems.firstname, mems.surname ORDER BY rank, mems.surname, mems.firstname;
var itemsQuery = context.Bookings .GroupBy(booking => new { booking.Member.FirstName, booking.Member.Surname }) .Select(group => new { group.Key.FirstName, group.Key.Surname, Hours = (group.Sum(booking => booking.Slots) + 10) / 20 * 10 }) .OrderByDescending(result => result.Hours) .ThenBy(result => result.Surname) .ThenBy(result => result.FirstName); var rankedItems = itemsQuery.Select(thisItem => new { thisItem.FirstName, thisItem.Surname, thisItem.Hours, Rank = itemsQuery.Count(mainItem => mainItem.Hours > thisItem.Hours) + 1 }) .ToList();
با این خروجی SQL نهایی:
مثال 19: سه امکانی را لیست کنید که بالاترین میزان فروش را داشتهاند.
این گزارش باید به همراه ستونهای name, rank باشد؛ مرتب شده بر اساس rank.
روش محاسبهی این گزارش با مثال قبلی یکی است (البته اینبار رتبه بندی بر اساس TotalRevenue است) و فقط در انتهای آن یک Where(result => result.Rank <= 3) را بیشتر دارد:
var facilitiesQuery = context.Bookings.Select(booking => new { booking.Facility.Name, Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => b.Name) .Select(group => new { Name = group.Key, TotalRevenue = group.Sum(b => b.Revenue) }) .OrderBy(result => result.TotalRevenue); var rankedFacilities = facilitiesQuery.Select(thisItem => new { thisItem.Name, thisItem.TotalRevenue, Rank = facilitiesQuery.Count(mainItem => mainItem.TotalRevenue > thisItem.TotalRevenue) + 1 }) .Where(result => result.Rank <= 3) .OrderBy(result => result.Rank) .ToList();
مثال 20: امکانات موجود را بر اساس میزان فروشی که دارند به گروههایی با تعداد مساوی high, average, low تقسیم بندی کنید.
این گزارش باید به همراه ستونهای name, revenue باشد؛ مرتب شده بر اساس revenue, name.
هدف اصلی از این گزارش کار با تابع ntile است که اطلاعات را بر اساس پارامتر ارسالی به آن تاجای ممکن به گروههای مساوی تقسیم میکند:
SELECT name, CASE WHEN class = 1 THEN 'high' WHEN class = 2 THEN 'average' ELSE 'low' END AS revenue FROM (SELECT facs.name AS name, ntile(3) OVER (ORDER BY sum(CASE WHEN memid = 0 THEN slots * facs.guestcost ELSE slots * membercost END) DESC) AS class FROM bookings AS bks INNER JOIN facilities AS facs ON bks.facid = facs.facid GROUP BY facs.name) AS subq ORDER BY class, name;
var facilities = context.Bookings.Select(booking => new { booking.Facility.Name, Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => b.Name) .Select(group => new { Name = group.Key, TotalRevenue = group.Sum(b => b.Revenue) }) .OrderByDescending(result => result.TotalRevenue) .ToList();
SELECT [f].[Name], SUM(CASE WHEN [b].[MemId] = 0 THEN CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[GuestCost] ELSE CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[MemberCost] END) AS [TotalRevenue] FROM [Bookings] AS [b] INNER JOIN [Facilities] AS [f] ON [b].[FacId] = [f].[FacId] GROUP BY [f].[Name] ORDER BY SUM(CASE WHEN [b].[MemId] = 0 THEN CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[GuestCost] ELSE CAST ([b].[Slots] AS DECIMAL (18, 6)) * [f].[MemberCost] END) DESC;
var n = 3; var tiledFacilities = facilities.Select((item, index) => new { Item = item, Index = (index / n) + 1 }) .GroupBy(x => x.Index) .Select(g => g.Select(z => new { z.Item.Name, z.Item.TotalRevenue, Tile = g.Key, GroupName = g.Key == 1 ? "High" : (g.Key == 2 ? "Average" : "Low") }) .OrderBy(x => x.GroupName) .ThenBy(x => x.Name) ) .ToList(); var flatTiledFacilities = tiledFacilities.SelectMany(group => group) .Select(tile => new { tile.Name, Revenue = tile.GroupName }) .ToList();
مثال 21: چندماه طول میکشد تا هر کدام از امکانات موجود بر اساس فروشی که دارند، هزینهی مالکیت ابتدایی خود را کسب کنند.
این گزارش باید به همراه ستونهای name, months باشد؛ مرتب شده بر اساس name.
var facilities = context.Bookings.Select(booking => new { booking.Facility.Name, booking.Facility.InitialOutlay, booking.Facility.MonthlyMaintenance, Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => new { b.Name, b.InitialOutlay, b.MonthlyMaintenance }) .Select(group => new { group.Key.Name, RepayTime = group.Key.InitialOutlay / ((group.Sum(b => b.Revenue) / 3) - group.Key.MonthlyMaintenance) }) .OrderBy(result => result.Name) .ToList();
مثال 22: گزارش میانگین متحرک فروش کل هر کدام از روزهای August 2012 را برای یک بازهی 15 روزهی قبل، محاسبه کنید.
این گزارش باید به همراه ستونهای date, revenue باشد؛ مرتب شده بر اساس date. در این گزارش روزهای ماه 8 میلادی ردیف شده و به ازای هر ردیف، میانگین فروش 15 روز قبل از آن تاریخ، نمایش داده میشود. به همین جهت به آن میانگین متحرک نیز میگویند.
هدف اصلی از این گزارش، استفاده از توابع avg(revdata.rev) over است. اما چون نمیتوان از آنها در LINQ to Entities استفاده کرد، از روش دیگری که شامل جوین یک جدول با خودش است، استفاده میکنیم:
var startDate = new DateTime(2012, 08, 1); var endDate = new DateTime(2012, 08, 31); var period = 14; var dailyRevenueQuery = context.Bookings .Select(booking => new { StartDate = booking.StartTime.Date, // How to group by date (or TruncateTime) in EF-Core Revenue = booking.MemId == 0 ? booking.Slots * booking.Facility.GuestCost : booking.Slots * booking.Facility.MemberCost }) .GroupBy(b => b.StartDate) .Select(group => new { Date = group.Key, TotalRevenue = group.Sum(b => b.Revenue) });
اکنون که میزان کل فروش روزها را داریم، میخواهیم میانگین فروش 15 روز قبل شروع شدهی از از ابتدای ماه 8، تا انتهای آنرا محاسبه کنیم. برای اینکار نیاز است کوئری فوق را یکبار دیگر با خودش جوین کنیم تا از یک سر آن تاریخ هر روز و از طرف دیگر، میانگین 15 روز قبل، تولید شود:
var movingAvgs = dailyRevenueQuery .Select(dr1 => new { dr1.Date, MovingAvg = dailyRevenueQuery .Where(dr2 => dr2.Date <= dr1.Date && dr2.Date >= dr1.Date.AddDays(-period)) .Average(dr2 => dr2.TotalRevenue) }) .Where(result => result.Date >= startDate && result.Date <= endDate) .OrderBy(result => result.Date) .ToList();
کدهای کامل این قسمت را در اینجا میتوانید مشاهده کنید.
این ایونت «احتمالا» بخش اول سری بنچمارکینگ است، قصد دارم اهمیت و نکات بنچمارکینگ رو مرور کنیم و در بخش اول، روی داتنت و اسکیوال سرور کار خواهیم کرد و اگر استقبال بشه، در بخش دوم روی روی لایبریهای گو کار کنیم.
یکشنبه ۱۸ شهریور/ ۸ سپتامبر
ساعت ۶:۳۰ (به وقت تهران)، مدت: ۱ ساعت
همانطور که میدانیم پلاگینهای جیکوئری، نقش مهمی را در محیط وب ایفا میکنند. در اینجا با یکی از این پلاگینها و چگونگی استفاده از آن آشنا میشویم.
برای آشنایی با نوشتن Plugin در jQuery، میتوان مباحث پیشین این سایت را دنبال کرد.( JQuery Plugins #1 و JQuery Plugins #2)
این Plugin برای ایجاد یک TextBox برای ورود زمان توسط کاربر استفاده میشود. با توجه به اینکه قبلاً چند Plugin برای این کار نوشته شده است ولی هر کدام از آنها معایب و مزایای خاص خود را داشتند، برای نمونه میتوانید به این سایت مراجعه کنید.
ویژگیهای این Plugin عبارتند از:
1- تنظیم زمان پیش فرض
2- کنترل حداقل و حداکثر زمان وارد شده
3- تغییر ساعت و دقیقه بوسیله کلیدهای جهتی بالا و پایین
4- تغییر انتخاب ساعت و دقیقه بوسیله کلیدهای جهتی چپ و راست
5- تغییر ساعت و دقیقه بوسیله فشردن اعداد روی صفحه کلید
چگونگی استفاده از این Plugin
ابتدا کتابخانه jQuery و این پلاگین را به
صفحه خود اضافه نمایید و سپس کدهای زیر را برای استفاده از این Plugin اضافه نمایید:
jQuery(document).ready(function () { $("#TextBox1").TickTack(); $("#TextBox2").TickTack({ initialTime: '8:44', minHour: 8, minMinute: 0, maxHour: 22, maxMinute: 40 }); });
initialTime : زمان اولیه جهت نمایش به کاربر (حتما بایستی ساعت و دقیقه بوسیله ‘:’ از یکدیگر جداشوند)
minHour : حداقل ساعت ورودی
minMinute : حداقل دقیقه ورودی
maxHour : حداکثر ساعت ورودی
maxMinute : حداکثر دقیقه ورودی
پس از انتخاب TextBox، قسمت ساعت به صورت پیش فرض انتخاب میشود و کاربر باید ساعت مد نظر را وارد کند؛ در اینجا، عدد اول ساعت، مد نظر است.
برای نمونه در اینجا عدد 2 توسط کاربر وارد میشود؛ پس از ورود عدد و با توجه به تنظیمات انجام شده، ساعت به صورت اتوماتیک به حداکثر مقداری که میتواند بپذیرد تغییر میکند (در این مثال چون کاربر عدد 2 را وارد کرده و در تنظیمات انجام شده حداکثر ساعت دریافتی 22 و حداکثر دقیقه 40 تعریف شده است، ساعت به صورت پیشفرض به 22:40 تغییر مییابد)
و پس از وارد کردن عدد دوم ساعت توسط کاربر مکان نما به قسمت دقیقه منتقل میشود که در این جا عدد اول دقیقه مد نظر است
وارد کردن عدد 3 برای دقیقه
وارد کردن عدد دوم دقیقه
پس از وارد کردن کامل دقیقه مکان نما دوباره به قسمت ساعت باز میگردد.
در ادامه دوستان علاقمند لطفا جهت بهبود کیفیت کار، باگ و یا مشکلات کدنویسی را اطلاع دهند.
با تشکر
MongoDb در سی شارپ (بخش پنجم)
خلاصه اشتراکهای روز یک شنبه 6 آذر 1390
من که برای جالب بود بنظرم ارزش تست رو داره قبلا از تم wekeroad-ink استفاده میکردم، با کار کردن توی این تم ها ساعت کاری و بازدهی بالا میره