<Report> <DocumentPreferences RunDirection="RTL" PageOrientation="Portrait" PageSize="A4"> <Watermark RunDirection="" Text="پس زمینه نمونه" Font="bardia.ttf" FillOpacity="0.7" StrokeOpacity="0.5" /> </DocumentPreferences> <DefaultFonts PrimaryFont="irsans.ttf" SecondaryFont="farnaz.ttf" Size="12px" Color="#CCFFBB" /> <DefaultFooter Message="پا صفحه نمونه" /> <DefaultHeader Message="سرصفحه نمونه" MessageFontColor="#ََََAA0012" ImagePath="logo.png" /> <MainTableTemplate TemplateName="AppleOrchardTemplate" /> <MainTablePreferences ColumnsWidthsType="Relative" NumberOfDataRowsPerPage="0" > <GroupsPreferences GroupType="HideGroupingColumns" ShowOneGroupPerPage="true" /> </MainTablePreferences> <MainTableSummarySettings OverallSummarySettings="جمع کل" PreviousPageSummarySettings="نقل از صفحه قبل" PageSummarySettings="جمع کل صفحه" AllGroupsSummarySettings="جمع کل گروه ها" /> <MainTableDataSource ProviderName="System.Data.SQLُServerCE" ConnectionString="Data Source=MyData.sdf;Persist Security Info=False;" SqlStatement="SELECT [url], [name], [NumberOfPosts], [AddDate] FROM [tblBlogs] WHERE [NumberOfPosts]>=@p1” > <Parameters> <Param Type="int" Name="@p1"/> </Parameters> </MainTableDataSource> <MainTableColumns> <Columns> <Column CellHorizontalAlignment="Right" ColumnItemsTemplate="TextBlock" HeaderCell="عنوان ستون 1" PropertyName="Title" Order="0" Width="1" /> <Column CellHorizontalAlignment="Right" Group="true" ColumnItemsTemplate="TextBlock" HeaderCell="عنوان ستون 2" PropertyName="Category" Order="1" Width="2" /> <Column CellHorizontalAlignment="Right" ColumnItemsTemplate="Image" HeaderCell="عنوان ستون 3" PropertyName="Image" Order="2" Width="2" /> </Columns> </MainTableColumns> <MainTableEvents> <DataSourceIsEmpty Message="داده ای برای نمایش وجود ندارد" /> </MainTableEvents> <Export ToExcel="True" ToCsv="False" ToXml="True" /> <Generate Type="AsPdfFile" FileName="Report.pdf" /> </Report>
یکی از نکات امنیتی که استاندارد Owasp بررسی مینماید هدر X-Content-Type-Options است که جهت جلوگیری از حملات از طریق فایلهای نامرتبط میباشد. در این رخنه ممکن است فایلی که مرورگر دریافت میکند با آنچه که وب سایت ما آن را میشناسد متفاوت باشد. به عنوان مثال یک فایل اسکریپت که به عنوان یک فایل استایل معرفی میگردد ولی قابلیت اجرای کدهای آن در مرورگر امکان پذیر است؛ به این نوع حملات MIME Sniffing میگویند. در یکی از سایتهایی که در حال حاضر در حال توسعه آن هستیم بخشی از گزارشها با استفاده از ابزار FastReport ایجاد شده بود و با توجه به اینکه در این ابزار از فایلهای axd استفاده میگردید و مرورگر نوع دیتای برگشتی و MimeType معرفی شده را همخوان نمیدانست، از نمایش و بارگذاری آن ممانعت به عمل آورده و در نتیجه فایل گزارش دیده نمیشد. در این مقاله قصد داریم به معرفی این نوع حمله، روش جلوگیری از آن و همچنین رفع محدودیت پیش آمده را بررسی کرده تا در موارد مشابه از آن استفاده نماییم.
Mime Sniffing
زمانیکه مرورگر در هدر(پاسخ) Response، نوع محتوای ارسال شده، Content-Type را دریافت نکند، یا مرورگر متوجه مغایرتی در آن شود، این نوع رفتار را Mime Sniffing شناسایی میکند. نحوه این شناساییها در هر مرورگری میتواند متفاوت باشد؛ ولی عموما بر اساس type ارسالی و پسوند مورد نظر میباشد. در بعضی از موارد نیز خواندن بایتهای ابتدایی یک فایل نیز میتواند نشان دهد که محتوای ارسالی واقعا چیست. به عنوان مثال برای فایلهایی با پسوند Gif، الگوی بایتهای ابتدایی شامل 47 49 46 38 39 میباشد؛ ولی از آنجا که در همه فایلها، بایتهای ابتدایی الگوی یکسانی ندارند، پس نمیتوان به این روش نیز بسنده کرد.
روش اینکه به مروگر بگوییم جلوی این نوع حملات را بگیرد و در صورت شناسایی Sniffing از اجرای آن سر باز بزند، استفاده از هدر X-Content-Type-Options میباشد که نحوه افزودن آن در فایل web.config به شکل زیر است:
<httpProtocol> <customHeaders> ... <remove name="X-Content-Type-Options"/> <add name="X-Content-Type-Options" value="nosniff" /> .. </customHeaders> </httpProtocol>
در پروژه ما به دلیل اینکه بخشی از گزارشها با استفاده از FastReport طراحی شده بود این مورد برای ما ایجاد مشکل میکرد و در گزارش نمایش داده نمیشد و در کنسول پیامهایی به شکل زیر دریافت میکردیم:
Refused to execute script from 'https://localhost:44377/WebResource.axd?d=xxx' because its MIME type ('text/js') is not executable, and strict MIME type checking is enabled.
با نگاهی به Response دریافتی نیز میتوان بازگشت هدر امنیتی X-Content-Type-Options را نیز مشاهده نمود:
پس باید این هدر را برای بعضی از آدرسها که میتوانند دچار مشکلات اجرایی گردند حذف کرده و برای مابقی بخشها همچنان این هدر فعال باشد؛ پس با افزودن کد زیر به web.config، هدر مورد نظر را برای این نوع فایل حذف میکنیم:
<location path="WebResource.axd" > <system.webServer> <httpProtocol> <customHeaders> <remove name="X-Content-Type-Options" /> </customHeaders> </httpProtocol> </system.webServer> </location>
نتیجه آن را میتوانید در صفحه ذیل برای همان درخواست و پاسخ قبلی نیز مشاهده نمایید:
String.format = function () { var s = arguments[0]; for (var i = 0; i < arguments.length - 1; i++) { s = s.replace("{" + i + "}", arguments[i + 1]); } return s; };
String.format = function () { var s = arguments[0]; for (var arg in arguments) { var i = parseInt(arg); s = s.replace("{" + i + "}", arguments[i + 1]); } return s; };
console.log(String.format("{0} is nice!", "donettips.info"));
donettips.info is nice!
console.log(String.format("{0} is {1} nice! {0} is {1} nice!", "donettips.info", "very"));
donettips.info is very nice! {0} is {1} nice!
String.format = function () { var original = arguments[0], replaced; for (var i = 0; i < arguments.length - 1; i++) { replaced = ''; while (replaced != original) { original = replaced || original; replaced = original.replace("{" + i + "}", arguments[i + 1]); } } return replaced; };
donettips.info is very nice! donettips.info is very nice!
String.format = function () { var s = arguments[0]; for (var i = 0; i < arguments.length - 1; i++) { s = s.replace(new RegExp("\\{" + i + "\\}", "g"), arguments[i + 1]); } return s; };
String.format = function () { var s = arguments[0], i = arguments.length - 1; while (i--) { s = s.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i + 1]); } return s; };
console.log(String.format("{0}:0 {1}:1 {2}:2", "zero", "{2}", "two"));
zero:0 {2}:1 two:2
zero:0 two:1 two:2
console.log(String.format("{0}:0 {1}:1 {2}:2", "zero", "one", "{1}"));
zero:0 one:1 one:2
zero:0 one:1 {1}:2
String.format = function () { var args = arguments; return args[0].replace(/{(\d+)}/g, function (match, number) { return args[parseInt(number) + 1]; }); };
console.log(String.format("{0} is {1} nice!", "donettips.info"));
donettips.info is undefined nice!
String.format = function () { var s = arguments[0], args = arguments; return s.replace(/{(\d+)}/g, function (match, number) { var i = parseInt(number); return typeof args[i + 1] != 'undefined' ? args[i + 1] : match; }); };
console.log(String.format("{0}:0 {1}:1 {2}:2, {{0}} {{{1}}} {{{{2}}}} {2}", "zero", "{2}", "two"));
zero:0 {2}:1 two:2, {zero} {{{2}}} {{{two}}} two
String.format = function () { var s = arguments[0], args = arguments; return s.replace(/\{\{|\}\}|\{(\d+)\}/g, function (match, number) { if (match == "{{") { return "{"; } if (match == "}}") { return "}"; } var i = parseInt(number); return typeof args[i + 1] != 'undefined' ? args[i + 1] : match; }); };
zero:0 {2}:1 two:2, {0} {{2}} {{2}} two
String.prototype.format = function () { ... }
String.prototype.format = function () { var s = this.toString(), args = arguments; return s.replace(/\{\{|\}\}|\{(\d+)\}/g, function (match, number) { if (match == "{{") { return "{"; } if (match == "}}") { return "}"; } return typeof args[number] != 'undefined' ? args[number] : match; }); };
console.log("{0}:0 {1}:1 {2}:2, {{0}} {{{1}}} {{{{2}}}} {2}".format("zero", "{2}", "two"));
String.format = function () { var s = arguments[0], args = arguments[1]; for (var arg in args) { s = s.replace(new RegExp("{" + arg + "}", "g"), args[arg]); } return s; };
String.prototype.format = function () { var s = this.toString(), args = arguments[0]; for (var arg in args) { s = s.replace(new RegExp("{" + arg + "}", "g"), args[arg]); } return s; };
console.log(String.format("{site} is {adj}! {site} is {adj}!", { site: "donettips.info", adj: "nice" })); console.log("{site} is {adj}! {site} is {adj}!".format({ site: "donettips.info", adj: "nice" }));
String.format = function String$format(format, args) { /// <summary locid="M:J#String.format" /> /// <param name="format" type="String"></param> /// <param name="args" parameterArray="true" mayBeNull="true"></param> /// <returns type="String"></returns> // var e = Function._validateParams(arguments, [ // { name: "format", type: String }, // { name: "args", mayBeNull: true, parameterArray: true } // ]); // if (e) throw e; return String._toFormattedString(false, arguments); }; String._toFormattedString = function String$_toFormattedString(useLocale, args) { var result = ''; var format = args[0]; for (var i = 0; ; ) { var open = format.indexOf('{', i); var close = format.indexOf('}', i); if ((open < 0) && (close < 0)) { result += format.slice(i); break; } if ((close > 0) && ((close < open) || (open < 0))) { if (format.charAt(close + 1) !== '}') { throw Error.argument('format', Sys.Res.stringFormatBraceMismatch); } result += format.slice(i, close + 1); i = close + 2; continue; } result += format.slice(i, open); i = open + 1; if (format.charAt(i) === '{') { result += '{'; i++; continue; } if (close < 0) throw Error.argument('format', Sys.Res.stringFormatBraceMismatch); var brace = format.substring(i, close); var colonIndex = brace.indexOf(':'); var argNumber = parseInt((colonIndex < 0) ? brace : brace.substring(0, colonIndex), 10) + 1; if (isNaN(argNumber)) throw Error.argument('format', Sys.Res.stringFormatInvalid); var argFormat = (colonIndex < 0) ? '' : brace.substring(colonIndex + 1); var arg = args[argNumber]; if (typeof (arg) === "undefined" || arg === null) { arg = ''; } if (arg.toFormattedString) { result += arg.toFormattedString(argFormat); } else if (useLocale && arg.localeFormat) { result += arg.localeFormat(argFormat); } else if (arg.format) { result += arg.format(argFormat); } else result += arg.toString(); i = close + 1; } return result; }
console.log(String.format("{0:n}, {0:c}, {0:p}, {0:d}", 100.0001)); // result: 100.00, ¤100.00, 10,000.01 %, 100.0001 console.log(String.format("{0:d}, {0:t}", new Date(2015, 1, 1, 10, 45))); // result: 02/01/2015, 10:45
var template = jQuery.validator.format("{0} is not a valid value"); console.log(template("abc")); // result: 'abc is not a valid value'
String.format([full format string], [arguments...]); // or: [date|number].format([partial format string]);
// Object path String.format("Welcome back, {username}!", { id: 3, username: "JohnDoe" }); // Result: "Welcome back, JohnDoe!" // Date/time formatting String.format("The time is now {0:t}.", new Date(2009, 5, 1, 13, 22)); // Result: "The time is now 01:22 PM." // Date/time formatting (without using a full format string) var d = new Date(); d.format("hh:mm:ss tt"); // Result: "02:28:06 PM" // Custom number format string String.format("Please call me at {0:+##0 (0) 000-00 00}.", 4601111111); // Result: "Please call me at +46 (0) 111-11 11." // Another custom number format string String.format("The last year result was {0:+$#,0.00;-$#,0.00;0}.", -5543.346); // Result: "The last year result was -$5,543.35." // Alignment String.format("|{0,10:PI=0.00}|", Math.PI); // Result: "| PI=3.14|" // Rounding String.format("1/3 ~ {0:0.00}", 1/3); // Result: "1/3 ~ 0.33" // Boolean values String.format("{0:true;;false}", 0); // Result: "false" // Explicitly specified localization // (note that you have to include the .js file for used cultures) msf.setCulture("en-US"); String.format("{0:#,0.0}", 3641.667); // Result: "3,641.7" msf.setCulture("sv-SE"); String.format("{0:#,0.0}", 3641.667); // Result: "3 641,7"
//inline arguments String.format("some string with {0} and {1} injected using argument {{number}}", 'first value', 'second value'); //returns: 'some string with first value and second value injected argument {number}' //single array String.format("some string with {0} and {1} injected using array {{number}}", [ 'first value', 'second value' ]); //returns: 'some string with first value and second value injected using array {number}' //single object String.format("some string with {first} and {second} value injected using {{propertyName}}",{first:'first value',second:'second value'}); //returns: 'some string with first value and second value injected using {propertyName}'
کار با Kendo UI DataSource
[DisplayName("تاریخ درج")] public DateTime CreateDate { get; set; } [DisplayName("تاریخ انتشار")] public DateTime PublishDate { get; set; }
[DisplayName("نوع مطلب")] public string BlogType { get; set; }
<script type="text/javascript"> $(function () { var blogDataSource = new kendo.data.DataSource({ transport: { read: { url: "@Url.Action("GetLastBlogs", "Admin")", dataType: "json", contentType: 'application/json; charset=utf-8', type: 'GET' }, parameterMap: function (options) { return kendo.stringify(options); } }, schema: { data: "data", total: "total", model: { fields: { "id": { type: "number" }, //تعیین نوع فیلد برای جستجوی پویا مهم است "title": { type: "string" }, "content": { type: "string" }, "imagepath": { type: "string" }, "attachmentid": { type: "number" }, "createdate": { type: "string" }, "publishdate": { type: "date" }, "blogtype": { type: "string" }, "lastmodified": { type: "date" }, "visitcount": { type: "number" }, "isarchived": { type: "boolean" }, "ispublished": { type: "boolean" }, "isdeleted": { type: "boolean" }, "tags": { type: "string" } } } }, error: function (e) { alert(e.errorThrown); }, pageSize: 10, sort: { field: "id", dir: "desc" }, serverPaging: true, serverFiltering: true, serverSorting: true }); $("#report-grid").kendoGrid({ dataSource: blogDataSource, autoBind: true, scrollable: false, pageable: true, sortable: true, filterable: true, reorderable: true, columnMenu: true, columns: [ { field: "id", title: "شماره", width: "130px" }, { field: "title", title: "عنوان مطلب" }, { field: "createdate", title: "تاریخ درج" }, { field: "publishdate", title: "تاریخ انتشار" }, { field: "content", title: "خلاصه مطلب" }, { field: "blogtype", title: "نوع" }, { field: "lastmodified", title: "آخرین ویرایش" }, { field: "visitcount", title: "تعداد بازدید" }, { field: "isarchived", title: "آرشیو شده", template: '<input type="checkbox" #= isarchived ? checked="checked" : "" # disabled="disabled" ></input>' }, { field: "ispublished", title: "منتشر شده", template: '<input type="checkbox" #= ispublished ? checked="checked" : "" # disabled="disabled" ></input>' } ] }); }); </script>
Serialization #2
مطابق آنچه در قسمت قبل گفته شد برای آنکه بتوان از مدل News برای سریالیکردن استفاده کرد، باید آن را به شکل ذیل پیادهسازی کرد:
[DataContract] public class News { [DataMember] public int Id; [DataMember] public string Body; [DataMember] public DateTime NewsDate; }
با Override کردن [DataContract]به صورت [("DataContract(Name=”MyCustomNews] میتوان نام ریشه XML فایل را به MyCustomNews تغییر داد. همچنین با Override کردن [DataMember] بصورت [("DataMember(Name=”MyCustomFieldName] میشود به هر فیلدی عنوان دلخواهی داد و همچنین با تعیین عبارت NameSpace به صورت [("DataContract(Name = "MyCustomNews", Namespace = "http://www.my.com] میشود فضای نام را تغییر داد که با این تغییرات، خروجی زیر حاصل میشود:
<?xml version="1.0" encoding="utf-8"?> <MyCustomNews xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.my.com"> <Body>NewsBody</Body> <MyCustomFieldName>111</MyCustomFieldName> <NewsDate>2012-10-04T00:00:00</NewsDate> </MyCustomNews>
ویژگی [DataMember] هم ازفیلدها و هم از propertyها، پشتیبانی میکند، خواه عمومی باشند یا خصوصی و نوع فیلد یا Property میتواند به یکی از اشکال زیر باشد:
- انواع اولیه .
- انواع DateTime ،TimeSpan، Guid ،Uri و انواع Enum
- انواع پوچ پذیر هر کدام از موارد بالا
- نوع byte[]
- انواع تعریف شده توسط کاربر که توسط صفت [DataContract] محصور شدهاند.
- هر نوع IEnumerable
- هر نوعی که با صفت [Serializable] محصور شود و یا اینترفیس ISerializable را پیاده سازی کند.
- هر نوعی که اینترفیس IXmlSerializble را پیاده سازی نماید.
تعیین فرمت باینری برای سریالیکردن:
برای سریالی کنندههای DataContractSerializer و NetDataContractSerializer میتوان به روش زیر فرمت خروجی را به شکل فرمت باینری درآورد که خروجی آن تاحد زیادی کوچکتر و کم حجمتر میشود:
var s = new MemoryStream(); using (XmlDictionaryWriter w=XmlDictionaryWriter.CreateBinaryWriter(s)) { ds.WriteObject(w,news); }
و برای Deserialize کردن آن به شیوه زیر عمل میکنیم:
var s2 = new MemoryStream(s.ToArray()); News deserializednews; using (XmlDictionaryReader r=XmlDictionaryReader.CreateBinaryReader(s2,XmlDictionaryReaderQuotas.Max)) { deserializednews = (News)ds.ReadObject(r); }
که در آن از ویژگی Max کلاس XmlDictionaryReaderQuotas برای به دست آوردن حداکثر سهمیه فضای دیسک مربوط به XmlDictionaryReaders استفاده میشود.
و مشکل منوها حل شد.فقط فایلهای validation و ajax رو همچنان ندارم
@Html.ActionLink("text", "Index", "Home")
پارامترهای دوم و سوم آن که به نامهای یک اکشن متد و کنترلر آن اشاره میکنند، توسط رشتهها تعریف شدهاند. مشکلاتی هم که با رشتهها در حالت کلی وجود دارند به شرح زیر است:
الف) میتوان نام کنترلر یا نام متد را در برنامه تغییر داد. به این ترتیب تمام ActionLink هایی که در برنامه به این کنترلر اشاره میکردند از کار میافتند (تکرار رشتهها به علاوه refactoring friendly نبودن آنها).
ب) برای نوشتن رشتهها intellisense کارآیی ندارد.
ج) امکان بروز اشتباهات تایپی در این بین بسیار زیاد است.
راه حل متداولی که برای حل این نوع مشکلات وجود دارد، تعریف یک کلاس عمومی و معرفی رشتهها به صورت فیلدهایی ثابت در آنها میباشند و سپس استفاده از این فیلدها بجای استفاده مستقیم از رشتهها.
و ... چقدر خوب میشد اگر ابزاری وجود میداشت که کلاسهای کنترلرهای ما را آنالیز میکرد و خودش این ثوابت رشتهای را از آنها استخراج و کلاسهای عمومی یاد شده را تشکیل میداد!
خوشبختانه نیازی به اختراع مجدد چرخ نیست و اینکار توسط پروژهی سورس بازی به نام T4MVC انجام شده است. برای دریافت آن به سایت زیر مراجعه نمائید:
این پروژه توسط David Ebbo از اعضای تیم ASP.NET MVC تهیه شده است.
پس از دریافت پروژه، تنها به دو فایل زیر از آن نیاز داریم:
T4MVC.tt و T4MVC.tt.settings.t4
دو فایل فوق را به درون پوشهای از پروژه جاری MVC خود کپی کنید (مثلا یک پوشه T4MVC را ایجاد و این دو فایل را به آن اضافه کنید). بلافاصله این فایلهای t4 وارد عمل شده و کلاسهای کنترلرها، Viewها، تصاویر و غیره را آنالیز و ... ثوابت رشتهای معادل آنها را تولید میکنند.
اکنون برای استفاده از این کلاسهای تولید شده میتوان به صورت زیر عمل کرد:
اصلاح ActionLinkها و حذف رشتههای موجود در آنها
اینبار بجای اینکه بنویسیم:
@Html.ActionLink("text", "Index", "Home")
@Html.ActionLink("text", result: MVC.Home.Index())
@Html.ActionLink("text", MVC.Home.ActionNames.Index, MVC.Home.Name)
برای دسترسی به امکانات آن با نام کلاس MVC شروع میکنیم و سپس برای مثال به نام کنترلر Home رسیده و توسط ActionNames آن به تمام اکشن متدهای موجود در آن میتوان دسترسی داشت.
البته این پروژه بسیار فراتر از تولید فیلدهای strongly typed معادل رشتهها است. همانطور که ملاحظه میکنید، یک سری overload را هم به متدهای پیش فرض ASP.NET MVC اضافه کرده است و حتی بجای معرفی رشته معادل اکشن متد Index، خود این اکشن متد را میتوان معرفی کرد (آرگومانی از نوع ActionResult را هم اضافه کرده است). نمونه دیگر آن به نحو زیر میتواند باشد:
@Url.Action(result: MVC.Article.Delete())
@Url.Action("Delete", "Article")
امکان معرفی بهتر نام Partial Viewها
برای مثال اگر پیشتر یک Partial View را به این شکل تعریف میکردید:
@{ Html.RenderPartial("_ViewPage1"); }
@{ Html.RenderPartial(MVC.Home.Views._ViewPage1); }
return PartialView(Views._ViewPage1);
و یا بجای:
return RedirectToAction(actionName: "Index", controllerName: "Menu");
return RedirectToAction(actionName: MVC.Menu.ActionNames.Index, controllerName: MVC.Menu.Name);
return RedirectToAction(result: MVC.Menu.Index());
و یا بجای مسیر دهی به شکل زیر:
return PartialView("~/Views/CommentsArchive/_LatestCommentsInfo.cshtml", data);
return PartialView(MVC.CommentsArchive.Views._LatestCommentsInfo, data);
امکان معرفی بهتر عناصر استاتیک سایت
این مورد نیز بسیار جالب توجه است. توسط کلاس Links آن میتوان به محتویات استاتیک (تصاویر، فایلهای css و غیره) پوشههای Content و Scripts هم دسترسی یافت و حتی این موارد را نیز refactor کرد:
<img src="@Links.Content.Images.arrow_right_png" alt="arrow" /> <script src="@Links.Scripts.jquery_1_5_1_min_js" type="text/javascript"></script>
امکان تعریف بهتر پارامترها و مقادیر route
بجای اینکه routeValues را همانند سابق با anonymously typed objects مقدار دهی کنیم:
Html.ActionLink(linkText: "عنوان", actionName: "Index", controllerName: "Comments", routeValues: new { userName = @Model.FriendlyName }, htmlAttributes: null))
Html.ActionLink(linkText: "عنوان", result: MVC.Comments.Index(userName: @Model.FriendlyName) htmlAttributes: null))
چند نکته جانبی
-این ابزار بر اساس Reflection کار میکند (البته فقط در حین تشکیل خودکار کلاسهای مورد نیاز؛ وگرنه ثوابتی را که ایجاد میکند کامپایل شده و در زمان اجرا سرباری را به برنامه اضافه نمیکنند). بنابراین اگر کلاسی به پروژه اضافه شده است، کامپایل کردن آنرا فراموش نکنید.
-اگر تغییری در فایلهای View، در تعداد و نام آنها صورت گرفت، روی فایل T4MVC.tt کلیک راست کرده و گزینهی اجرای آنرا انتخاب کنید. پس از اینکار، مجددا کامپایل پروژه را فراموش نکنید.
-در فایل T4MVC.tt.settings.t4 یک سری تنظیمات پیش فرض قرار دارند. برای مثال اگر علاقمندید که به این فایلهای تولید شده خودکار، فضای نام سفارشی خاصی را اضافه کنید میشود آرایه ReferencedNamespaces آنرا مقدار دهی کرد.
- overloadهای جدید ActionResult دار آن نسبت به نمونههای استاندارد موجود، بسیار منطقیتر به نظر میرسند.
- توضیحات کامل امکانات T4MVC را در مستندات رسمی آن میتوانید مطالعه کنید.
و ... اگر یک مدت با آن کار کنید خواهید گفت: «من قبلا چطور با ASP.NET MVC کار میکردم؟!»
آشنایی و بررسی ابزار Version Manager
من این ماژول را نصب کردم اما زمان فعال سازی خطا صادر میشود، فایل ActivityLog را بررسی کردم خطای ذیل ثبت شده بود.
<entry> <record>5390</record> <time>2013/09/09 08:41:39.525</time> <type>Error</type> <source>VisualStudio</source> <description>End package load [VersionManagerPackage]</description> <guid>{775E4DAB-A8DC-46E5-A64B-4072C0DD3A42}</guid> <hr>80004005 - E_FAIL</hr> <errorinfo>Could not load file or assembly 'Microsoft.VisualStudio.Shell.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.</errorinfo> </entry>
پلاگین DataTables کتابخانه jQuery - قسمت سوم
زمانیکه من با Ajax , Jquery سطرهای دیتای جدول مورد نظر برای DataTable شدن را از سمت سرور ایجاد میکنم متاسفانه بار اول دیتاها رو نشون میده ولی Search نمیکنه و صفحه بندی هم نمیکنه و ... در ضمن کدهای مربوطه رو هم میگذارم . لطفا راهنمایی کنید که اگه خواستیم دیتاها را از سمت سرور بیاریم و کار بده باید چه کار کرد؟ مرسی
$(document).ready(function () { dataparam2 = "cmd=FillScope"; $.ajax({ url: "Default2.aspx", type: "POST", data: dataparam2, async: true, success: function (msg) { if (msg != '') { var data = eval("(" + msg + ")"); $("#tbodytblMain").html(''); for (var i = 0; i < data.length; i++) { $("#tbodytblMain").append( "<tr class='odd gradeX'>" + "<td style='width:200px'>" + data[i].T + "</td>" + "<td style='width:150px'>" + data[i].P + "</td>" + "<td>" + data[i].S + "</td>" + "<td>" + data[i].TP + "</td>" + "<td>" + data[i].Sp + "</td>" + "</tr>"); } } }, error: function (msg) { } }); $('#tblMain').dataTable(); });
if (Request["cmd"] == "FillScope") { string Val = "برخوار"; JavaScriptSerializer js = new JavaScriptSerializer(); string serText = ""; MUIDataClassesDataContext db = new MUIDataClassesDataContext(); var LST = (from x in db.tblProjectInfos where x.tblScope.xScopeName.Contains(Val) orderby x.tblScope.xScopeName select new { P = x.xPlace, S = x.tblScope.xScopeName, TP = x.tblProjectType.xProjectTypeName, Sp = x.tblStatus.xStatusName }); serText = js.Serialize(LST); Response.Write(serText); Response.End(); }