.NET 8 Preview 7 is now available. We’re already at the final preview release for .NET 8 and will now shift to release candidates. Almost all new features for the release are in their final shape. System.Text.Json and codegen have the biggest changes in this build. You’ll want to take a look at those. Now is great time to pick up and test .NET 8 if you haven’t yet.
Visual Studio 2022 17.2 منتشر شد
This release brings continued improvements to the C# and .NET experiences, new Git performance and experiences, updates for C++ developers, and new Azure tools for local development and deployment. We also continue to address your direct feedback submitted via Developer Community, addressing over 400 feedback items in this release! You can see the broader list of community feedback addressed in releases by visiting the fixes page on Developer Community.
Response.AddHeader("content-disposition", "inline;filename=sample.pdf");
ASP.NET Core 2.0 Preview 2 منتشر شد
ایجاد فرمهای مقدماتی، با بوت استرپ 4
بوت استرپ به همراه کلاسهایی مانند form-group و form-control است که از آنها میتوان برای ایجاد یک فرم مقدماتی استفاده کرد. در ابتدا مثال غیر تزئین شدهی زیر را در نظر بگیرید:
<body> <div class="container"> <h2>Medical Questionnaire</h2> <form> <fieldset> <legend>Owner Info</legend> <div> <label for="ownername">Owner name</label> <input type="text" id="ownername" placeholder="Your Name"> </div> <div> <label for="owneremail">Email address</label> <input type="email" id="owneremail" aria-describedby="emailHelp" placeholder="Enter email"> <small id="emailHelp">We'll never share your email</small> </div> </fieldset> <fieldset> <legend>Pet Info</legend> <div> <label for="petname">Pet name</label> <input type="text" id="petname" placeholder="Your Pet's name"> </div> <div> <label for="pettype">Pet type</label> <select id="pettype"> <option>Choose</option> <option value="cat">Dog</option> <option value="cat">Cat</option> <option value="bird">Other</option> </select> </div> <div> <label for="reasonforvisit">Reason for today's visit</label> <textarea id="reasonforvisit" rows="3"></textarea> </div> <div> <label>Has your pet been spayed or neutered?</label> <label><input type="radio" name="spayneut" value="yes" checked> Yes</label> <label><input type="radio" name="spayneut" value="no"> No</label> </div> <div> <label>Has the pet had any of the following in the past 30 days</label> <label><input type="checkbox"> Abdominal pain</label> <label><input type="checkbox"> Lack of appetite</label> <label><input type="checkbox"> Weakness</label> </div> </fieldset> <button type="submit">Submit</button> </form> </div><!-- content container --> </body>
در ادامه شروع میکنیم به تزئین کردن این فرم، با کلاسهای بوت استرپ 4:
- ابتدا به fieldsetهای تعریف شده، کلاس form-goup را انتساب میدهیم. این مورد سبب میشود تا اندکی فاصله بین آنها ایجاد شود.
- سپس به تمام divهایی که المانها را در بر گرفتهاند نیز کلاس form-group را اعمال میکنیم.
با اینکار فاصلهی مناسبی بین المانهای تعریف شدهی در صفحه ایجاد میشود:
- در ادامه به تمام المانهای input، select و textarea (منهای checkboxها) کلاس form-control را نسبت میدهیم:
با اینکار، ظاهر این المانها بسیار شکیلتر شدهاست و همچنین این فرم واکنشگرا نیز میباشد.
- پس از آن، تمام المانهای label را انتخاب کرده و کلاس form-control-label را به آنها انتساب میدهیم. هرچند با اینکار ظاهر فعلی فرم تغییری نمیکند، اما چنین تعریفی برای فعالسازی کلاسهای اعتبارسنجی ضروری است.
اگر به هر دلیلی نخواستید این برچسبها را نمایش دهید، میتوانید از کلاس sr-only استفاده کنید که صرفا سبب نمایش آنها به screen readers میشود.
- ذیل فیلد ورود ایمیل، متنی وجود دارد. این متن را با کلاسهای form-text text-muted مزین میکنیم:
- به دکمهی پایین صفحه نیز کلاسهای btn btn-primary را اضافه میکنیم که در مطلب «بررسی شیوهنامههای المانهای پر کاربرد بوت استرپ 4» بیشتر به آنها پرداختیم.
مزین سازی checkboxها و radio-buttonها در بوت استرپ 4
روش مزین سازی checkboxها و radio-buttonها در بوت استرپ، با سایر المانها اندکی متفاوت است:
<div class="form-check"> <label class="form-check-label"> <input class="form-check-input" type="checkbox"> Lack of appetite </label> </div>
یک نکته: اگر نیاز است این المانها کنار یکدیگر نمایش داده شوند، میتوان بر روی div آنها از کلاسهای form-check form-check-inline استفاده کرد. در این حالت اگر میخواهید برچسب برای مثال radio-button تعریف شده، در یک سطر و گزینهها آن در سطری دیگر باشند، از کلاس d-block بر روی این برچسب استفاده کنید:
<div class="form-group"> <label class="d-block">Has your pet been spayed or neutered?</label> <div class="form-check form-check-inline"> <label class="form-check-label"> <input class="form-check-input" type="radio" name="spayneut" value="yes" checked> Yes </label> </div> <div class="form-check form-check-inline"> <label class="form-check-label"> <input class="form-check-input" type="radio" name="spayneut" value="no"> No </label> </div> </div>
کلاسهای کنترل اندازه و اعتبارسنجی المانهای فرمهای بوت استرپ 4
- با استفاده از کلاس form-control-sm میتوان اندازهی فیلدهای input را با ارتفاع کوچکتری نمایش داد و یا توسط کلاس form-control-lg میتوان آنها را بزرگتر کرد.
- کلاس form-inline سبب میشود تا یک form-group به صورت inline نمایش داده شود. یعنی برچسب و کنترلهای درون آن، در طی یک سطر نمایش داده خواهند شد. در این حالت، نیاز به کلاسهای Margin مانند mx-sm-2 خواهد بود تا فاصلهی بین کنترلها را بتوان کنترل کرد.
- برای نمایش نتایج اعتبارسنجی کنترلها:
- اگر کل فرم اعتبارسنجی شدهاست، کلاس was-validated را به المان form اضافه کنید.
- اگر اعتبارسنجی کنترلی با موفقیت روبرو شود، کلاس is-valid و اگر خیر کلاس is-invalid را به آن نسبت دهید.
- اگر میخواهید پیام خاصی را پس از موفقیت اعتبارسنجی نمایش دهید، آنرا درون یک div با کلاس valid-feedback قرار دهید و یا برعکس از کلاس invalid-feedback استفاده کنید.
- برای تغییر رنگ برچسب المانها نیز از کلاسهای text-color همانند قبل استفاده کنید؛ مانند text-success.
یک مثال:
<div class="form-group"> <label for="owneremail" class="text-success">Email address</label> <input class="form-control is-valid" type="email" id="owneremail" aria-describedby="emailHelp" placeholder="Enter email"> <small class="form-text text-muted" id="emailHelp">We'll never share your email</small> <div class="valid-feedback"> Looks good! </div> </div>
تغییر نحوهی چیدمان عناصر فرمها در بوت استرپ 4
فرم زیر را در نظر بگیرید:
قصد داریم با استفاده از کلاسهای ویژهی بوت استرپ 4، آنرا دو ستونی کنیم؛ به طوریکه برچسبها در یک ستون و فیلدهای ورودی، در ستونی دیگر نمایش داده شوند. همچنین این فرم واکنشگرا نیز باشد؛ به این معنا که این دو ستونی شدن، فقط در اندازههای پس از md رخ دهد:
<body> <div class="container"> <h2>Medical Questionnaire</h2> <form> <fieldset class="form-group"> <legend>Owner Info</legend> <div class="form-group row"> <label class="form-control-label col-md-2 col-form-label text-md-right" for="ownername">Owner</label> <div class="col-md-10"> <input class="form-control" type="text" id="ownername" placeholder="Your Name"> </div> </div> <div class="form-group row"> <label class="form-control-label col-md-2 col-form-label text-md-right" for="owneremail">Address</label> <div class="col-md-10"> <input class="form-control" type="text" id="owneremail" placeholder="Address"> </div> </div> <div class="form-group row"> <div class="form-group col-6 offset-md-2"> <label class="form-control-label sr-only" for="ownercity">City</label> <input class="form-control" type="text" id="ownercity" placeholder="City"> </div> <div class="form-group col-md-4 col-6"> <label class="form-control-label sr-only" for="ownerzip">Zip</label> <input class="form-control" type="text" id="ownerzip" placeholder="Zip"> </div> </div> <div class="form-group row"> <div class="offset-md-2 col-md-10"> <button class="btn btn-primary" type="submit">Submit</button> </div> </div> </fieldset> </form> </div> </body>
توضیحات:
برای ستونی کردن فرمها، ابتدا کلاس row، به form-group قرار گرفتهی داخل container اصلی اضافه میشود:
<div class="form-group row"> <label class="form-control-label col-md-2 col-form-label text-md-right" for="ownername">Owner</label> <div class="col-md-10"> <input class="form-control" type="text" id="ownername" placeholder="Your Name"> </div> </div>
پس از آن نوبت به تعریف ستون فیلد تعریف شدهاست که با ایجاد یک div و تعریف تعداد واحدی را که به خود اختصاص میدهد (col-md-10)، انجام میشود.
در اینجا برچسبهای فیلدهای city و zip با کلاس sr-only مشخص شدهاند. به همین جهت فقط به screen readers نمایش داده میشوند.
<div class="form-group row"> <div class="form-group col-6 offset-md-2"> <label class="form-control-label sr-only" for="ownercity">City</label> <input class="form-control" type="text" id="ownercity"placeholder="City"> </div>
<div class="form-group row"> <div class="offset-md-2 col-md-10"> <button class="btn btn-primary" type="submit">Submit</button> </div> </div>
ایجاد گروهی از ورودیها در بوت استرپ 4
برای افزودن آیکنهایی به فیلدهای ورودی، از روش ایجاد گروهی از ورودیها در بوت استرپ 4 استفاده میشود:
<div class="form-group"> <label class="form-control-label" for="donationamt"> Donation Amount </label> <div class="input-group"> <div class="input-group-prepend"> <span class="input-group-text">$</span> </div> <input type="text" class="form-control" id="donationamt" placeholder="Amount"> <div class="input-group-append"> <span class="input-group-text">.00</span> </div> </div> </div>
در بوت استرپ 4، کلاسهای input-group-addon و input-group-btn بوت استرپ 3 حذف و با کلاسهای input-group-prepend و input-group-append جایگزین شدهاند. از prepend برای قرار دادن آیکنی پیش از فیلد ورودی و از append همانند مثال فوق، برای قرار دادن آیکنی اختیاری پس از فیلد ورودی استفاده میشود.
نمونهی متداول دیگر آن، نحوهی تعریف ویژهی فیلد جستجوی سایت، در منوی راهبری آن است:
<nav class="navbar bg-dark navbar-dark navbar-expand-sm"> <div class="container"> <div class="navbar-brand d-none d-sm-inline-block"> Wisdom Pet Medicine </div> <div class="navbar-nav mr-auto"> <a class="nav-item nav-link active" href="#">Home</a> <a class="nav-item nav-link" href="#">Mission</a> <a class="nav-item nav-link" href="#">Services</a> <a class="nav-item nav-link" href="#">Staff</a> <a class="nav-item nav-link" href="#">Testimonials</a> </div> <form class="form-inline d-none d-md-inline-block"> <div class="input-group"> <label for="search" class="form-control-label sr-only"></label> <input type="text" id="search" class="form-control" placeholder="Search ..."> <div class="input-group-append"> <button class="btn btn-outline-light" type="submit">Go</button> </div> </div> </form> </div> </nav>
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: Bootstrap4_10.zip
We are extremely excited to announce the general availability of EF Core 3.0and EF 6.3 on nuget.org .
What’s new in EF Core 3.0
Including major features, minor enhancements, and bug fixes, EF Core 3.0 contains more than 600 product improvements. Here are some of the most important ones:
- LINQ overhaul
- Restricted client evaluation
- Single SQL statement per LINQ query
- Cosmos DB support
- C# 8.0 support (Asynchronous streams, Nullable reference types)
- Interception of database operations
- Reverse engineering of database views
- Dependent entities sharing a table with principal are now optional
Change this section ... <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel> </PropertyGroup> to the following ... <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel> <AspNetCoreModuleName>AspNetCoreModule</AspNetCoreModuleName> </PropertyGroup>
برای این منظور MVC به کنترلهایی که باید اعتبارسنجی شوند، خصوصیاتی را از طریق Data Attribute اضافه میکند. برای مثال اگر در مدل خود فیلد ایمیل را به شکل زیر امضاء کرده باشید:
[Display(Name = "رایانامه")] [Required(AllowEmptyStrings = false, ErrorMessage = "رایانامه خود را وارد کنید.")] [RegularExpression("\\w+([-+.']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*", ErrorMessage = "نشانی رایانامه پذیرفتنی نمیباشد.")] [ExistField(Action = "EmailExist", Namespace = "Parsnet.Controllers", Controller = "Account", ErrorMessage = "این رایانامه پیشتر به کار گرفته شده است.")] public string Email { get; set; }
@Html.TextBoxFor(m => m.Email, new { @class = "form-control en", placeholder = @Html.DisplayNameFor(m => m.Email) })
<input data-val="true" data-val-existfiledvalidator="این رایانامه پیشتر به کار گرفته شده است." data-val-existfiledvalidator-url="/account/emailexist" data-val-regex="نشانی رایانامه پذیرفتنی نمیباشد." data-val-regex-pattern="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" data-val-required="رایانامه خود را وارد کنید." id="Email" name="Email" placeholder="رایانامه" value="" type="text">
در یکی از پروژههایی که در حال کار کردن بر روی آن هستم لازم شد تا این اطلاعات اعتبارسنجی به یک تگ span اعمال شوند. سناریوی مورد نظر به این صورت است که در بخش پروفایل کاربر، کاربر میتواند اطلاعات خود را بصورت inline ویرایش کنید. برای اینکار از کتابخانه X-editable استفاده کردم که از این لینک قابل دریافت است.
ابتدا اطلاعات موردنیاز در یک تگ span نمایش داده میشوند و در ادامه کاربر پس از کلیک بر روی آیکن ویرایش، امکان تغییر آن فیلد را دارد. برای اعتبارسنجی دادهها لازم بود تا تمامی اطلاعات مورد نیاز اعتبارسنجی در سمت کلاینت را به شکلی در اختیار داشته باشم و به ذهنم رسید تا با ایجاد یک Helper سفارشی، خصوصیات موردنظر را به تگ span اعمال کنم و سپس در سمت کلاینت از آن استفاده کنم. در واقع با اینکار با استفاده از همان کلاس مدل و این Helper سفارشی، از وارد کردن دستی دادهها و خصوصیات اجتناب کنم. (تصور کنید چیزی حدود 30 فیلد که هرکدام حداقل 4 خصوصیت دارند)
با نگاهی به سورس MVC دیدم پیاده سازی این قابلیت چندان سخت نیست و به راحتی با ایجاد یک Helper سفارشی، منطق خود را پیاده سازی و اعتبارسنجی در سمت کلاینت را به راحتی اعمال کردم.
برای ایجاد این Helper سفارشی ابتدا یک کلاس استاتیک ایجاد کنید و با استفاده از extension Methodها یک helper جدید را ایجاد کنید:
namespace Parsnet { public static MvcHtmlString SpanFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) { var sb = new StringBuilder(); var span = new TagBuilder("span"); var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData); var name = ExpressionHelper.GetExpressionText(expression); var fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); var value = ""; if (metadata.Model != null && metadata.Model.GetType() == typeof(List<IdentityProvider.IdentityRole>)) { var modelList = (List<IdentityProvider.IdentityRole>)metadata.Model; value = String.Join("، ", modelList.Select(r => r.Name)); } else { value = htmlHelper.FormatValue(metadata.Model, null); } span.MergeAttributes<string, object>(((IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes))); var fieldName = fullName.Split('.')[1]; span.MergeAttribute("data-name", fieldName, true); span.MergeAttributes<string, object>(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata)); sb.Append(span.ToString(TagRenderMode.StartTag)); sb.Append(value); sb.Append(span.ToString(TagRenderMode.EndTag)); return new MvcHtmlString(sb.ToString()); } } }
با استفاده از کلاس TagBuilder تگ مورد نظر خود را ایجاد میکنیم. در اینجا من تگ span را ایجاد کردهام که شما میتوانید هر تگ دلخواه دیگری را نیز ایجاد کنید. اولین مرحله، استخراج اطلاعات موردنیاز از metadata مدل است که در خط زیر با پردازش عبارت لامبدا اینکار صورت میگیرد:
var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
var name = ExpressionHelper.GetExpressionText(expression); var fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
در سناریوی من کاربر میتواند زمینهی فعالیت خود را انتخاب کند که به صورت IdentityRole پیاده سازی شده است. من در اینجا چک میکنیم که اگر نوع دادهای این فیلد List<IdentityProvider.IdentityRole> بود زمینه فعالیت کاربر را از طریق "،" از هم جدا کرده و به صورت یک رشته تبدیل میکنم. در غیر اینصورت همان مقدار عادی فیلد را بکار میگیرم.
if (metadata.Model != null && metadata.Model.GetType() == typeof(List<IdentityProvider.IdentityRole>)) { var modelList = (List<IdentityProvider.IdentityRole>)metadata.Model; value = String.Join("، ", modelList.Select(r => r.Name)); } else { value = htmlHelper.FormatValue(metadata.Model, null); }
span.MergeAttributes<string, object>(((IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)));
span.MergeAttributes<string, object>(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));
@Html.SpanFor(m => m.Profile.Email, new { @class = "editor", data_type = "text" })
<span class="editor" data-name="Email" data-type="text" data-val="true" data-val-existfiledvalidator="این رایانامه پیشتر به کار گرفته شده است." data-val-existfiledvalidator-url="/account/emailexist" data-val-regex="نشانی رایانامه پذیرفتنی نمیباشد." data-val-regex-pattern="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" data-val-required="رایانامه خود را وارد کنید.">alireza_s_84@yahoo.com</span>
و پس از ویرایش:
البته برای درک بهتر این موضوع سعی خواهم کرد تا با یک مثال عملی کامل، نحوهی پیاده سازی را در همینجا قرار دهم.