public double TotalRating { get ; set ; } = 0.0 ; public int TotalRaters { get ; set ; } = 0 ; public double AverageRating { get ; set ; } = 0.0 ;
modelBuilder.Entity<Product>().OwnsOne(p = > p.Rating)
public double TotalRating { get ; set ; } = 0.0 ; public int TotalRaters { get ; set ; } = 0 ; public double AverageRating { get ; set ; } = 0.0 ;
modelBuilder.Entity<Product>().OwnsOne(p = > p.Rating)
namespace MvcApplication3.Models
{
public class Product
{
public Product(string productNumber, string name, decimal price)
{
Name = name;
Price = price;
ProductNumber = productNumber;
}
public string ProductNumber { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
}
using System.Collections.Generic;
namespace MvcApplication3.Models
{
public class Products : List<Product>
{
public Products()
{
this.Add(new Product("D123", "Super Fast Bike", 1000M));
this.Add(new Product("A356", "Durable Helmet", 123.45M));
this.Add(new Product("M924", "Soft Bike Seat", 34.99M));
}
}
}
using System.Web.Mvc;
using MvcApplication3.Models;
namespace MvcApplication3.Controllers
{
public class ProductsController : Controller
{
public ActionResult Index()
{
var products = new Products();
return View(products);
}
}
}
@model List<MvcApplication3.Models.Product>
@{
ViewBag.Title = "Index";
var number = 12;
var data = "some text...";
<h2>line1: @data</h2>
@:line-2: @data <br />
<text>line-3:</text> @data
}
<br />
site@(data)
<br />
@@name
<br />
@(number/10)
<br />
First product: @Model.First().Name
<br />
@if (@number>10)
{
<span>@data</span>
}
else
{
<text>Plain Text</text>
}
<br />
@foreach (var item in Model)
{
<li>@item.Name, $@item.Price </li>
}
@*
A Razor Comment
*@
<br />
@("First product: " + Model.First().Name)
<br />
<img src="@(number).jpg" />
@model List<MvcApplication3.Models.Product>
@{
ViewBag.Title = "Index";
var number = 12;
var data = "some text...";
<h2>line1: @data</h2>
@:line-2: @data <br />
<text>line-3:</text> @data
}
@{ ...Code Block.... }
<br />
site@data
<br />
site@(data)
<img src="@(number).jpg" />
<br />
@name
<br />
@@name
First product: @Model.First().Name
<br />
@number/10
<br />
@(number/10)
@if (@number>10)
{
<span>@data</span>
}
else
{
<text>Plain Text</text>
}
@if( i < 1 ) int myVar=0;
@foreach (var item in Model)
{
<li>@item.Name, $@item.Price </li>
}
@using namespace;
@*
A Razor Comment / Server side Comment
*@
@("First product: " + Model.First().Name)
Unrecognised method call: System.String:Boolean StartsWith(System.String)
Unrecognised method call: System.String:Boolean Contains(System.String)
using NHibernate.Validator.Constraints;
namespace NH3Test.MappingDefinitions.Domain
{
public class Account
{
public virtual int Id { get; set; }
[NotNullNotEmpty]
[Length(Min = 3, Max = 120, Message = "طول نام باید بین 3 و 120 کاراکتر باشد")]
public virtual string Name { get; set; }
[NotNull]
public virtual int Balance { set; get; }
}
}
var list = new[] { 12,10};
var resultList = session.QueryOver<Account>()
.WhereRestrictionOn(p => p.Balance)
.IsIn(list)
.List();
SELECT
this_.AccountId as AccountId0_0_,
this_.Name as Name0_0_,
this_.Balance as Balance0_0_
FROM
Accounts this_
WHERE
this_.Balance in (
@p0 /* = 10 */, @p1 /* = 12 */
)
var accountsContianX = session.QueryOver<Account>()
.WhereRestrictionOn(x => x.Name)
.IsLike("X", NHibernate.Criterion.MatchMode.Anywhere)
.List();
SELECT
this_.AccountId as AccountId0_0_,
this_.Name as Name0_0_,
this_.Balance as Balance0_0_
FROM
Accounts this_
WHERE
this_.Name like @p0 /* = %X% */
using NHibernate.Criterion;
...
var accountsContianX = session.QueryOver<Account>()
.Where(x => x.Name.IsLike("%X%"))
.List();
using System;
using System.Linq.Expressions;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Impl;
namespace NH3Test.ConsoleApplication
{
public static class MyQueryOverExts
{
public static bool YearIs(this DateTime projection, int year)
{
throw new Exception("Not to be used directly - use inside QueryOver expression");
}
public static ICriterion ProcessAnsiYear(MethodCallExpression methodCallExpression)
{
string property = ExpressionProcessor.FindMemberExpression(methodCallExpression.Arguments[0]);
object value = ExpressionProcessor.FindValue(methodCallExpression.Arguments[1]);
return Restrictions.Eq(
Projections.SqlFunction("year", NHibernateUtil.DateTime, Projections.Property(property)),
value);
}
}
public class QueryOverExtsRegistry
{
public static void RegistrMyQueryOverExts()
{
ExpressionProcessor.RegisterCustomMethodCall(
() => MyQueryOverExts.YearIs(DateTime.Now, 0),
MyQueryOverExts.ProcessAnsiYear);
}
}
}
QueryOverExtsRegistry.RegistrMyQueryOverExts(); //یکبار در ابتدای اجرای برنامه باید ثبت شود
...
var data = session.QueryOver<Account>()
.Where(x => x.AddDate.YearIs(2010))
.List();
SELECT
this_.AccountId as AccountId0_0_,
this_.Name as Name0_0_,
this_.Balance as Balance0_0_,
this_.AddDate as AddDate0_0_
FROM
Accounts this_
WHERE
strftime("%Y", this_.AddDate) = @p0 /* =2010 */
public byte[] RowVersion { get; set; }
modelBuilder.Entity<Blog>() .Property(p => p.RowVersion) .IsRowVersion();
public virtual PropertyBuilder IsRowVersion() { Builder.ValueGenerated(ValueGenerated.OnAddOrUpdate, ConfigurationSource.Explicit); Builder.IsConcurrencyToken(true, ConfigurationSource.Explicit); return this; }
یک سیستم مدیریت کاربر مبتنی بر نقش، سیستمی است که در آن نقشهای گوناگونی تعریف میگردد. به گونهای که هر نقش شامل یک سری دسترسی هایی است که به کاربر اجازه میدهد تا به بخشی از سیستم دسترسی داشته باشد. هر کاربر در این سیستم میتواند یک و یا چند نقش متفاوت داشته باشد و بر اساس آن نقشها و دسترسیهای تعریف شده، درون هر نقش به قسمتی از سیستم دسترسی داشته باشد.بر اساس این تعریف ما در نهایت سه موجودیت نقش (Role)، دسترسی (Permission) و کاربر (User) را خواهیم داشت. در این سیستم دسترسی را اینگونه تعریف میکنیم:
دسترسی در یک سیستم، مجموعهای از حوزهها و یا ناحیههایی است که در سیستم تعریف میشود. این حوزهها میتوانند شامل یک متد یا مجموعهای از متدهای نوشته شده و یا فراتر از آن، شامل مجموعهای از کنترلرها باشد که کاربر اجازهی فراخوانی آنها را دارد.
بدیهی است کاربر برای ما موجودیتی مشخص است. یک کاربر ویژگیهایی مانند نام، نام کاربری، سن، رمز عبور و ... خاص خود را داراست که به واسطه این ویژگیها از کاربری دیگر تمییز داده میشود. کاربر در سیستم طی دو مرحله جداگانه Authentication و Authorization مجاز است تا از بخشهایی از سیستم استفاده کند. مرحله Authentication به طور خلاصه شامل مرحلهای است که هویت کاربر (به عنوان مثال نام کاربری و رمز عبور) تایید صلاحیت میشود. این مرحله در واقع تایید کننده کاربر است و اما بخش بعدی که ما قصد داریم تا در این مورد راهکاری را ارائه دهیم بخش Authorization است. در این بخش به کاربر بر اساس نقش وی دسترسیهایی اعطا میگردد و کاربر را به استفاده از بخشهایی از سیستم مجاز میدارد.
دیاگرام زیر نمود سه موجودیت کاربر، نقش و دسترسی میباشد.
برای شرح دیاگرام فوق این چنین میتوان گفت که هر کاربر مجاز است چندین نقش داشته باشد و هر نقش نیز میتواند به چندین کاربر انتساب شود. در مورد دسترسیها نیز به همین صورت، هر دسترسی نیز میتواند به چندین نقش انتساب شود.
install-package automapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
using AutoMapper;
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddAutoMapper(); }
public class UserMappingProfile:Profile { public UserMappingProfile() { CreateMap<User, UserPost>(); CreateMap<UserPost,User >() .ForMember(dest=>dest.Username,src=>src.Ignore()) .ForMember(dest=>dest.Email,src=>src.Ignore()); } }
private readonly IMapper _mapper; public UserController(IMapper mapper) { _mapper = mapper; } public async Task<UserPost> FindUser(string username) { var users = await _userServices.GetUser(username); var user = _mapper.Map<User,UserPost>(users); return user; }
public class HomeViewModel { public string Id { get; set; } public string Message { get; set; } public DateTime DateTime { get; set; } }
اکنون به پوشهی Views بروید و فایل Index.cshtml را به این صورت تغییر دهید:
@model AspNetCoreDependencyInjection.Models.HomeViewModel @{ ViewData["Title"] = "Home"; } <div> <div> <div> <p> <b>Id : </b><span>@Model.Id</span> <br /> <b>Date And Time : </b><span> @Model.DateTime </span> <br/> <b>Message : </b><span>@Model.Message</span> </p> </div> </div> </div>
using AspNetCoreDependencyInjection.Services; namespace AspNetCoreDependencyInjection.ServicesImplentaions { public class MessageServiceAA { public string Message() { return "A message from MessageServiceAA"; } } }
namespace AspNetCoreDependencyInjection.Helpers { public class GuidProvider { private readonly Guid _serviceGuid; public GuidProvider() { _serviceGuid = Guid.NewGuid(); } public Guid GetNewGuid() => Guid.NewGuid(); public string GetGuidAsFormatedString(string prefix = "") => getFormatedGuid(_serviceGuid, prefix); private string getFormatedGuid(Guid guid, string prefix = "") { var guidString = guid.GetHashCode().ToString("x"); if (string.IsNullOrEmpty(prefix) == false) guidString = new StringBuilder($"{prefix}-").Append(guidString).ToString(); return guidString; } } }
حالا درون کنترل HomeController، این تغییرات را انجام میدهیم:
private readonly ILogger<HomeController> _logger; private readonly MessageServiceAA _messageService; private readonly GuidProvider _ guidProvider; public HomeController(ILogger<HomeController> logger) { _logger = logger; _messageService = new MessageServiceAA(); _guidProvider = new GuidProvider(); } public IActionResult Index() { var model = new HomeViewModel() { Id = _ guidProvider.GetGuidAsFormatedString(), Message = _messageService.Message(), DateTime = DateTime.Now, }; return View(model); }
همانطور که میبینید، در کد بالا، کنترلر HomeController، به دو شیء از کلاسها و یا سرویسهای GuidProvider و MessageServiceAA به صورت مستقیم وابسته شدهاست و با هر تغییری در هر کدام از این سرویسها، باید دوباره کامپایل شود. علاوه بر این اگر بخواهیم پیاده سازیهای مختلفی را برای هر کدام از این موارد، ارائه دهیم، به مشکل بر میخوریم. خب بیاید تغییراتی را در کد بالا بدهیم تا مشکلات ذکر شده را حل کنیم.
برای این منظور پوشهای را به نام Services میسازیم و اینترفیسی را
به نام IMessageBrokerA ایجاد میکنیم و سپس کاری میکنیم که MessageServiceAA از این
اینترفیس ارث بری کند:
namespace AspNetCoreDependencyInjection.Services { public interface IMessageServiceA { string Message(); } }
و حالا میخواهیم با
استفاده از تزریق وابستگی، وابستگی کنترلر HomeController را از کلاس MessageBrokerAA لغو کرده و آن را به اینترفیس IMessageBrokerA (انتزاع) وابسته کنیم. در
اینجا ما از تکنیک تزریق درون سازنده یا Constructor Injection استفاده میکنیم.
تزریق درون سازنده
در این تکنیک، ما لیستی از وابستگیهای مورد نیاز را به عنوان پارامترهای ورودی سازندهی کلاس، تعریف میکنیم:private readonly ILogger<HomeController> _logger; private readonly IMessageServiceA _messageService; private readonly GuidProvider _guidHelper; public HomeController(ILogger<HomeController> logger , IMessageServiceA messageService) { _logger = logger; _messageService = messageService; _messageService = new MessageServiceAA(); _guidHelper = new GuidProvider(); }
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // کدها جهت خوانایی بیشتر مخفی شده اند } }
همانطورکه میبینید، متد ConfigureService پارامتر IServiceCollection را میگیرد که به وسیلهی WebHost در زمان اجرای برنامه، مقدار دهی میشود.
تعداد زیادی Extension method برای IServiceCollection وجود دارند که برای پشتیبانی از ثبت کردن سرویسهای مختلف در سناریوهای گوناگون به کار میروند. در اینجا ما از نسخهی 3.1 چارچوب ASP.NET Core استفاده میکنیم. برای همین هم برای ثبت سرویسهای پیش فرض فریمورک MVC از متد توسعهی services.AddControllersWithViews() استفاده میکنیم. متد توسعهی AddControllersWithViews() سرویسهایی را که معمولا در فریم ورک MVC استفاده میشوند، درون IServiceCollection ثبت میکند. در نسخههای قبلی چارچوب ASP.NET Core، مانند نسخههای 2.1 و 2.2 برای این کار از متد توسعهی AddMvc() استفاده میشد.
در Microsoft Dependency Injection Container ، معمولا ترتیب ثبت سرویسها مهم نیست.
خب، اولین سرویس اختصاصی برنامهی خودمان را با چرخهی حیات Transient و زیر سرویس پیشین، به شکل زیر ثبت میکنیم :
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddTransient<IMessageServiceA, MessageServiceAA>(); }
public static IServiceCollection AddTransient<TService, TImplementation>(this IServiceCollection services)
در اینجا وقتی ما برای IMessageServiceA ، پیاده سازی MessageServiceA را ثبت میکنیم، از این به بعد DI Container، هر زمانیکه در لیست پارامترهای سازندهی یک کلاس، IMessageServiceA را مشاهده کند، بررسی میکند که چه کلاسی به عنوانی پیاده سازی این اینترفیس ثبت شدهاست، سپس از آن نمونه سازی میکند و درون سازندهی مورد نظر تزریق میکند. خب، حالا برنامه را دوباره اجرا کنید؛ میبینید که برنامه اجرا میشود.
public class Product { public int Id { set; get; } public DateTime AddDate { set; get; } public string Name { set; get; } public decimal Price { set; get; } }
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> <link href="~/Content/themes/base/jquery.ui.all.css" rel="stylesheet" /> <link href="~/Content/jquery.jqGrid/ui.jqgrid.css" rel="stylesheet" /> <link href="~/Content/PersianDatePicker.css" rel="stylesheet" /> <link href="~/Content/Site.css" rel="stylesheet" type="text/css" /> </head> <body> <div> @RenderBody() </div> <script src="~/Scripts/jquery-1.7.2.min.js"></script> <script src="~/Scripts/jquery-ui-1.8.11.min.js"></script> <script src="~/Scripts/i18n/grid.locale-fa.js"></script> <script src="~/Scripts/jquery.jqGrid.min.js"></script> <script src="~/Scripts/PersianDatePicker.js"></script> <script src="~/Scripts/jquery.price_format.2.0.js"></script> @RenderSection("Scripts", required: false) </body> </html>
colModel: [ { name: 'Name', index: 'Name', align: 'right', width: 100, editable: true, edittype: 'text', editoptions: { maxlength: 40, dataInit: function (elem) { // http://jqueryui.com/autocomplete/ $(elem).autocomplete({ source: '@Url.Action("GetProductNames","Home")', minLength: 2, select: function (event, ui) { $(elem).val(ui.item.value); $(elem).trigger('change'); } }); } }, editrules: { required: true } } ],
public ActionResult GetProductNames(string term) { var list = ProductDataSource.LatestProducts .Where(x => x.Name.StartsWith(term)) .Select(x => x.Name) .Take(10) .ToArray(); return Json(list, JsonRequestBehavior.AllowGet); }
colModel: [ { name: 'AddDate', index: 'AddDate', align: 'center', width: 100, editable: true, edittype: 'text', editoptions: { maxlength: 10, // https://www.dntips.ir/post/1382 onclick: "PersianDatePicker.Show(this,'@today');" }, editrules: { required: true } } ],
@{ ViewBag.Title = "Index"; var today = DateTime.Now.ToPersianDate(); }
colModel: [ { name: 'Price', index: 'Price', align: 'center', width: 100, formatter: 'currency', formatoptions: { decimalSeparator: '.', thousandsSeparator: ',', decimalPlaces: 2, prefix: '$' }, editable: true, edittype: 'text', editoptions: { dir: 'ltr', dataInit: function (elem) { // http://jquerypriceformat.com/ $(elem).priceFormat({ prefix: '', thousandsSeparator: ',', clearPrefix: true, centsSeparator: '', centsLimit: 0 }); } }, editrules: { required: true, minValue: 0 } } ],
using System; using System.Globalization; using System.Threading; using System.Web.Mvc; namespace jqGrid05.CustomModelBinders { /// <summary> /// How to register it in the Application_Start method of Global.asax.cs /// ModelBinders.Binders.Add(typeof(decimal), new DecimalBinder()); /// </summary> public class DecimalBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext.ModelType == typeof(decimal) || bindingContext.ModelType == typeof(decimal?)) { return bindDecimal(bindingContext); } return base.BindModel(controllerContext, bindingContext); } private static object bindDecimal(ModelBindingContext bindingContext) { var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult == null) return null; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult); decimal value; var valueAsString = valueProviderResult.AttemptedValue == null ? null : valueProviderResult.AttemptedValue.Trim(); if (string.IsNullOrEmpty(valueAsString)) return null; if (!decimal.TryParse(valueAsString, NumberStyles.Any, Thread.CurrentThread.CurrentCulture, out value)) { const string error ="عدد وارد شده معتبر نیست"; var ex = new InvalidOperationException(error, new Exception(error, new FormatException(error))); bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex); return null; } return value; } } }
using System; using System.Web.Mvc; using System.Web.Routing; using jqGrid05.CustomModelBinders; namespace jqGrid05 { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ModelBinders.Binders.Add(typeof(DateTime), new PersianDateModelBinder()); ModelBinders.Binders.Add(typeof(decimal), new DecimalBinder()); } } }
https://plus.google.com/u/0/userId/posts
https://plus.google.com/_/stream/getactivities/?&sp=[1,2,"userId",null,null,null,null,"social.google.com",[]]
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
//needs a ref. to System.Web.Extensions asm.
using System.Web.Script.Serialization;
namespace GooglePlus
{
[DebuggerDisplay("{DateTime}-{Title}-{Comment}-{Url}")]
public class GooglePlusItem
{
public DateTime DateTime { set; get; }
public string Url { set; get; }
public string Title { set; get; }
public string Comment { set; get; }
}
public static class Utils
{
public static DateTime ConvertFromUnixTimestamp(this long data)
{
return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(data);
}
public static string ToSafeString(this object data)
{
return data == null ? string.Empty : data.ToString();
}
}
public class GooglePlusJsonParser
{
public static IList<GooglePlusItem> GetMyPublicItemsList(string userId)
{
var url = string.Format("https://plus.google.com/_/stream/getactivities/?&sp=[1,2,\"{0}\",null,null,null,null,\"social.google.com\",[]]", userId);
var jsonData = new WebClient().DownloadString(url);
return ParsePublicData(jsonData);
}
public static IList<GooglePlusItem> ParsePublicData(string jsonData)
{
jsonData = removeRedundantData(jsonData);
var posts = parseJson(jsonData);
return createItemsList(posts);
}
private static IList<GooglePlusItem> createItemsList(object[] posts)
{
var result = new List<GooglePlusItem>();
foreach (object[] post in posts)
{
var items = (object[])((object[])post[66])[0];
result.Add(new GooglePlusItem
{
DateTime = (((Int64)post[5]) / 1000).ConvertFromUnixTimestamp(),
Title = items[3].ToSafeString(),
Url = items[1].ToSafeString(),
Comment = post[4].ToSafeString()
});
}
return result;
}
private static object[] parseJson(string jsonData)
{
var jsonObject = new JavaScriptSerializer().Deserialize<object[]>(jsonData);
var posts = (object[])(((object[])jsonObject[1])[0]);
return posts;
}
private static string removeRedundantData(string jsonData)
{
return jsonData.Replace(")]}'", string.Empty)
.Replace("[,", "[\"\",")
.Replace(",,", ",\"\",")
.Replace(",,", ",\"\",");
}
}
}