public class Order { public int Id { get; set; } public Customer Customer { get; set; } } public class Customer { public int Id { get; set; } } ...... modelBuilder.Entity<Order>().HasRequired(ff => ff.Customer);
public class Order { public int Id { get; set; } public Customer Customer { get; set; } } public class Customer { public int Id { get; set; } } ...... modelBuilder.Entity<Order>().HasRequired(ff => ff.Customer);
var user = _documentSession .Include<User>(x => x.Apps[59].AddressId) .Load("Users/131-A"); var address = _documentSession.Load<Address>(user.Apps[59].AddressId)
var user = _documentSession .Include<User>(x => x.Apps.Values.Select(app => app.AddressId)) .Load("Users/131-A"); var addresses = List<Address>(); foreach(app in user.Apps) { addresses.Add(_documentSession.Load<Address>(app.AddressId)); //queryسمت کلاینت انجام اجرا میشود }
List<User> users = await _documentSession .Query<Users>() .Where(u => u.PhoneNumber.StartsWith("915")) .ToListAsync();
var users = await _documentSession.Query<AppUser>() .Where(u => u.Id.Equals("915")) .Select(u => new { u.Apps[appCode].FirstName, u.Apps [appCode].LastName, }) .ToListAsync();
from Users as user where startsWith(user.PhoneNumber, "915") select { FirstName : user.Apps ["59"].FirstName, LastName : user.Apps ["59"].LastName }
from u in _documentSession.Query<User>() where u.PhoneNumber.StartsWith("915") let app = u.Apps["59"] select new { app.FirstName, app.LastName, };
declare function output(u) { var app = u.Apps["59"]; return { FirstName : app.FirstName, LastName : app.LastName}; } from Users as user where startsWith(user.PhoneNumber, "915") select output(user)
app.FirstName, app.LastName, *key = u.ActiveInApps.Select(a => a.Key)
query = query.Search(u => u.key, "59");
public class User_MyIndex : AbstractIndexCreationTask<User> { Map = users => from u in users from app in u.Apps select new { Id = u.Id, PhoneNumber = u.PhoneNumber, UserName = app.Value.UserName, FirstName = app.Value.FirstName, LastName = app.Value.LastName, IsActive = app.Value.IsActive, key = app.Key }; }
new User_MyIndex().Execute(store);
IndexCreation.CreateIndexes(typeof(User_MyIndex).Assembly, store);
from u in _documentSession.Query<User, User_MyIndex>() ...
select new { ... key = aia.Key, Address = LoadDocument<Address>(aia.Value.AddressId), // City = LoadDocument<Address>(aia.Value.AddressId).City, };
Message = app.Messages.Select(m => LoadDocument<Message>(m).Content)
var users = _documentSession.Advanced.AsyncDocumentQuery<User, User_MyIndex>() .WhereStartsWith(nameof(AppUser.PhoneNumber), "915") .WhereEquals("key", appCode, exact: true) .SelectFields<AppUserModel>(new[] { $"Apps[{appCode}].FirstName", $"Apps[{appCode}].LastName" }) .ToListAsync();
public class Post { public int Id { get; set; } public string Content { get; set; } public string Title { get; set; } public List<string> Tags { get; set; } public string WriterName { get; set; } public string WriterId { get; set; } }
public class Post_ByContent : AbstractIndexCreationTask<Post> { public Post_ByContent() { Map = posts=> from post in posts select new { post.Content }; Analyzers.Add(p => p.Content, "StandardAnalyzer"); } }
List<Post> posts = _documentSession .Query<Post, Post_ByContent>() .MoreLikeThis(builder => builder .UsingDocument(p => p.Id == "posts/59-A") .WithOptions(new MoreLikeThisOptions { Fields = new[] { nameof(Post.Content) }, StopWordsDocumentId = "appConfig/StopWords" })) .ToList();
dotnet new api -n DotNetGraphQLClient
https://localhost:5003;http://localhost:5004
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "GraphQLURI": "https://localhost:5001/graphql", "AllowedHosts": "*" }
dotnet add package GraphQL.Client
public void ConfigureServices(IServiceCollection services) { services.AddScoped(x => new GraphQL.Client.GraphQLClient(Configuration["GraphQLURI"])); ... }
public class OwnerConsumer { private readonly GraphQL.Client.GraphQLClient _client; public OwnerConsumer(GraphQL.Client.GraphQLClient client) { _client = client; } }
public void ConfigureServices(IServiceCollection services) { services.AddScoped(x => new GraphQL.Client.GraphQLClient(Configuration["GraphQLURI"])); services.AddScoped<OwnerConsumer>(); ... }
public enum TypeOfAccount { Cash, Savings, Expense, Income }
public class Account { public Guid Id { get; set; } public TypeOfAccount Type { get; set; } public string Description { get; set; } }
public class Owner { public Guid Id { get; set; } public string Name { get; set; } public string Address { get; set; } public ICollection<Account> Accounts { get; set; } }
public class OwnerInput { public string Name { get; set; } public string Address { get; set; } }
public async Task<List<Owner>> GetAllOwners() { var query = new GraphQLRequest { Query = @" query ownersQuery{ owners { id name address accounts { id type description } } }" }; var response = await _client.PostAsync(query); return response.GetDataFieldAs<List<Owner>>("owners"); }
[Route("api/owners")] public class OwnerController: Controller { private readonly OwnerConsumer _consumer; public OwnerController(OwnerConsumer consumer) { _consumer = consumer; } [HttpGet] public async Task<IActionResult> Get() { var owners = await _consumer.GetAllOwners(); return Ok(owners); } }
public async Task<Owner> CreateOwner(OwnerInput ownerToCreate) { var query = new GraphQLRequest { Query = @" mutation($owner: ownerInput!){ createOwner(owner: $owner){ id, name, address } }", Variables = new { owner = ownerToCreate } }; var response = await _client.PostAsync(query); return response.GetDataFieldAs<Owner>("createOwner"); }
[HttpPost] public async Task<IActionResult> PostItem([FromBody]OwnerInput model) { var result = await _consumer.CreateOwner(model); return Json(result); }
public static void RegisterRoutes(RouteCollection routes) { ... routes.LowercaseUrls = true; ... }
using Microsoft.AspNetCore.Identity; namespace BlazorServer.Entities { public class ApplicationUser : IdentityUser { public string Name { get; set; } } }
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
namespace BlazorWasm.WebApi { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // ...
dotnet tool update --global dotnet-ef --version 5.0.4 dotnet build dotnet ef migrations --startup-project ../../BlazorWasm/BlazorWasm.WebApi/ add AddNameToAppUser --context ApplicationDbContext dotnet ef --startup-project ../../BlazorWasm/BlazorWasm.WebApi/ database update --context ApplicationDbContext
using System.ComponentModel.DataAnnotations; namespace BlazorServer.Models { public class UserRequestDTO { [Required(ErrorMessage = "Name is required")] public string Name { get; set; } [Required(ErrorMessage = "Email is required")] [RegularExpression("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$", ErrorMessage = "Invalid email address")] public string Email { get; set; } public string PhoneNo { get; set; } [Required(ErrorMessage = "Password is required.")] [DataType(DataType.Password)] public string Password { get; set; } [Required(ErrorMessage = "Confirm password is required")] [DataType(DataType.Password)] [Compare("Password", ErrorMessage = "Password and confirm password is not matched")] public string ConfirmPassword { get; set; } } }
public class RegistrationResponseDTO { public bool IsRegistrationSuccessful { get; set; } public IEnumerable<string> Errors { get; set; } }
using System; using BlazorServer.Entities; using BlazorServer.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using System.Linq; using BlazorServer.Common; namespace BlazorWasm.WebApi.Controllers { [Route("api/[controller]/[action]")] [ApiController] [Authorize] public class AccountController : ControllerBase { private readonly SignInManager<ApplicationUser> _signInManager; private readonly UserManager<ApplicationUser> _userManager; private readonly RoleManager<IdentityRole> _roleManager; public AccountController(SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager) { _roleManager = roleManager ?? throw new ArgumentNullException(nameof(roleManager)); _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); _signInManager = signInManager ?? throw new ArgumentNullException(nameof(signInManager)); } [HttpPost] [AllowAnonymous] public async Task<IActionResult> SignUp([FromBody] UserRequestDTO userRequestDTO) { var user = new ApplicationUser { UserName = userRequestDTO.Email, Email = userRequestDTO.Email, Name = userRequestDTO.Name, PhoneNumber = userRequestDTO.PhoneNo, EmailConfirmed = true }; var result = await _userManager.CreateAsync(user, userRequestDTO.Password); if (!result.Succeeded) { var errors = result.Errors.Select(e => e.Description); return BadRequest(new RegistationResponseDTO { Errors = errors, IsRegistrationSuccessful = false }); } var roleResult = await _userManager.AddToRoleAsync(user, ConstantRoles.Customer); if (!roleResult.Succeeded) { var errors = result.Errors.Select(e => e.Description); return BadRequest(new RegistationResponseDTO { Errors = errors, IsRegistrationSuccessful = false }); } return StatusCode(201); // Created } } }
[Route("api/[controller]/[action]")] [ApiController] [Authorize]
using System.ComponentModel.DataAnnotations; namespace BlazorServer.Models { public class AuthenticationDTO { [Required(ErrorMessage = "UserName is required")] [RegularExpression("^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$", ErrorMessage = "Invalid email address")] public string UserName { get; set; } [Required(ErrorMessage = "Password is required.")] [DataType(DataType.Password)] public string Password { get; set; } } }
using System.Collections.Generic; namespace BlazorServer.Models { public class AuthenticationResponseDTO { public bool IsAuthSuccessful { get; set; } public string ErrorMessage { get; set; } public string Token { get; set; } public UserDTO UserDTO { get; set; } } public class UserDTO { public string Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string PhoneNo { get; set; } } }
namespace BlazorServer.Models { public class BearerTokensOptions { public string Key { set; get; } public string Issuer { set; get; } public string Audience { set; get; } public int AccessTokenExpirationMinutes { set; get; } } }
{ "BearerTokens": { "Key": "This is my shared key, not so secret, secret!", "Issuer": "https://localhost:5001/", "Audience": "Any", "AccessTokenExpirationMinutes": 20 } }
namespace BlazorWasm.WebApi { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { services.AddOptions<BearerTokensOptions>().Bind(Configuration.GetSection("BearerTokens")); // ...
using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using BlazorServer.Entities; using BlazorServer.Models; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace BlazorServer.Services { public interface ITokenFactoryService { Task<string> CreateJwtTokensAsync(ApplicationUser user); } public class TokenFactoryService : ITokenFactoryService { private readonly UserManager<ApplicationUser> _userManager; private readonly BearerTokensOptions _configuration; public TokenFactoryService( UserManager<ApplicationUser> userManager, IOptionsSnapshot<BearerTokensOptions> bearerTokensOptions) { _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); if (bearerTokensOptions is null) { throw new ArgumentNullException(nameof(bearerTokensOptions)); } _configuration = bearerTokensOptions.Value; } public async Task<string> CreateJwtTokensAsync(ApplicationUser user) { var signingCredentials = new SigningCredentials( new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.Key)), SecurityAlgorithms.HmacSha256); var claims = await getClaimsAsync(user); var now = DateTime.UtcNow; var tokenOptions = new JwtSecurityToken( issuer: _configuration.Issuer, audience: _configuration.Audience, claims: claims, notBefore: now, expires: now.AddMinutes(_configuration.AccessTokenExpirationMinutes), signingCredentials: signingCredentials); return new JwtSecurityTokenHandler().WriteToken(tokenOptions); } private async Task<List<Claim>> getClaimsAsync(ApplicationUser user) { string issuer = _configuration.Issuer; var claims = new List<Claim> { // Issuer new Claim(JwtRegisteredClaimNames.Iss, issuer, ClaimValueTypes.String, issuer), // Issued at new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64, issuer), new Claim(ClaimTypes.Name, user.Email, ClaimValueTypes.String, issuer), new Claim(ClaimTypes.Email, user.Email, ClaimValueTypes.String, issuer), new Claim("Id", user.Id, ClaimValueTypes.String, issuer), new Claim("DisplayName", user.Name, ClaimValueTypes.String, issuer), }; var roles = await _userManager.GetRolesAsync(user); foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role, ClaimValueTypes.String, issuer)); } return claims; } } }
namespace BlazorWasm.WebApi { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { services.AddScoped<ITokenFactoryService, TokenFactoryService>(); // ...
namespace BlazorWasm.WebApi.Controllers { [Route("api/[controller]/[action]")] [ApiController] [Authorize] public class AccountController : ControllerBase { private readonly SignInManager<ApplicationUser> _signInManager; private readonly UserManager<ApplicationUser> _userManager; private readonly ITokenFactoryService _tokenFactoryService; public AccountController( SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager, ITokenFactoryService tokenFactoryService) { _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager)); _signInManager = signInManager ?? throw new ArgumentNullException(nameof(signInManager)); _tokenFactoryService = tokenFactoryService; } [HttpPost] [AllowAnonymous] public async Task<IActionResult> SignIn([FromBody] AuthenticationDTO authenticationDTO) { var result = await _signInManager.PasswordSignInAsync( authenticationDTO.UserName, authenticationDTO.Password, isPersistent: false, lockoutOnFailure: false); if (!result.Succeeded) { return Unauthorized(new AuthenticationResponseDTO { IsAuthSuccessful = false, ErrorMessage = "Invalid Authentication" }); } var user = await _userManager.FindByNameAsync(authenticationDTO.UserName); if (user == null) { return Unauthorized(new AuthenticationResponseDTO { IsAuthSuccessful = false, ErrorMessage = "Invalid Authentication" }); } var token = await _tokenFactoryService.CreateJwtTokensAsync(user); return Ok(new AuthenticationResponseDTO { IsAuthSuccessful = true, Token = token, UserDTO = new UserDTO { Name = user.Name, Id = user.Id, Email = user.Email, PhoneNo = user.PhoneNumber } }); } } }
{ "iss": "https://localhost:5001/", "iat": 1616396383, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "vahid@dntips.ir", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "vahid@dntips.ir", "Id": "582855fb-e95b-45ab-b349-5e9f7de40c0c", "DisplayName": "vahid@dntips.ir", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": "Admin", "nbf": 1616396383, "exp": 1616397583, "aud": "Any" }
namespace BlazorWasm.WebApi { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { var bearerTokensSection = Configuration.GetSection("BearerTokens"); services.AddOptions<BearerTokensOptions>().Bind(bearerTokensSection); // ... var apiSettings = bearerTokensSection.Get<BearerTokensOptions>(); var key = Encoding.UTF8.GetBytes(apiSettings.Key); services.AddAuthentication(opt => { opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; opt.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateAudience = true, ValidateIssuer = true, ValidAudience = apiSettings.Audience, ValidIssuer = apiSettings.Issuer, ClockSkew = TimeSpan.Zero, ValidateLifetime = true }; }); // ...
namespace BlazorWasm.WebApi { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { // ... services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "BlazorWasm.WebApi", Version = "v1" }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { In = ParameterLocation.Header, Description = "Please enter the token in the field", Name = "Authorization", Type = SecuritySchemeType.ApiKey }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] { } } }); }); } // ...
PM>Install-Package Autofac PM>Install-Package Autofac.Mvc5 PM>Install-Package AutoMapper
namespace AufacDI.DomainClasses { public class Category { public int Id { get; set; } public string Name { get; set; } } }
namespace AufacDI.ViewModel { public class CategoryViewModel { public int Id { get; set; } public int Name { get; set; } } }
using AufacDI.DomainClasses; using AufacDI.ViewModel; using AutoMapper; namespace AufacDI.MapperProfile { public class CategoryProfile : Profile { public CategoryProfile() { CreateMap<Category, CategoryViewModel>(); CreateMap<CategoryViewModel, Category>(); } } }
using AufacDI.MapperProfile; using Autofac; using AutoMapper; using System; using System.Linq; namespace AufacDI.IocConfig { public static class IoCContainer { public static void Register(ContainerBuilder builder) { // شناسایی پروفایلها براساس نمونه از کلاس پر.وفایل var profiles = from types in typeof(CategoryProfile).Assembly.GetTypes() where typeof(Profile).IsAssignableFrom(types) select (Profile)Activator.CreateInstance(types); // رجیستر کردن کلاسهای پروفایل در اتومپر builder.Register(ctx => new MapperConfiguration(cfg => { foreach (var profile in profiles) cfg.AddProfile(profile); })).SingleInstance().AutoActivate().AsSelf(); // رجیستر کردن کلاس MapperConfiguration و ایجاد آن براساس IMapper builder.Register(ctx => ctx.Resolve<MapperConfiguration>().CreateMapper()).As<IMapper>().InstancePerRequest(); } } }
var builder = new ContainerBuilder(); // تزریق کنترلرها برای تزریف سایر المانها در سازنده builder.RegisterControllers(typeof(MvcApplication).Assembly).InstancePerDependency(); // فراخوانی متد رجیستر برای تزریق وابستگی مپر و کلاسهای پروفایل آن IoCContainer.Register(builder); // ایجاد نمونه از سازنده var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
namespace AufacDI.WebApplication.Controllers { public class HomeController : Controller { private readonly IMapper _mapepr; public HomeController(IMapper mapepr) { _mapepr = mapepr; } public ActionResult Index() { // مپ کردن یک کلاس به یک کلاس var categoryViewModel = new CategoryViewModel { Id = 1, Name = "News" }; var categoryModel = _mapepr.Map<CategoryViewModel, Category>(categoryViewModel); // مپ کردن لیست از کلاس به لیستی از کلاس var categoryListModel = new List<Category>(); categoryListModel.Add(new Category { Id = 1, Name = "A" }); categoryListModel.Add(new Category { Id = 2, Name = "B" }); categoryListModel.Add(new Category { Id = 3, Name = "C" }); categoryListModel.Add(new Category { Id = 4, Name = "D" }); categoryListModel.Add(new Category { Id = 5, Name = "E" }); var categoryListViewModel = categoryListModel.AsQueryable().ProjectTo<CategoryViewModel>(_mapepr.ConfigurationProvider).ToList(); ; return View(); } } }
public class BaseEntity { public DateTimeOffset CreatedDate { get; set; } public DateTimeOffset UpdatedDate { get; set; } }
//override because we need add created and updated date to some entities public override async Task<int> SaveChangesAsync( CancellationToken cancellationToken = default(CancellationToken)) { AddCreatedUpdatedDate(); return (await base.SaveChangesAsync(true, cancellationToken)); } //override because we need add created and updated date to some entities public override int SaveChanges() { AddCreatedUpdatedDate(); return base.SaveChanges(); }
/// <summary> /// Add created and updated date to any entities that /// inherit from BaseEntity class /// </summary> public void AddCreatedUpdatedDate() { var entries = ChangeTracker .Entries() .Where(e => e.Entity is BaseEntity && ( e.State == EntityState.Added || e.State == EntityState.Modified)); foreach (var entityEntry in entries) { ((BaseEntity)entityEntry.Entity).UpdatedDate = DateTimeOffset.UtcNow; if (entityEntry.State == EntityState.Added) { ((BaseEntity)entityEntry.Entity).CreatedDate = DateTimeOffset.UtcNow; } } }
public class Student: BaseEntity { public int StudentID { get; set; } public string StudentName { get; set; } public DateTimeOffset? DateOfBirth { get; set; } public decimal Height { get; set; } public float Weight { get; set; } }
namespace EntitySample1.DomainClasses { public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } public virtual PersonInfo PersonInfo { get; set; } public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; } public virtual ICollection<Address> Addresses { get; set; } } }
namespace EntitySample1.DomainClasses { public class PersonInfo { public int Id { get; set; } public string Note { get; set; } public string Major { get; set; } } }
namespace EntitySample1.DomainClasses { public enum PhoneType { Home, Mobile, Work } public class PhoneNumber { public int Id { get; set; } public string Number { get; set; } public PhoneType PhoneType { get; set; } public virtual Person Person { get; set; } } }
namespace EntitySample1.DomainClasses { public class Address { public int Id { get; set; } public string City { get; set; } public string Street { get; set; } public virtual ICollection<Person> Persons { get; set; } } }
namespace EntitySample1.DataLayer { public class PhoneBookDbContext : DbContext { public DbSet<Person> Persons { get; set; } public DbSet<PhoneNumber> PhoneNumbers { get; set; } public DbSet<Address> Addresses { get; set; } } }
private static void ManualDetectChanges() { using (var context = new PhoneBookDbContext()) { context.Configuration.AutoDetectChangesEnabled = false; // turn off Auto Detect Changes var p1 = context.Persons.Single(p => p.FirstName == "joe"); p1.LastName = "Brown"; Console.WriteLine("Before DetectChanges: {0}", context.Entry(p1).State); context.ChangeTracker.DetectChanges(); // call detect changes manually Console.WriteLine("After DetectChanges: {0}", context.Entry(p1).State); } }
Before DetectChanges: Unchanged After DetectChanges: Modified
private static void AddMultiplePerson() { using (var context = new PhoneBookDbContext()) { context.Configuration.AutoDetectChangesEnabled = false; context.Persons.Add(new Person { FirstName = "brad", LastName = "watson", BirthDate = new DateTime(1990, 6, 8) }); context.Persons.Add(new Person { FirstName = "david", LastName = "brown", BirthDate = new DateTime(1990, 6, 8) }); context.Persons.Add(new Person { FirstName = "will", LastName = "smith", BirthDate = new DateTime(1990, 6, 8) }); context.SaveChanges(); } }
private static void DetectRelationshipChanges() { using (var context = new PhoneBookDbContext()) { var phone1 = context.PhoneNumbers.Single(x => x.Number == "09351234567"); var person1 = context.Persons.Single(x => x.FirstName == "will"); person1.PhoneNumbers.Add(phone1); Console.WriteLine("Before DetectChanges: {0}", phone1.Person.FirstName); context.ChangeTracker.DetectChanges(); // ralationships fix-up Console.WriteLine("After DetectChanges: {0}", phone1.Person.FirstName); } }
Before DetectChanges: david After DetectChanges: will
تا قبل از فراخوانی تشخیص تغییرات(DetectChanegs)، هنوز phone1 منتسب به شخص قدیمی(david) بوده، ولی پس از فراخوانی DetectChanges ، اصلاح رابطه رخ داده و همه چیز با یکدیگر منطبق میشوند.
فعال سازی و کار با پروکسیهای ردیابی تغییر
اگر پروفایلر کارایی شما، فراخوانیهای بیش از اندازه DetectChnages را به عنوان یک مشکل شناسایی کند، و یا شما ترجیح میدهید که اصلاح رابطه به صورت بلادرنگ صورت گیرد ، ردیابی تغییر پروکسیهای پویا، به عنوان گزینه ای دیگر مطرح میشود.فقط با چند تغییر کوچک در کلاسهای POCO، EF قادر به ساخت پروکسیهای پویا خواهد بود.پروکسیهای ردیابی تغییر به EF اجازه ردیابی تغییرات در همان لحظه ای که ما تغییری در اشیای خود میدهیم را میدهند و همچنین امکان انجام اصلاح رابطه را در هر زمانی که تغییرات روابط را تشخیص دهد، دارد.
برای اینکه پروکسی ردیابی تغییر بتواند ساخته شود، باید قوانین زیر رعایت شود:
• کلاس باید public باشد و seald نباشد.
• همهی خواص(properties) باید virtual تعریف شوند.
• همهی خواص باید getter و setter با سطح دسترسی public داشته باشند.
• همهی خواص راهبری مجموعه ای باید نوعشان، از نوع ICollection<T> تعریف شوند.
کلاس Person مثال خود را به گونه ای بازنویسی کرده ایم که تمام قوانین فوق را پیاده سازی کرده باشد.
نکته: توجه داشته باشید که ما دیگر در داخل سازنده کلاس ،کدی نمینویسیم و منطقی که باعث نمونه سازی اولیه خواص راهبری میشدند، را پیاده سازی نمیکنیم. این پروکسی ردیاب تغییر، همهی خواص راهبری مجموعه ای را تحریف کرده و ار نوع مجموعه ای مخصوص خود(EntityCollection<T>) استفاده میکند. این نوع مجموعه ای، هر تغییری که در این مجموعه صورت میگیرد را زیر نظر گرفته و به ردیاب تغییر گزارش میدهد. اگر تلاش کنید تا نوع دیگری مانند List<T> که معمولا در سازنده کلاس از آن استفاده میکردیم را به آن انتساب دهیم، پروکسی، استثنایی را پرتاب میکند.
namespace EntitySample1.DomainClasses { public class Person { public virtual int Id { get; set; } public virtual string FirstName { get; set; } public virtual string LastName { get; set; } public virtual DateTime BirthDate { get; set; } public virtual PersonInfo PersonInfo { get; set; } public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; } public virtual ICollection<Address> Addresses { get; set; } } }
private static void TestForChangeTrackingProxy() { using (var context = new PhoneBookDbContext()) { var person = context.Persons.First(); var isProxy = person is IEntityWithChangeTracker; Console.WriteLine("person is a proxy: {0}", isProxy); } }
Before DetectChanges: Modified After DetectChanges: Modified
Before DetectChanges: will After DetectChanges: will
private static void CreateNewProxies() { using (var context = new PhoneBookDbContext()) { var phoneNumber = new PhoneNumber { Number = "987" }; var davidPersonProxy = context.Persons.Create(); davidPersonProxy.FirstName = "david"; davidPersonProxy.PhoneNumbers.Add(phoneNumber); Console.WriteLine(phoneNumber.Person.FirstName); } }
var newStudent = context.Persons.Create<Student>();
private static void PrintPersonsWithoutChangeTracking() { using (var context = new PhoneBookDbContext()) { var persons = context.Persons.AsNoTracking().ToList(); foreach (var person in persons) { Console.WriteLine(person.FirstName); } } }
var query = from p in context.Persons.AsNoTracking() where p.FirstName == "joe" select p;
شما همچنین از AsNoTracking میتوانید برای تبدیل یک کوئری LINQ موجود، به یک کوئری فاقد ردیابی استفاده کنید. این نکته را به یاد داشته باشید که فقط AsNoTracking بر روی کوئری، فرانخوانده شده است، بلکه متغیر query را با نتیجهی حاصل از فراخوانی AsNoTracking بازنویسی(override) کرده است و این، از این جهت لازم است که AsNoTracking ،تغییری در کوئری ای که بر روی آن فراخوانده شده نمیدهد، بلکه یک کوئری جدید بر میگرداند.
var query = from p in context.Persons where p.FirstName == "joe" select p; query = query.AsNoTracking();
<!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/Site.css" rel="stylesheet" type="text/css" /> </head> <body> <div> @RenderBody() </div> <script src="~/Scripts/jquery-1.11.1.min.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script> <script src="~/Scripts/ajaxfileupload.js"></script> @RenderSection("Scripts", required: false) </body> </html>
namespace MVCAjaxFormUpload.Models { public class Product { public int Id { set; get; } public string Name { set; get; } } }
using System.Threading; using System.Web; using System.Web.Mvc; using MVCAjaxFormUpload.Models; namespace MVCAjaxFormUpload.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(Product product) { var isAjax = this.Request.IsAjaxRequest(); return Json(new { result = "ok" }, JsonRequestBehavior.AllowGet); } [HttpPost] public ActionResult UploadFiles(HttpPostedFileBase image1, int id) { var isAjax = this.Request.IsAjaxRequest(); Thread.Sleep(3000); //شبیه سازی عملیات طولانی return Json(new { FileName = "/Uploads/filename.ext" }, "text/html", JsonRequestBehavior.AllowGet); } } }
@model MVCAjaxFormUpload.Models.Product @{ ViewBag.Title = "Index"; } <h2>Ajax Form Upload</h2> @using (Ajax.BeginForm(actionName: "Index", controllerName: "Home", ajaxOptions: new AjaxOptions { HttpMethod = "POST" }, routeValues: null, htmlAttributes: new { id = "uploadForm" })) { <label>Name:</label> @Html.TextBoxFor(model => model.Name) <br /> <label>Image:</label> <br /> <input type="file" name="Image1" id="Image1" /> <br /> <input type="submit" value="Submit" /> <img id="loading" src="~/Content/Images/loading.gif" style="display:none;"> } @section Scripts { <script type="text/javascript"> $(function () { $('#uploadForm').submit(function () { $("#loading").show(); $.ajaxFileUpload({ url: "@Url.Action("UploadFiles", "Home")", // مسیری که باید فایل به آن ارسال شود secureuri: false, fileElementId: 'Image1', // آی دی المان ورودی فایل dataType: 'json', data: { id: 1, data: 'test' }, // اطلاعات اضافی در صورت نیاز success: function (data, status) { $("#loading").hide(); if (typeof (data.FileName) != 'undefined') { alert(data.FileName); } }, error: function (data, status, e) { $("#loading").hide(); alert(e); } }); }); }); </script> }