به این آدرس مراجعه کنید: http://training.pluralsight.com/redeem (اگر دسترسی به گوگل پلاس ندارید) و سپس از کد زیر استفاده کنید:
Multi-use Redemption Code: 176-2-GCKU-6VDH
using System.IO; using System.Xml; using System.Xml.Serialization; namespace DNTViewer.Common.Toolkit { public static class Serializer { public static string Serialize<T>(T type) { var serializer = new XmlSerializer(type.GetType()); using (var stream = new MemoryStream()) { serializer.Serialize(stream, type); stream.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(stream)) { return reader.ReadToEnd(); } } } } }
private string createXmlFile(string dir) { var xLinq = new XElement("ArrayOfPost", _blogPosts .AsNoTracking() .Include(x => x.Comments) .Include(x => x.User) .Include(x => x.Tags) .OrderBy(x => x.Id) .ToList() .Select(x => new XElement("Post", postXElement(x))) ); var xmlFile = Path.Combine(dir, "dot-net-tips-database.xml"); xLinq.Save(xmlFile); return xmlFile; } private static XElement[] postXElement(BlogPost x) { return new XElement[] { new XElement("Id", x.Id), new XElement("Title", x.Title), new XElement("Body", x.Body), new XElement("CreatedOn", x.CreatedOn), tagElement(x), new XElement("User", new XElement("Id", x.UserId.Value), new XElement("FriendlyName", x.User.FriendlyName)) }.Where(item => item != null).ToArray(); } private static XElement tagElement(BlogPost x) { var tags = x.Tags.Any() ? x.Tags.Select(y => new XElement("Tag", new XElement("Id", y.Id), new XElement("Name", y.Name))) .ToArray() : null; if (tags == null) return null; return new XElement("Tags", tags); }
using System.IO; using System.Xml; using System.Xml.Serialization; namespace DNTViewer.Common.Toolkit { public static class Serializer { public static T DeserializePath<T>(string xmlAddress) { using (var xmlReader = new XmlTextReader(xmlAddress)) { var serializer = new XmlSerializer(typeof(T)); return (T)serializer.Deserialize(xmlReader); } } } }
mydomain.com/Home/Index mydomain.com/home/index mydomain.com/Home/index mydomain.com/home/Index
using System.Globalization; using System.Web; using System.Web.Mvc; namespace WebToolkit { public class ForceWww : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { modifyUrlAndRedirectPermanent(filterContext); base.OnActionExecuting(filterContext); } private static void modifyUrlAndRedirectPermanent(ActionExecutingContext filterContext) { if (canIgnoreRequest(filterContext)) return; var absoluteUrl = HttpUtility.UrlDecode(filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)); var absoluteUrlToLower = absoluteUrl.ToLowerInvariant(); absoluteUrlToLower = forceWwwAndLowercase(filterContext, absoluteUrlToLower); absoluteUrlToLower = avoidTrailingSlashes(filterContext, absoluteUrlToLower); if (!absoluteUrl.Equals(absoluteUrlToLower)) { filterContext.Result = new RedirectResult(absoluteUrlToLower, permanent: true); } } private static string avoidTrailingSlashes(ActionExecutingContext filterContext, string absoluteUrlToLower) { if (!isRootRequest(filterContext) && absoluteUrlToLower.EndsWith("/")) return absoluteUrlToLower.TrimEnd(new[] { '/' }); return absoluteUrlToLower; } private static bool isRootRequest(ActionExecutingContext filterContext) { return filterContext.RequestContext.HttpContext.Request.Url.AbsolutePath == "/"; } private static bool canIgnoreRequest(ActionExecutingContext filterContext) { return filterContext.IsChildAction || filterContext.HttpContext.Request.IsAjaxRequest() || filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.Contains("?"); } private static string forceWwwAndLowercase(ActionExecutingContext filterContext, string absoluteUrlToLower) { if (isLocalRequet(filterContext)) return absoluteUrlToLower; if (absoluteUrlToLower.Contains("www")) return absoluteUrlToLower; return absoluteUrlToLower.Replace("http://", "http://www.") .Replace("https://", "https://www."); } private static bool isLocalRequet(ActionExecutingContext filterContext) { return filterContext.RequestContext.HttpContext.Request.IsLocal; } } }
public override bool RunAt(DateTime utcNow) { if (this.IsShuttingDown || this.Pause) return false; var now = utcNow.AddHours(3.5); return (now.Day % 3 == 0) && (now.Hour == 0 && now.Minute == 1 && now.Second == 1); }
public class UserFactory { public User CreateUser(string email) { return new User(email); } }
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace ValueOf { public class ValueOf<TValue, TThis> where TThis : ValueOf<TValue, TThis>, new() { private static readonly Func<TThis> Factory; /// <summary> /// WARNING - THIS FEATURE IS EXPERIMENTAL. I may change it to do /// validation in a different way. /// Right now, override this method, and throw any exceptions you need to. /// Access this.Value to check the value /// </summary> protected virtual void Validate() { } static ValueOf() { ConstructorInfo ctor = typeof(TThis) .GetTypeInfo() .DeclaredConstructors .First(); var argsExp = new Expression[0]; NewExpression newExp = Expression.New(ctor, argsExp); LambdaExpression lambda = Expression.Lambda(typeof(Func<TThis>), newExp); Factory = (Func<TThis>)lambda.Compile(); } public TValue Value { get; protected set; } public static TThis From(TValue item) { TThis x = Factory(); x.Value = item; x.Validate(); return x; } protected virtual bool Equals(ValueOf<TValue, TThis> other) { return EqualityComparer<TValue>.Default.Equals(Value, other.Value); } public override bool Equals(object obj) { if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; return obj.GetType() == GetType() && Equals((ValueOf<TValue, TThis>)obj); } public override int GetHashCode() { return EqualityComparer<TValue>.Default.GetHashCode(Value); } public static bool operator ==(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b) { if (a is null && b is null) return true; if (a is null || b is null) return false; return a.Equals(b); } public static bool operator !=(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b) { return !(a == b); } public override string ToString() { return Value.ToString(); } } }
public class EmailAddress : ValueOf<string, EmailAddress> { }
EmailAddress emailAddress = EmailAddress.From("foo@bar.com");
public class Address : ValueOf<(string firstLine, string secondLine, Postcode postcode), Address> { }
<embed height="400" width="500" flashvars="config=http://www.aparat.com//video/video/config/videohash/BA9Md/watchtype/embed" allowfullscreen="true" quality="high" name="aparattv_BA9Md" id="aparattv_BA9Md" src="http://host10.aparat.com/public/player/aparattv" type="application/x-shockwave-flash">
using System.Web.Mvc; namespace MvcApplication1 { public static class AparatPlayerHelper { public static MvcHtmlString AparatPlayer(this HtmlHelper helper, string mediafile, int height, int width) { var player = @"<embed height=""{0}"" width=""{1}"" flashvars=""config=http://www.aparat.com//video/video/config/videohash/{2}/watchtype/embed"" allowfullscreen=""true"" quality=""high"" name=""aparattv_{2}"" id=""aparattv_{2}"""" src=""http://host10.aparat.com/public/player/aparattv"" type=""application/x-shockwave-flash"">"; player = string.Format(player, height, width, mediafile); return new MvcHtmlString(player); } } }
@Html.AparatPlayer("BA9Md", 400, 500)
using System; using System.Drawing; using System.Web.Mvc; namespace MvcApplication1 { public static class YouTubePlayerHelper { public static MvcHtmlString YouTubePlayer(this HtmlHelper helper, string playerId, string mediaFile, YouTubePlayerOption youtubePlayerOption) { const string baseURL = "http://www.youtube.com/v/"; // YouTube Embedded Code var player = @"<div id=""YouTubePlayer_{7}""width:{1}px; height:{2}px;""> <object width=""{1}"" height=""{2}""> <param name=""movie"" value=""{6}{0}&fs=1&border={3}&color1={4}&color2={5}""></param> <param name=""allowFullScreen"" value=""true""></param> <embed src=""{6}{0}&fs=1&border={3}&color1={4}&color2={5}"" type = ""application/x-shockwave-flash"" width=""{1}"" height=""{2}"" allowfullscreen=""true""></embed> </object> </div>"; // Replace All The Value player = String.Format(player, mediaFile, youtubePlayerOption.Width, youtubePlayerOption.Height, (youtubePlayerOption.Border ? "1" : "0"), ConvertColorToHexa.ConvertColorToHexaString(youtubePlayerOption.PrimaryColor), ConvertColorToHexa.ConvertColorToHexaString(youtubePlayerOption.SecondaryColor), baseURL, playerId); //Retrun Embedded Code return new MvcHtmlString(player); } } public class YouTubePlayerOption { int _width = 425; int _height = 355; Color _color1 = Color.Black; Color _color2 = Color.Aqua; public YouTubePlayerOption() { Border = false; } public int Width { get { return _width; } set { _width = value; } } public int Height { get { return _height; } set { _height = value; } } public Color PrimaryColor { get { return _color1; } set { _color1 = value; } } public Color SecondaryColor { get { return _color2; } set { _color2 = value; } } public bool Border { get; set; } } public class ConvertColorToHexa { private static readonly char[] HexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public static string ConvertColorToHexaString(Color color) { var bytes = new byte[3]; bytes[0] = color.R; bytes[1] = color.G; bytes[2] = color.B; var chars = new char[bytes.Length * 2]; for (int i = 0; i < bytes.Length; i++) { int b = bytes[i]; chars[i * 2] = HexDigits[b >> 4]; chars[i * 2 + 1] = HexDigits[b & 0xF]; } return new string(chars); } } }
@Html.YouTubePlayer("Casablanca", "iLdqKUkkM6w", new YouTubePlayerOption() { Border = true })
namespace BlazorWasm.Client.Services { public interface IClientHotelRoomService { public Task<IEnumerable<HotelRoomDTO>> GetHotelRoomsAsync(DateTime checkInDate, DateTime checkOutDate); public Task<HotelRoomDTO> GetHotelRoomDetailsAsync(int roomId, DateTime checkInDate, DateTime checkOutDate); } }
namespace BlazorWasm.Client.Services { public class ClientHotelRoomService : IClientHotelRoomService { private readonly HttpClient _httpClient; public ClientHotelRoomService(HttpClient httpClient) { _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); } public Task<HotelRoomDTO> GetHotelRoomDetailsAsync(int roomId, DateTime checkInDate, DateTime checkOutDate) { throw new NotImplementedException(); } public Task<IEnumerable<HotelRoomDTO>> GetHotelRoomsAsync(DateTime checkInDate, DateTime checkOutDate) { // How to url-encode query-string parameters properly var uri = new UriBuilderExt(new Uri(_httpClient.BaseAddress, "/api/hotelroom")) .AddParameter("checkInDate", $"{checkInDate:yyyy'-'MM'-'dd}") .AddParameter("checkOutDate", $"{checkOutDate:yyyy'-'MM'-'dd}") .Uri; return _httpClient.GetFromJsonAsync<IEnumerable<HotelRoomDTO>>(uri); } } }
using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; using BlazorServer.Models; using BlazorWasm.Client.Utils; using System; using System.Collections.Specialized; using System.Web; namespace BlazorWasm.Client.Utils { public class UriBuilderExt { private readonly NameValueCollection _collection; private readonly UriBuilder _builder; public UriBuilderExt(Uri uri) { _builder = new UriBuilder(uri); _collection = HttpUtility.ParseQueryString(string.Empty); } public UriBuilderExt AddParameter(string key, string value) { _collection.Add(key, value); return this; } public Uri Uri { get { _builder.Query = _collection.ToString(); return _builder.Uri; } } } }
@using BlazorWasm.Client.Services @using BlazorServer.Models
namespace BlazorWasm.Client { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); // ... builder.Services.AddScoped<IClientHotelRoomService, ClientHotelRoomService>(); // ... } } }
namespace BlazorWasm.WebApi.Controllers { [Route("api/[controller]")] [ApiController] public class HotelRoomController : ControllerBase { // ... [HttpGet] public async Task<IActionResult> GetHotelRooms(DateTime? checkInDate, DateTime? checkOutDate) { // ... } [HttpGet("{roomId}")] public async Task<IActionResult> GetHotelRoom(int? roomId, DateTime? checkInDate, DateTime? checkOutDate) { // ... } } }
namespace BlazorServer.Services { public interface IHotelRoomService : IDisposable { Task<List<HotelRoomDTO>> GetAllHotelRoomsAsync(DateTime? checkInDate, DateTime? checkOutDate); Task<HotelRoomDTO> GetHotelRoomAsync(int roomId, DateTime? checkInDate, DateTime? checkOutDate); // ... } }
{ "BaseAPIUrl": "https://localhost:5001/" }
namespace BlazorWasm.Client { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); // ... // builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.Configuration.GetValue<string>("BaseAPIUrl")) }); // ... } } }
@page "/hotel/rooms" @inject ILocalStorageService LocalStorage @inject IJSRuntime JsRuntime @inject IClientHotelRoomService HotelRoomService <h3>HotelRooms</h3> @code { HomeVM HomeModel = new HomeVM(); IEnumerable<HotelRoomDTO> Rooms = new List<HotelRoomDTO>(); protected override async Task OnInitializedAsync() { try { var model = await LocalStorage.GetItemAsync<HomeVM>(ConstantKeys.LocalInitialBooking); if (model is not null) { HomeModel = model; } else { HomeModel.NoOfNights = 1; } await LoadRooms(); } catch (Exception e) { await JsRuntime.ToastrError(e.Message); } } private async Task LoadRooms() { Rooms = await HotelRoomService.GetHotelRoomsAsync(HomeModel.StartDate, HomeModel.EndDate); } }
{ "iisSettings": { "iisExpress": { "applicationUrl": "http://localhost:62930", "sslPort": 44314 } } }
{ "BlazorWasm.Client": { "applicationUrl": "https://localhost:5002;http://localhost:5003", } }
استفاده از Exception برای نمایش پیغام برای کاربر نهایی
با صدور یک استثناء و مدیریت سراسری آن در بالاترین (خارجی ترین) لایه و نمایش پیغام مرتبط با آن به کاربر نهایی، میتوان از آن به عنوان ابزاری برای ارسال هر نوع پیغامی به کاربر نهایی استفاده کرد. اگر قوانین تجاری با موفقیت برآورده نشدهاند یا لازم است به هر دلیلی یک پیغام مرتبط با یک اعتبارسنجی تجاری را برای کاربر نمایش دهید، این روش بسیار کارساز میباشد و با یکبار وقت گذاشتن برای توسعه زیرساخت برای این موضوع، به عنوان یک Cross Cutting Concern تحت عنوان Exception Management، آزادی عمل زیادی در ادامه توسعه سیستم خود خواهید داشت.
اگر مطالب پیش نیاز را مطالعه کنید، قطعا روش مطرح شده را انتخاب نخواهید کرد؛ به همین دلیل به دنبال راه حل صحیح برخورد با این سناریوها بودم که نتیجه آن را در ادامه خواهیم دید.
راه حل صحیح برای برخورد با این سناریوها بازگشت یک Result میباشد که در مطلب قبلی هم تحت عنوان OperationResult مطرح شد.
public class Result { private static readonly Result SuccessResult = new Result(true, null); protected Result(bool succeeded, string message) { if (succeeded) { if (message != null) throw new ArgumentException("There should be no error message for success.", nameof(message)); } else { if (message == null) throw new ArgumentNullException(nameof(message), "There must be error message for failure."); } Succeeded = succeeded; Error = message; } public bool Succeeded { get; } public string Error { get; } [DebuggerStepThrough] public static Result Success() { return SuccessResult; } [DebuggerStepThrough] public static Result Failed(string message) { return new Result(false, message); } [DebuggerStepThrough] public static Result<T> Failed<T>(string message) { return new Result<T>(default, false, message); } [DebuggerStepThrough] public static Result<T> Success<T>(T value) { return new Result<T>(value, true, string.Empty); } [DebuggerStepThrough] public static Result Combine(string seperator, params Result[] results) { var failedResults = results.Where(x => !x.Succeeded).ToList(); if (!failedResults.Any()) return Success(); var error = string.Join(seperator, failedResults.Select(x => x.Error).ToArray()); return Failed(error); } [DebuggerStepThrough] public static Result Combine(params Result[] results) { return Combine(", ", results); } [DebuggerStepThrough] public static Result Combine<T>(params Result<T>[] results) { return Combine(", ", results); } [DebuggerStepThrough] public static Result Combine<T>(string seperator, params Result<T>[] results) { var untyped = results.Select(result => (Result) result).ToArray(); return Combine(seperator, untyped); } public override string ToString() { return Succeeded ? "Succeeded" : $"Failed : {Error}"; } }
مشابه کلاس بالا، در فریمورک ASP.NET Identity کلاسی تحت عنوان IdentityResult برای همین منظور در نظر گرفته شدهاست.
پراپرتی Succeeded نشان دهنده موفقت آمیز بودن یا عدم موفقیت عملیات (به عنوان مثال یک متد ApplicationService) میباشد. پراپرتی Error دربرگیرنده پیغام خطایی میباشد که قبلا از طریق Message مربوط به یک استثناء صادر شده، در اختیار بالاترین لایه قرار میگرفت. با استفاده از متد Combine، امکان ترکیب چندین Result حاصل از عملیات مختلف را خواهید داشت. متدهای استاتیک Failed و Success هم برای درگیر نشدن برای وهله سازی از کلاس Result در نظر گرفته شدهاند.
متد GetForEdit مربوط به MeetingService را در نظر بگیرید. به عنوان مثال وظیفه این متد بازگشت یک MeetingEditModel میباشد؛ اما با توجه به یکسری قواعد تجاری، بهعنوان مثال «امکان ویرایش جلسهای که پابلیش نهایی شدهاست، وجود ندارد و ...» لازم است خروجی این متد نیز در صورت Fail شدن، دلیل آن را به مصرف کننده ارائه دهد. از این رو کلاس جنریک Result را به شکل زیر خواهیم داشت:
public class Result<T> : Result { private readonly T _value; protected internal Result(T value, bool succeeded, string error) : base(succeeded, error) { _value = value; } public T Value { get { if (!Succeeded) throw new InvalidOperationException("There is no value for failure."); return _value; } } }
public static class ResultExtensions { public static Result<TK> OnSuccess<T, TK>(this Result<T> result, Func<T, TK> func) { return !result.Succeeded ? Result.Failed<TK>(result.Error) : Result.Success(func(result.Value)); } public static Result<T> Ensure<T>(this Result<T> result, Func<T, bool> predicate, string message) { if (!result.Succeeded) return Result.Failed<T>(result.Error); return !predicate(result.Value) ? Result.Failed<T>(message) : Result.Success(result.Value); } public static Result<TK> Map<T, TK>(this Result<T> result, Func<T, TK> func) { return !result.Succeeded ? Result.Failed<TK>(result.Error) : Result.Success(func(result.Value)); } public static Result<T> OnSuccess<T>(this Result<T> result, Action<T> action) { if (result.Succeeded) action(result.Value); return result; } public static T OnBoth<T>(this Result result, Func<Result, T> func) { return func(result); } public static Result OnSuccess(this Result result, Action action) { if (result.Succeeded) action(); return result; } public static Result<T> OnSuccess<T>(this Result result, Func<T> func) { return !result.Succeeded ? Result.Failed<T>(result.Error) : Result.Success(func()); } public static Result<TK> OnSuccess<T, TK>(this Result<T> result, Func<T, Result<TK>> func) { return !result.Succeeded ? Result.Failed<TK>(result.Error) : func(result.Value); } public static Result<T> OnSuccess<T>(this Result result, Func<Result<T>> func) { return !result.Succeeded ? Result.Failed<T>(result.Error) : func(); } public static Result<TK> OnSuccess<T, TK>(this Result<T> result, Func<Result<TK>> func) { return !result.Succeeded ? Result.Failed<TK>(result.Error) : func(); } public static Result OnSuccess<T>(this Result<T> result, Func<T, Result> func) { return !result.Succeeded ? Result.Failed(result.Error) : func(result.Value); } public static Result OnSuccess(this Result result, Func<Result> func) { return !result.Succeeded ? result : func(); } public static Result Ensure(this Result result, Func<bool> predicate, string message) { if (!result.Succeeded) return Result.Failed(result.Error); return !predicate() ? Result.Failed(message) : Result.Success(); } public static Result<T> Map<T>(this Result result, Func<T> func) { return !result.Succeeded ? Result.Failed<T>(result.Error) : Result.Success(func()); } public static TK OnBoth<T, TK>(this Result<T> result, Func<Result<T>, TK> func) { return func(result); } public static Result<T> OnFailure<T>(this Result<T> result, Action action) { if (!result.Succeeded) action(); return result; } public static Result OnFailure(this Result result, Action action) { if (!result.Succeeded) action(); return result; } public static Result<T> OnFailure<T>(this Result<T> result, Action<string> action) { if (!result.Succeeded) action(result.Error); return result; } public static Result OnFailure(this Result result, Action<string> action) { if (!result.Succeeded) action(result.Error); return result; } }
[HttpPost, AjaxOnly, ValidateAntiForgeryToken, ValidateModelState] public virtual async Task<ActionResult> Create([Bind(Prefix = "Model")]MeetingCreateModel model) { var result = await _service.CreateAsync(model); return result.OnSuccess(() => { }) .OnFailure(() => { }) .OnBoth(r => r.Succeeded ? InformationNotification("Messages.Save.Success") : ErrorMessage(r.Error)); }
یا در حالتهای پیچیده تر:
var result = await _service.CreateAsync(new TenantAwareEntityCreateModel()); return Result.Combine(result, Result.Success(), Result.Failed("نتیجه یک متد دیگر به عنوان مثال")) .OnSuccess(() => { }) .OnFailure(() => { }) .OnBoth(r => r.Succeeded ? Json("OK") : Json(r.Error));
ترکیب با الگوی Maybe یا Option
public struct Maybe<T> : IEquatable<Maybe<T>> where T : class { private readonly T _value; private Maybe(T value) { _value = value; } public bool HasValue => _value != null; public T Value => _value ?? throw new InvalidOperationException(); public static Maybe<T> None => new Maybe<T>(); public static implicit operator Maybe<T>(T value) { return new Maybe<T>(value); } public static bool operator ==(Maybe<T> maybe, T value) { return maybe.HasValue && maybe.Value.Equals(value); } public static bool operator !=(Maybe<T> maybe, T value) { return !(maybe == value); } public static bool operator ==(Maybe<T> left, Maybe<T> right) { return left.Equals(right); } public static bool operator !=(Maybe<T> left, Maybe<T> right) { return !(left == right); } /// <inheritdoc /> /// <summary> /// Avoid boxing and Give type safety /// </summary> /// <param name="other"></param> /// <returns></returns> public bool Equals(Maybe<T> other) { if (!HasValue && !other.HasValue) return true; if (!HasValue || !other.HasValue) return false; return _value.Equals(other.Value); } /// <summary> /// Avoid reflection /// </summary> /// <param name="obj"></param> /// <returns></returns> public override bool Equals(object obj) { if (obj is T typed) { obj = new Maybe<T>(typed); } if (!(obj is Maybe<T> other)) return false; return Equals(other); } /// <summary> /// Good practice when overriding Equals method. /// If x.Equals(y) then we must have x.GetHashCode()==y.GetHashCode() /// </summary> /// <returns></returns> public override int GetHashCode() { return HasValue ? _value.GetHashCode() : 0; } public override string ToString() { return HasValue ? _value.ToString() : "NO VALUE"; } }
public static Result<T> ToResult<T>(this Maybe<T> maybe, string message) where T : class { return !maybe.HasValue ? Result.Failed<T>(message) : Result.Success(maybe.Value); }
Result<Customer> customerResult = _customerRepository.GetById(model.Id) .ToResult("Customer with such Id is not found: " + model.Id);
همچنین متدهای الحاقی زیر را نیز برای ساختار داده Maybe میتوان در نظر گرفت:
public static T GetValueOrDefault<T>(this Maybe<T> maybe, T defaultValue = default) where T : class { return maybe.GetValueOrDefault(x => x, defaultValue); } public static TK GetValueOrDefault<T, TK>(this Maybe<T> maybe, Func<T, TK> selector, TK defaultValue = default) where T : class { return maybe.HasValue ? selector(maybe.Value) : defaultValue; } public static Maybe<T> Where<T>(this Maybe<T> maybe, Func<T, bool> predicate) where T : class { if (!maybe.HasValue) return default(T); return predicate(maybe.Value) ? maybe : default(T); } public static Maybe<TK> Select<T, TK>(this Maybe<T> maybe, Func<T, TK> selector) where T : class where TK : class { return !maybe.HasValue ? default : selector(maybe.Value); } public static Maybe<TK> Select<T, TK>(this Maybe<T> maybe, Func<T, Maybe<TK>> selector) where T : class where TK : class { return !maybe.HasValue ? default(TK) : selector(maybe.Value); } public static void Execute<T>(this Maybe<T> maybe, Action<T> action) where T : class { if (!maybe.HasValue) return; action(maybe.Value); } }
using System.Text.RegularExpressions; using Microsoft.AspNetCore.Routing; public class CustomUrlTransformer : IOutboundParameterTransformer { private static readonly Regex _camelCasingRegEx = new Regex("([a-z])([A-Z])", RegexOptions.Compiled); public string TransformOutbound(object value) { return value == null ? null : _camelCasingRegEx.Replace(value.ToString(), "$1-$2"); } }
mysite.com/reporting
using System.ComponentModel.DataAnnotations; namespace SampleProject.Models { public class Channel { public string ChannelTitle { get; set; } [Required] public string ChannelUrl { get; set; } } }
using System.Collections.Generic; namespace SampleProject.Models { public static class ChannelDataSource { static ChannelDataSource() => Channels = new List<Channel>(); public static List<Channel> Channels { get; private set; } public static void Add(Channel channel) => Channels.Add(channel); } }
using SampleProject.Models; using System.Linq; using System.Web.Mvc; namespace SampleProject.Controllers { public class ChannelController : Controller { // GET: Channel public ActionResult Index() { var channels = ChannelDataSource.Channels; return View(channels); } public ActionResult Channel(string channelUrl) { if (string.IsNullOrWhiteSpace(channelUrl)) { return new HttpNotFoundResult("channel not found!"); } var channel = ChannelDataSource.Channels.SingleOrDefault(ch => ch.ChannelUrl == channelUrl.ToLower()); if (channel == null) { return new HttpNotFoundResult("channel not found!"); } return View(channel); } public ActionResult Create() => View(); [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Channel channel) { if (!ModelState.IsValid) { ModelState.AddModelError(string.Empty, "Please check your inputs!"); return View(channel); } ChannelDataSource.Add(channel); TempData["Message"] = "Channel added successfully!"; return RedirectToAction(nameof(Index)); } } }
using System.Web.Mvc; using System.Web.Routing; namespace SampleProject { public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "ChannelUrls", url: "{channelurl}", defaults: new { controller = "Channel", action = "Channel", id = UrlParameter.Optional } ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Channel", action = "Index", id = UrlParameter.Optional } ); } } }
using System.Collections.Generic; using System.Linq; using System.Web.Routing; namespace SampleProject.Models { public static class Utility { public static List<string> GetAllAreaNames() { var areaNames = RouteTable.Routes.OfType<Route>() .Where(d => d.DataTokens != null) .Where(d=> d.DataTokens.ContainsKey("area")) .Select(r => r.DataTokens["area"].ToString().ToLower()) .ToList(); return areaNames; } } }
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Web.Mvc; using SampleProject.Models; namespace SampleProject.CustomValidators { public class CheckForAreaExisting : ValidationAttribute, IClientValidatable { public List<string> AreaNames { get { return Utility.GetAllAreaNames(); } } public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var rule = new ModelClientValidationRule { ValidationType = "checkforareaexisting", ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()) }; rule.ValidationParameters.Add("areanames", string.Join(",", AreaNames)); yield return rule; } public override bool IsValid(object value) { if (value != null) { return Utility.GetAllAreaNames() .SingleOrDefault(area => area == value.ToString().ToLower()) == null; } return true; } } }
jQuery.validator.addMethod("checkforareaexisting", function (value, element, param) { var isValueOneOfTheAreaNames = $.inArray(value.toLowerCase(), param.areaNames) === -1; return isValueOneOfTheAreaNames; }); $.validator.unobtrusive.adapters.add('checkforareaexisting', ['areanames'], function (options) { options.rules['checkforareaexisting'] = { areaNames: options.params.areanames.split(',') }; options.messages['checkforareaexisting'] = options.message; });
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>_Layout</title> <style> </style> </head> <body> <div> @RenderBody() </div> <script src="~/Scripts/Jquery.js"></script> <script src="~/Scripts/jquery.validate.min.js"></script> <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script> <script src="~/Scripts/CustomValidation.js"></script> </body> </html>
using SampleProject.CustomValidators; using System.ComponentModel.DataAnnotations; namespace SampleProject.Models { public class Channel { public string ChannelTitle { get; set; } [Required] [CheckForAreaExisting(ErrorMessage = "You can't use this url for your channel!")] public string ChannelUrl { get; set; } } }
[HttpPost] public ActionResult CheckForAreaExisting(string channelUrl) { var isValueOneOfTheAreaNames = Utility.GetAllAreaNames() .SingleOrDefault(area => area == channelUrl.ToLower()) == null; return Json(isValueOneOfTheAreaNames); }
using System.ComponentModel.DataAnnotations; using System.Web.Mvc; namespace SampleProject.Models { public class Channel { public string ChannelTitle { get; set; } [Required] [Remote("CheckForAreaExisting", "Channel", ErrorMessage = "You can't use this url for your channel!", HttpMethod = "Post")] public string ChannelUrl { get; set; } } }