<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup>
public class Person { public string? Name { get; set; } public string? Family { get; set; }
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <Nullable>enable</Nullable> </PropertyGroup>
public class Person { public string? Name { get; set; } public string? Family { get; set; }
dotnet add package Microsoft.AspNetCore.DataProtection.EntityFrameworkCore
public class MyKeysContext : DbContext, IDataProtectionKeyContext { // A recommended constructor overload when using EF Core // with dependency injection. public MyKeysContext(DbContextOptions<MyKeysContext> options) : base(options) { } // This maps to the table that stores keys. public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } }
public int Id { get; set; } public string FriendlyName { get; set; } public string XmlData { get; set; }
public void ConfigureServices(IServiceCollection services) { // using Microsoft.AspNetCore.DataProtection; services.AddDataProtection() .PersistKeysToDbContext<MyKeysContext>(); }
روی متدهای GetAllCategory و GetAllNews به صورت جداگانه کلیک کنید. متوجه خواهید شد که هرچند در کلاس tblNews شیای از نوع tblCategory و در کلاس tblCategory شیای از نوع مجموعهی tblNews به صورت Virtual تعریف شده است ولی در بر خلاف انتظارمان اثری از آن در اینجا دیده نمیشود. نتیجهی مشاهدهشده به خاطر است که در هر دو تعریف صفت DataMember را به ویژگیهای ناوبری اختصاص نداده ایم و این میتواند راهبرد ما در طراحی WCF باشد. ولی اگر میخواهید ویژگی ناوبری میان موجودیتها در متدهای ما هم دیده شود ادامهی این درس را بخوانید وگرنه ممکن است تصمیم داشته باشید در صورت نیاز به پیوند میان موجودیتها، متد جدیدی بنویسید و از دستورهای Linq استفاده کنید و یا برای اینکار از Stored Procedured بهره ببرید.
در اینجا من این سناریو را دنبال میکنم که در صورتی که متد GetAllNews اجرا شود؛ بدون اینکه نیاز باشد برای دانستن نام دستهی خبر از متد دیگری مانند GetAllCategory استفاده کنیم؛ رکورد وابسته موجودیت دسته در هر خبر نشان داده شود.
از Solution Explorer فایل MyNewsModel.tt را باز کنید و دنبال کد زیر بگردید:
public string NavigationProperty(NavigationProperty navigationProperty) { var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2} {{ {3}get; {4}set; }}", AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, _code.Escape(navigationProperty), _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), _code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); }
سپس آنرا به صورت زیر ویرایش کنید:
public string NavigationProperty(NavigationProperty navigationProperty) { var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); return string.Format( CultureInfo.InvariantCulture, "{0}{1} {2} {3} {{ {4}get; {5}set; }}", navigationProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many ? "[DataMember]" + Environment.NewLine : "", AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, _code.Escape(navigationProperty), _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), _code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); }
پس از ذخیرهی فایل، خواهید دید که صفت DataMember در کلاس tblNews پیش از ویژگی tblCategory افزوده شده است. بار دیگر پروژه را اجرا کنید. روی متد GetAllNews کلیک کنید و روی دکمه Invoke بفشارید. خواهید دید که هرچند tblCategory در ویژگیهای آن قرار گرفته است ولی مقدار آن Null است. برای حل این مشکل باید از Solution Explorer فایل MyNewsService.cs را باز کنید و به به جای کد مربوط به متدهای GetAllNews و GetNews کدهای زیر را قرار دهید:
public List<tblNews> GetAllNews()
{
return dbMyNews.tblNews.Include(p=>p.tblCategory).Where(c=>c.IsDeleted == false).ToList();
}
public tblNews GetNews(int tblNewsId)
{
return dbMyNews.tblNews.Include(p => p.tblCategory).FirstOrDefault(p => p.tblNewsId == tblNewsId);
}
این بار اگر پروژه را اجرا کنید با نتیجهای مانند شکل زیر روبهرو خواهید شد:
در بخش هفتم پیرامون میزبانی WCF Library خواهم نوشت.
public void Save( TEntity entity ) { DbTransaction transaction = null; try { transaction = this.Database.Connection.BeginTransaction(); //عملیات مورد نظر transaction.Commit(); } catch { transaction.Rollback(); } finally { transaction.Dispose(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Menu.Models.Entities { public class Category { public int Id { get; set; } public string Name { get; set; } public int? ParentId { get; set; } public virtual Category Parent { get; set; } public virtual ICollection<Category> Children { get; set; } } } public class MyContext : DbContext { public DbSet<Category> Category { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { // Self Referencing Entity modelBuilder.Entity<Category>() .HasOptional(x => x.Parent) .WithMany(x => x.Children) .HasForeignKey(x => x.ParentId) .WillCascadeOnDelete(false); base.OnModelCreating(modelBuilder); } }
این حالت میتواند تا n سطح پیش برود، حال نحوه نمایش در View مربوطه باید به صورت زیر باشد :
@using Menu.Helper @model IEnumerable<.Models.Entities.Category> @ShowTree(Model) @helper ShowTree(IEnumerable<Menu.Models.Entities.Category> categories) { foreach (var item in categories) { <li class="@(item.Children.Any() ? "dropdown-submenu" : "")"> @Html.ActionLink(item.Name, actionName: "Category", controllerName: "Product", routeValues: new { Id = item.Id, productName = item.Name.ToSeoUrl() }, htmlAttributes: null) @if (item.Children.Any()) { <ul> @ShowTree(item.Children) </ul> } </li> } }
public class PropertiesValidator<TK, T> where T : new() where TK : new() { static TK _instance; public static TK Instance { get { if (_instance == null) { _instance = new TK(); } return _instance; } } public void Validate(T expectedObject, T realObject, params string[] propertiesNotToCompare) { var properties = realObject.GetType().GetProperties(); foreach (var currentRealProperty in properties) { if (!propertiesNotToCompare.Contains(currentRealProperty.Name)) { var currentExpectedProperty = expectedObject.GetType().GetProperty(currentRealProperty.Name); var exceptionMessage = $"The property {currentRealProperty.Name} of class {currentRealProperty.DeclaringType?.Name} was not as expected."; if (currentRealProperty.PropertyType != typeof(DateTime) && currentRealProperty.PropertyType != typeof(DateTime?)) { Assert.AreEqual( currentExpectedProperty.GetValue( expectedObject, null ), currentRealProperty.GetValue( realObject, null ), exceptionMessage ); } else { DateTimeAssert.Validate( currentExpectedProperty.GetValue( expectedObject, null ) as DateTime?, currentRealProperty.GetValue( realObject, null ) as DateTime?, TimeSpan.FromMinutes( 5 ) ); } } } } }
public class ObjectToAssert { public string FirstName { get; set; } public string LastName { get; set; } public DateTime LastVisit { get; set; } }
var expectedObject = new ObjectToAssert { FirstName = "Vahid", LastName = "Mohammad Taheri", LastVisit = new DateTime( 2016, 11, 14, 0, 10, 50 ) }; var actualObject = new ObjectToAssert { FirstName = "Vahid", LastName = "Mohammad Taheri", LastVisit = new DateTime( 2016, 11, 14, 0, 13, 50 ) };
public class ObjectToAssertValidator : PropertiesValidator<ObjectToAssertValidator, ObjectToAssert> { public void Validate(ObjectToAssert expected, ObjectToAssert actual) { this.Validate(expected, actual, "FirstName"); } }
ObjectToAssertValidator.Instance.Validate(expectedObject, actualObject);
using FluentValidationSample.Models; using Microsoft.AspNetCore.Mvc; namespace FluentValidationSample.Web.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(); } [HttpPost] public IActionResult RegisterValidateManually(RegisterModel model) { var validator = new RegisterModelValidator(); var validationResult = validator.Validate(model); if (!validationResult.IsValid) { return BadRequest(validationResult.Errors[0].ErrorMessage); } // TODO: Save the model return Ok(); } } }
public IActionResult RegisterValidateManually(RegisterModel model) { var validator = new RegisterModelValidator(); var validationResult = validator.Validate(model); if (!validationResult.IsValid) { validationResult.AddToModelState(ModelState, null); return BadRequest(ModelState); } // TODO: Save the model return Ok(); }
namespace FluentValidationSample.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IValidator<RegisterModel>, RegisterModelValidator>(); services.AddControllersWithViews(); }
namespace FluentValidationSample.Web.Controllers { public class HomeController : Controller { private readonly IValidator<RegisterModel> _registerModelValidator; public HomeController(IValidator<RegisterModel> registerModelValidator) { _registerModelValidator = registerModelValidator; } [HttpPost] public IActionResult RegisterValidatorInjection(RegisterModel model) { var validationResult = _registerModelValidator.Validate(model); if (!validationResult.IsValid) { return BadRequest(validationResult.Errors[0].ErrorMessage); } // TODO: Save the model return Ok(); } } }
namespace FluentValidationSample.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<IValidator<RegisterModel>, RegisterModelValidator>(); services.AddControllersWithViews().AddFluentValidation(); }
namespace FluentValidationSample.Web.Controllers { public class HomeController : Controller { [HttpPost] public IActionResult RegisterValidatorAutomatically(RegisterModel model) { if (!ModelState.IsValid) { // re-render the view when validation failed. return View(model); } // TODO: Save the model return Ok(); } } }
namespace FluentValidationSample.Web.Controllers { [Route("[controller]")] [ApiController] public class HomeController : Controller { [HttpPost] public IActionResult RegisterValidatorAutomatically(RegisterModel model) { // TODO: Save the model return Ok(); } } }
{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "traceId": "|84df05e2-41e0d4841bb61293.", "errors": { "FirstName": [ "'First Name' must not be empty." ] } }
public void ConfigureServices(IServiceCollection services) { // ... // override modelstate services.Configure<ApiBehaviorOptions>(options => { options.InvalidModelStateResponseFactory = context => { var errors = context.ModelState.Values.SelectMany(x => x.Errors.Select(p => p.ErrorMessage)).ToList(); return new BadRequestObjectResult(new { Code = "00009", Message = "Validation errors", Errors = errors }); }; }); }
namespace FluentValidationSample.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews().AddFluentValidation( fv => fv.RegisterValidatorsFromAssemblyContaining<RegisterModelValidator>() ); }
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews().AddFluentValidation( fv => { fv.RegisterValidatorsFromAssemblyContaining<RegisterModelValidator>(); fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false; } ); }
new SendMsg().ShowMsg( new AlertConfirmBoxModel { ErrorTitle = "خطای اعتبار سنجی", Errors = errors, }, validationException);
public class DomainResult { public bool Succeed { get; set; } public IEnumerable<Exception> Errors { get; set; } public DomainErrorType ErrorType { get; set; } } public enum DomainErrorType { Validation, Concurrency, Update }
public DomainResult ApplyAllChanges(string userName, bool updateAuditFields = true) ...