همچنین باید درنظر داشت، ASP.NET Core Identity یک سیستم اعتبارسنجی مبتنی بر کوکیها است. دقیقا زمانیکه کار AddIdentity را انجام میدهیم، در پشت صحنه همان services.AddAuthentication().AddCookie قسمت قبل فراخوانی میشود. بنابراین بکارگیری آن با JSON Web Tokens هرچند مشکلی را به همراه ندارد و میتوان یک سیستم اعتبارسنجی «دوگانه» را نیز در اینجا داشت، اما ... سربار اضافی تولید کوکیها را نیز به همراه دارد؛ هرچند برای کار با میانافزار اعتبارسنجی، الزامی به استفادهی از ASP.NET Core Identity نیست و عموما اگر از آن به همراه JWT استفاده میکنند، بیشتر به دنبال پیاده سازیهای پیشفرض مدیریت کاربران و نقشهای آن هستند و نه قسمت تولید کوکیهای آن. البته در مطلب جاری این موارد را نیز همانند مطلب اعتبارسنجی مبتنی بر کوکیها، خودمان مدیریت خواهیم کرد و در نهایت سیستم تهیه شده، هیچ نوع کوکی را تولید و یا مدیریت نمیکند.
تنظیمات آغازین برنامه جهت فعالسازی اعتبارسنجی مبتنی بر JSON Web Tokens
اولین تفاوت پیاده سازی یک سیستم اعتبارسنجی مبتنی بر JWT، با روش مبتنی بر کوکیها، تنظیمات متد ConfigureServices فایل آغازین برنامه است:
public void ConfigureServices(IServiceCollection services) { services.Configure<BearerTokensOptions>(options => Configuration.GetSection("BearerTokens").Bind(options)); services .AddAuthentication(options => { options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(cfg => { cfg.RequireHttpsMetadata = false; cfg.SaveToken = true; cfg.TokenValidationParameters = new TokenValidationParameters { ValidIssuer = Configuration["BearerTokens:Issuer"], ValidAudience = Configuration["BearerTokens:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["BearerTokens:Key"])), ValidateIssuerSigningKey = true, ValidateLifetime = true, ClockSkew = TimeSpan.Zero }; cfg.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents)); logger.LogError("Authentication failed.", context.Exception); return Task.CompletedTask; }, OnTokenValidated = context => { var tokenValidatorService = context.HttpContext.RequestServices.GetRequiredService<ITokenValidatorService>(); return tokenValidatorService.ValidateAsync(context); }, OnMessageReceived = context => { return Task.CompletedTask; }, OnChallenge = context => { var logger = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(nameof(JwtBearerEvents)); logger.LogError("OnChallenge error", context.Error, context.ErrorDescription); return Task.CompletedTask; } }; });
{ "BearerTokens": { "Key": "This is my shared key, not so secret, secret!", "Issuer": "http://localhost/", "Audience": "Any", "AccessTokenExpirationMinutes": 2, "RefreshTokenExpirationMinutes": 60 } }
سپس کار فراخوانی services.AddAuthentication صورت گرفتهاست. تفاوت این مورد با حالت اعتبارسنجی مبتنی بر کوکیها، ثوابتی است که با JwtBearerDefaults شروع میشوند. در حالت استفادهی از کوکیها، این ثوابت بر اساس CookieAuthenticationDefaults تنظیم خواهند شد.
البته میتوان متد AddAuthentication را بدون هیچگونه پارامتری نیز فراخوانی کرد. این حالت برای اعتبارسنجیهای دوگانه مفید است. برای مثال زمانیکه پس از AddAuthentication هم AddJwtBearer را ذکر کردهاید و هم AddCookie اضافه شدهاست. اگر چنین کاری را انجام دادید، اینبار باید درحین تعریف فیلتر Authorize، دقیقا مشخص کنید که حالت مبتنی بر JWT مدنظر شما است، یا حالت مبتنی بر کوکیها:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
بررسی تنظیمات متد AddJwtBearer
در کدهای فوق، تنظیمات متد AddJwtBearer یک چنین مفاهیمی را به همراه دارند:
- تنظیم SaveToken به true، به این معنا است که میتوان به توکن دریافتی از سمت کاربر، توسط متد HttpContext.GetTokenAsync در کنترلرهای برنامه دسترسی یافت.
در قسمت تنظیمات TokenValidationParameters آن:
- کار خواندن فایل appsettings.json برنامه جهت تنظیم صادر کننده و مخاطبین توکن انجام میشود. سپس IssuerSigningKey به یک کلید رمزنگاری متقارن تنظیم خواهد شد. این کلید نیز در تنظیمات برنامه قید میشود.
- تنظیم ValidateIssuerSigningKey به true سبب خواهد شد تا میانافزار اعتبارسنجی، بررسی کند که آیا توکن دریافتی از سمت کاربر توسط برنامهی ما امضاء شدهاست یا خیر؟
- تنظیم ValidateLifetime به معنای بررسی خودکار طول عمر توکن دریافتی از سمت کاربر است. اگر توکن منقضی شده باشد، اعتبارسنجی به صورت خودکار خاتمه خواهد یافت.
- ClockSkew به معنای تنظیم یک تلرانس و حد تحمل مدت زمان منقضی شدن توکن در حالت ValidateLifetime است. در اینجا به صفر تنظیم شدهاست.
سپس به قسمت JwtBearerEvents میرسیم:
- OnAuthenticationFailed زمانی فراخوانی میشود که اعتبارسنجهای تنظیمی فوق، با شکست مواجه شوند. برای مثال طول عمر توکن منقضی شده باشد و یا توسط ما امضاء نشدهباشد. در اینجا میتوان به این خطاها دسترسی یافت و درصورت نیاز آنها را لاگ کرد.
- OnChallenge نیز یک سری دیگر از خطاهای اعتبارسنجی را پیش از ارسال آنها به فراخوان در اختیار ما قرار میدهد.
- OnMessageReceived برای حالتی است که توکن دریافتی، توسط هدر مخصوص Bearer به سمت سرور ارسال نمیشود. عموما هدر ارسالی به سمت سرور یک چنین شکلی را دارد:
$.ajax({ headers: { 'Authorization': 'Bearer ' + jwtToken },
const string tokenKey = "my.custom.jwt.token.key"; if (context.HttpContext.Items.ContainsKey(tokenKey)) { context.Token = (string)context.HttpContext.Items[tokenKey]; }
تهیه یک اعتبارسنج توکن سفارشی
قسمت OnTokenValidated تنظیمات ابتدای برنامه به این صورت مقدار دهی شدهاست:
OnTokenValidated = context => { var tokenValidatorService = context.HttpContext.RequestServices.GetRequiredService<ITokenValidatorService>(); return tokenValidatorService.ValidateAsync(context); },
public class TokenValidatorService : ITokenValidatorService { private readonly IUsersService _usersService; private readonly ITokenStoreService _tokenStoreService; public TokenValidatorService(IUsersService usersService, ITokenStoreService tokenStoreService) { _usersService = usersService; _usersService.CheckArgumentIsNull(nameof(usersService)); _tokenStoreService = tokenStoreService; _tokenStoreService.CheckArgumentIsNull(nameof(_tokenStoreService)); } public async Task ValidateAsync(TokenValidatedContext context) { var userPrincipal = context.Principal; var claimsIdentity = context.Principal.Identity as ClaimsIdentity; if (claimsIdentity?.Claims == null || !claimsIdentity.Claims.Any()) { context.Fail("This is not our issued token. It has no claims."); return; } var serialNumberClaim = claimsIdentity.FindFirst(ClaimTypes.SerialNumber); if (serialNumberClaim == null) { context.Fail("This is not our issued token. It has no serial."); return; } var userIdString = claimsIdentity.FindFirst(ClaimTypes.UserData).Value; if (!int.TryParse(userIdString, out int userId)) { context.Fail("This is not our issued token. It has no user-id."); return; } var user = await _usersService.FindUserAsync(userId).ConfigureAwait(false); if (user == null || user.SerialNumber != serialNumberClaim.Value || !user.IsActive) { // user has changed his/her password/roles/stat/IsActive context.Fail("This token is expired. Please login again."); } var accessToken = context.SecurityToken as JwtSecurityToken; if (accessToken == null || string.IsNullOrWhiteSpace(accessToken.RawData) || !await _tokenStoreService.IsValidTokenAsync(accessToken.RawData, userId).ConfigureAwait(false)) { context.Fail("This token is not in our database."); return; } await _usersService.UpdateUserLastActivityDateAsync(userId).ConfigureAwait(false); } }
- آیا توکن دریافتی به همراه Claims تنظیم شدهی درحین لاگین هست یا خیر؟
- آیا توکن دریافتی دارای یک Claim سفارشی به نام SerialNumber است؟ این SerialNumber معادل چنین فیلدی در جدول کاربران است.
- آیا توکن دریافتی دارای user-id است؟
- آیا کاربر یافت شدهی بر اساس این user-id هنوز فعال است و یا اطلاعات او تغییر نکردهاست؟
- همچنین در آخر کار بررسی میکنیم که آیا اصل توکن دریافتی، در بانک اطلاعاتی ما پیشتر ثبت شدهاست یا خیر؟
اگر خیر، بلافاصله متد context.Fail فراخوانی شده و کار اعتبارسنجی را با اعلام شکست آن، به پایان میرسانیم.
در قسمت آخر، نیاز است اطلاعات توکنهای صادر شده را ذخیره کنیم. به همین جهت نسبت به مطلب قبلی، جدول UserToken ذیل به برنامه اضافه شدهاست:
public class UserToken { public int Id { get; set; } public string AccessTokenHash { get; set; } public DateTimeOffset AccessTokenExpiresDateTime { get; set; } public string RefreshTokenIdHash { get; set; } public DateTimeOffset RefreshTokenExpiresDateTime { get; set; } public int UserId { get; set; } // one-to-one association public virtual User User { get; set; } }
از اطلاعات آن در دو قسمت TokenValidatorService فوق و همچنین قسمت logout برنامه استفاده میکنیم. در سیستم JWT، مفهوم logout سمت سرور وجود خارجی ندارد. اما با ذخیره سازی هش توکنها در بانک اطلاعاتی میتوان لیستی از توکنهای صادر شدهی توسط برنامه را تدارک دید. سپس در حین logout فقط کافی است tokenهای یک کاربر را حذف کرد. همینقدر سبب خواهد شد تا قسمت آخر TokenValidatorService با شکست مواجه شود؛ چون توکن ارسالی به سمت سرور دیگر در بانک اطلاعاتی وجود ندارد.
سرویس TokenStore
public interface ITokenStoreService { Task AddUserTokenAsync(UserToken userToken); Task AddUserTokenAsync( User user, string refreshToken, string accessToken, DateTimeOffset refreshTokenExpiresDateTime, DateTimeOffset accessTokenExpiresDateTime); Task<bool> IsValidTokenAsync(string accessToken, int userId); Task DeleteExpiredTokensAsync(); Task<UserToken> FindTokenAsync(string refreshToken); Task DeleteTokenAsync(string refreshToken); Task InvalidateUserTokensAsync(int userId); Task<(string accessToken, string refreshToken)> CreateJwtTokens(User user); }
پیاده سازی کامل این سرویس را در اینجا میتوانید مشاهده کنید.
تولید Access Tokens و Refresh Tokens
پس از تنظیمات ابتدایی برنامه، اکنون میتوانیم دو نوع توکن را تولید کنیم:
تولید Access Tokens
private async Task<string> createAccessTokenAsync(User user, DateTime expires) { var claims = new List<Claim> { // Unique Id for all Jwt tokes new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), // Issuer new Claim(JwtRegisteredClaimNames.Iss, _configuration.Value.Issuer), // Issued at new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToUnixEpochDate().ToString(), ClaimValueTypes.Integer64), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.Name, user.Username), new Claim("DisplayName", user.DisplayName), // to invalidate the cookie new Claim(ClaimTypes.SerialNumber, user.SerialNumber), // custom data new Claim(ClaimTypes.UserData, user.Id.ToString()) }; // add roles var roles = await _rolesService.FindUserRolesAsync(user.Id).ConfigureAwait(false); foreach (var role in roles) { claims.Add(new Claim(ClaimTypes.Role, role.Name)); } var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.Value.Key)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _configuration.Value.Issuer, audience: _configuration.Value.Audience, claims: claims, notBefore: DateTime.UtcNow, expires: expires, signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); }
<ItemGroup> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" /> </ItemGroup>
پس از تهیهی Claims، اینبار بجای یک کوکی، یک JSON Web Toekn را توسط متد new JwtSecurityTokenHandler().WriteToken تهیه خواهیم کرد. این توکن حاوی Claims، به همراه اطلاعات طول عمر و امضای مرتبطی است.
حاصل آن نیز یک رشتهاست که دقیقا به همین فرمت به سمت کلاینت ارسال خواهد شد. البته ما در اینجا دو نوع توکن را به سمت کلاینت ارسال میکنیم:
public async Task<(string accessToken, string refreshToken)> CreateJwtTokens(User user) { var now = DateTimeOffset.UtcNow; var accessTokenExpiresDateTime = now.AddMinutes(_configuration.Value.AccessTokenExpirationMinutes); var refreshTokenExpiresDateTime = now.AddMinutes(_configuration.Value.RefreshTokenExpirationMinutes); var accessToken = await createAccessTokenAsync(user, accessTokenExpiresDateTime.UtcDateTime).ConfigureAwait(false); var refreshToken = Guid.NewGuid().ToString().Replace("-", ""); await AddUserTokenAsync(user, refreshToken, accessToken, refreshTokenExpiresDateTime, accessTokenExpiresDateTime).ConfigureAwait(false); await _uow.SaveChangesAsync().ConfigureAwait(false); return (accessToken, refreshToken); }
جهت بالا رفتن امنیت سیستم، این Guid را هش کرد و سپس این هش را در بانک اطلاعاتی ذخیره میکنیم. به این ترتیب دسترسی غیرمجاز به این هشها، امکان بازیابی توکنهای اصلی را غیرممکن میکند.
پیاده سازی Login
پس از پیاده سازی متد CreateJwtTokens، کار ورود به سیستم به سادگی ذیل خواهد بود:
[AllowAnonymous] [HttpPost("[action]")] public async Task<IActionResult> Login([FromBody] User loginUser) { if (loginUser == null) { return BadRequest("user is not set."); } var user = await _usersService.FindUserAsync(loginUser.Username, loginUser.Password).ConfigureAwait(false); if (user == null || !user.IsActive) { return Unauthorized(); } var (accessToken, refreshToken) = await _tokenStoreService.CreateJwtTokens(user).ConfigureAwait(false); return Ok(new { access_token = accessToken, refresh_token = refreshToken }); }
پیاده سازی Refresh Token
پیاده سازی توکن به روز رسانی همانند عملیات لاگین است:
[AllowAnonymous] [HttpPost("[action]")] public async Task<IActionResult> RefreshToken([FromBody]JToken jsonBody) { var refreshToken = jsonBody.Value<string>("refreshToken"); if (string.IsNullOrWhiteSpace(refreshToken)) { return BadRequest("refreshToken is not set."); } var token = await _tokenStoreService.FindTokenAsync(refreshToken); if (token == null) { return Unauthorized(); } var (accessToken, newRefreshToken) = await _tokenStoreService.CreateJwtTokens(token.User).ConfigureAwait(false); return Ok(new { access_token = accessToken, refresh_token = newRefreshToken }); }
پیاده سازی Logout
در سیستمهای مبتنی بر JWT، پیاده سازی Logout سمت سرور بیمفهوم است؛ از این جهت که تا زمان انقضای یک توکن میتوان از آن توکن جهت ورود به سیستم و دسترسی به منابع آن استفاده کرد. بنابراین تنها راه پیاده سازی Logout، ذخیره سازی توکنها در بانک اطلاعاتی و سپس حذف آنها در حین خروج از سیستم است. به این ترتیب اعتبارسنج سفارشی توکنها، از استفادهی مجدد از توکنی که هنوز هم معتبر است و منقضی نشدهاست، جلوگیری خواهد کرد:
[AllowAnonymous] [HttpGet("[action]"), HttpPost("[action]")] public async Task<bool> Logout() { var claimsIdentity = this.User.Identity as ClaimsIdentity; var userIdValue = claimsIdentity.FindFirst(ClaimTypes.UserData)?.Value; // The Jwt implementation does not support "revoke OAuth token" (logout) by design. // Delete the user's tokens from the database (revoke its bearer token) if (!string.IsNullOrWhiteSpace(userIdValue) && int.TryParse(userIdValue, out int userId)) { await _tokenStoreService.InvalidateUserTokensAsync(userId).ConfigureAwait(false); } await _tokenStoreService.DeleteExpiredTokensAsync().ConfigureAwait(false); await _uow.SaveChangesAsync().ConfigureAwait(false); return true; }
آزمایش نهایی برنامه
در فایل index.html، نمونهای از متدهای لاگین، خروج و فراخوانی اکشن متدهای محافظت شده را مشاهده میکنید. این روش برای برنامههای تک صفحهای وب یا SPA نیز میتواند مفید باشد و به همین نحو کار میکنند.
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید.
- مشاهده صفحات طراحی شده در چند مرورگر - بازار آی تی | itbazaar.persianblog.ir
- ASP.NET 4.5 خلاصه تغییرات| mojtabasahraei.blogfa.com
- The Visual Studio Async CTP3 | www.microsoft.com
- توضیحاتی در مورد Updated Async CTP | blogs.msdn.com
- کار با JSON در دات نت | www.codeproject.com
- مثالهایی از Commands در الگوی MVVM | www.codeproject.com
- مثالی از کاربرد Expression Trees | www.codeproject.com
- نسخه بعدی ویژوال استودیو و debugging Contracts | blogs.msdn.com
- نکاتی در مورد نصب Async CTP v3 | blogs.msdn.com
در این ویدیوی آموزشی، شما با مفاهیم و تکنیکهای کلیدی برنامهنویسی با زبان پایتون آشنا میشوید. در فقط 3 ساعت، از مبانی تا پیشرفتهترین قسمتهای این زبان برنامهنویسی محبوب و قدرتمند را بیاموزید.
مباحث:
00:00:00 مقدمه
00:04:14 نصب پایتون
00:17:06 vscode آموزش استفاده از
00:26:13 آموزش استفاده از جوپیتر نوت بوک
00:37:12 متغیرهای متنی
00:47:39 متغیرهای عددی
00:54:22 لیست، تاپل، ست
01:15:44 دیکشنری
01:24:29 شرط ها
01:37:46 پروژه اول-ساخت ماشین حساب ساده
01:44:15 حلقه ها
01:55:06 توابع
02:11:59 خواندن و نوشتن فایلها
02:22:11 استفاده از پکیجها
02:34:10 استفاده از محیط مجازی
02:45:20 کلاسها و آبجکتها
03:03:08 پروژه دوم- ساخت بازی کوئیز
NoSQL و مایکروسافت
1) Azure table storage
Azure table storage در حقیقت یک Key-value store ابری است و برای کار با آن از اینترفیس پروتکل استاندارد OData استفاده میشود. علت استفاده و طراحی یک سیستم Key-value store در اینجا، مناسب بودن اینگونه سیستمها جهت مقاصد عمومی است و به این ترتیب میتوان به بازه بیشتری از مصرف کنندگان، خدمات ارائه داد.
پیش از ارائه Azure table storage، مایکروسافت سرویس خاصی را به نام SQL Server Data Services که به آن SQL Azure نیز گفته میشود، معرفی کرد. این سرویس نیز یک Key-Value store است؛ هرچند از SQL Server به عنوان مخزن نگهداری اطلاعات آن استفاده میکند.
2) SQL Azure XML Columns
فیلدهای XML از سال 2005 به امکانات توکار SQL Server اضافه شدند و این نوع فیلدها، بسیاری از مزایای دنیای NoSQL را درون SQL Server رابطهای مهیا میسازند. برای مثال با تعریف یک فیلد به صورت XML، میتوان از هر ردیف به ردیفی دیگر، اطلاعات متفاوتی را ذخیره کرد؛ به این ترتیب امکان کار با یک فیلد که میتواند اطلاعات یک شیء را قبول کند و در حقیقت امکان تعریف اسکیمای پویا و متغیر را در کنار امکانات یک بانک اطلاعاتی رابطهای که از اسکیمای ثابت پشتیبانی میکند، میسر میشود. در این حالت در هر ردیف میتوان تعدادی ستون ثابت را با یک ستون XML با اسکیمای کاملا پویا ترکیب کرد.
همچنین SQL Server در این حالت قابلیتی را ارائه میدهد که در بسیاری از بانکهای اطلاعاتی NoSQL میسر نیست. در اینجا در صورت نیاز و لزوم میتوان اسکیمای کاملا مشخصی را به یک فیلد XML نیز انتساب داد؛ هر چند این مورد اختیاری است و میتوان یک un typed XML را نیز بکار برد. به علاوه امکانات کوئری گرفتن توکار از این اطلاعات را به کمک XPath ترکیب شده با T-SQL، نیز فراموش نکنید.
بنابراین اگر یکی از اهداف اصلی گرایش شما به سمت دنیای NoSQL، استفاده از امکان تعریف اطلاعاتی با اسکیمای متغیر و پویا است، فیلدهای نوع XML اس کیوال سرور را مدنظر داشته باشید.
یک مثال عملی: فناوری Azure Dev Fabric's Table Storage (نسخه Developer ویندوز Azure که روی ویندوزهای معمولی اجرا میشود؛ یک شبیه ساز خانگی) به کمک SQL Server و فیلدهای XML آن طراحی شده است.
3) SQL Azure Federations
در اینجا منظور از Federations در حقیقت همان پیاده سازی قابلیت Sharding بانکهای اطلاعاتی NoSQL توسط SQL Azure است که برای توزیع اطلاعات بر روی سرورهای مختلف طراحی شده است. به این ترتیب دو قابلیت Partitioning و همچنین Replication به صورت خودکار در دسترس خواهند بود. هر Partition در اینجا، یک SQL Azure کامل است. بنابراین چندین بانک اطلاعاتی فیزیکی، یک بانک اطلاعاتی کلی را تشکیل خواهند داد.
هرچند در اینجا Sharding (که به آن Federation member گفته میشود) و در پی آن مفهوم «عاقبت یک دست شدن اطلاعات» وجود دارد، اما درون یک Shard یا یک Federation member، مفهوم ACID پیاده سازی شده است. از این جهت که هر Shard واقعا یک بانک اطلاعاتی رابطهای است. اینجا است که مفهوم برنامههای Multi-tenancy را برای درک آن باید درنظر داشت. برای نمونه یک برنامه وب را درنظر بگیرید که قسمت اصلی اطلاعات کاربران آن بر روی یک Shard قرار دارد و سایر اطلاعات بر روی سایر Shards پراکنده شدهاند. در این حالت است که یک برنامه وب با وجود مفهوم ACID در یک Shard میتواند سریع پاسخ دهد که آیا کاربری پیشتر در سایت ثبت نام کرده است یا خیر و از ثبت نامهای غیرمجاز جلوگیری به عمل آورد.
در اینجا تنها موردی که پشتیبانی نشدهاست، کوئریهای Fan-out میباشد که پیشتر در مورد آن بحث شد. از این جهت که با نحوه خاصی که Sharding آن طراحی شده است، نیازی به تهیه کوئریهایی که به صورت موازی بر روی کلیه Shards برای جمع آوری اطلاعات اجرا میشوند، نیست. هر چند از هر shard با استفاده از برنامههای دات نت، میتوان به صورت جداگانه نیز کوئری گرفت.
4) OData
اگر به CouchDB و امکان دسترسی به امکانات آن از طریق وب دقت کنید، در محصولات مایکروسافت نیز این دسترسی REST API پیاده سازی شدهاند.
OData یک RESTful API است برای دسترسی به اطلاعاتی که به شکل XML یا JSON بازگشت داده میشوند. انواع و اقسام کلاینتهایی برای کار با آن از جاوا اسکریپت گرفته تا سیستمهای موبایل، دات نت و جاوا، وجود دارند. از این API نه فقط برای خواندن اطلاعات، بلکه برای ثبت و به روز رسانی دادهها نیز استفاده میشود. در سیستمهای جاری مایکروسافت، بسیاری از فناوریها، اطلاعات خود را به صورت OData دراختیار مصرف کنندگان قرار میدهند مانند Azure table storage، کار با SQL Azure از طریق WCF Data Services (جایی که OData از آن نشات گرفته شده)، Azure Data Market (برای ارائه فیدهایی از اطلاعات خصوصا رایگان)، ابزارهای گزارشگیری مانند SQL Server reporting services، لیستهای شیرپوینت و غیره.
به این ترتیب به بسیاری از قابلیتهای دنیای NoSQL مانند کار با اطلاعات JSON بدون ترک دنیای رابطهای میتوان دسترسی داشت.
5) امکان اجرای MongoDB و امثال آن روی سکوی کاری Azure
امکان توزیع MongoDB بر روی یک Worker role سکوی کاری Azure وجود دارد. در این حالت بانکهای اطلاعاتی این سیستمها بر روی Azure Blob Storage قرار میگیرند که به آنها Azure drive نیز گفته میشود. همین روش برای سایر بانکهای اطلاعاتی NoSQL نیز قابل اجرا است.
به علاوه امکان اجرای Hadoop نیز بر روی Azure وجود دارد. مایکروسافت به کمک شرکتی به نام HortonWorks نسخه ویندوزی Hadoop را توسعه دادهاند. HortonWorks را افرادی تشکیل دادهاند که پیشتر در شرکت یاهو بر روی پروژه Hadoop کار میکردهاند.
6) قابلیتهای فرا رابطهای SQL Server
الف) فیلدهای XML (که در ابتدای این مطلب به آن پرداخته شد). به این ترتیب میتوان به یک اسکیمای انعطاف پذیر، بدون از دست دادن ضمانت ACID رسید.
ب) فیلد HierarchyId برای ذخیره سازی اطلاعات چند سطحی. برای مثال در بانکهای اطلاعاتی NoSQL سندگرا، یک سند میتواند سند دیگری را در خود ذخیره کند و الی آخر.
ج) Sparse columns؛ ستونهای اسپارس تقریبا شبیه به Key-value stores عمل میکنند و یا حتی Wide column stores نیز با آن قابل مقایسه است. در اینجا هنوز اسکیما وجود دارد، اما برای نمونه علت استفاده از Wide column stores این نیست که واقعا نمیدانید ساختار دادههای مورد استفاده چیست، بلکه در این حالت میدانیم که در هر ردیف تنها از تعداد معدودی از فیلدها استفاده خواهیم کرد. به همین جهت در هر ردیف تمام فیلدها قرار نمیگیرند، چون در اینصورت تعدادی از آنها همواره خالی باقی میماندند. مایکروسافت این مشکل را با ستونهای اسپارس حل کرده است؛ در اینجا هر چند ساختار کلی مشخص است، اما مواردی که هر بار استفاده میشوند، تعداد محدودی میباشند. به این صورت SQL Server تنها برای ستونهای دارای مقدار، فضایی را اختصاص میدهد. به این ترتیب از لحاظ فیزیکی و ذخیره سازی نهایی، به همان مزیت Wide column stores خواهیم رسید.
د) FileStreams در اس کیوال سرور بسیار شبیه به پیوستهای سندهای بانکهای اطلاعاتی NoSQL سندگرا هستند. در اینجا نیز اطلاعات در فایل سیستم ذخیره میشوند اما ارجاعی به آنها در جداول مرتبط وجود خواهند داشت.
7) SQL Server Parallel Data Warehouse Edition
SQL PDW، نگارش خاصی از SQL Server است که در آن یک شبکه از SQL Serverها به صورت یک وهله منطقی SQL Server در اختیار برنامه نویسها قرار میگیرد.
این نگارش، از فناوری خاصی به نام MPP یا massively parallel processing برای پردازش کوئریها استفاده میکند. در اینجا همانند بانکهای اطلاعاتی NoSQL، یک کوئری به نود اصلی ارسال شده و به صورت موازی بر روی تمام نودها پردازش گردیده (همان مفهوم Map Reduce که پیشتر در مورد آن بحث شد) و نتیجه در اختیار مصرف کننده قرار خواهد گرفت. نکته مهم آن نیز در عدم نیاز به نوشتن کدی جهت رخ دادن این عملیات از طرف برنامه نویسها است و موتور پردازشی آن جزئی از سیستم اصلی است. تنها کافی است یک کوئری SQL صادر گردد تا نتیجه نهایی از تمام سرورها جمع آوری و بازگردانده شود.
این نگارش ویژه تنها به صورت یک Appliance به فروش میرسد (به صورت سخت افزار و نرم افزار باهم) که در آن CPUها، فضاهای ذخیره سازی اطلاعات و جزئیات شبکه به دقت از پیش تنظیم شدهاند.
راه اندازی StimulSoft Report در ASP.NET MVC
فایلهای گزارشی که در استودیو خود استیمول طراحی میشن از فارسی پشتیبانی کرده و دارای خصوصیت Right to left برای true شدن میباشند.
همچین در صورتی که مشکل در نمایش داخل خود صفحه وب است در بالا کد css آن ذکر شده است.
نمایش motion گرافیک پیشرفته در اندرود و ios و وب
1- مسیر لوکال هست، در مطلب مقدماتی استفاده از XMLWorker از کلاس ImageProvider تهیه شده استفاده کنید.
2- URL و مسیر وب است، خود iTextSharp به صورت خودکار آنرا دانلود میکند.
3- base64 است، از راه حل مطلب جاری استفاده کنید.
از لحاظ سرعت کار، 3 سریعترین است؛ بعد 1 و در آخر 2.
نهایتا در هر سه حالت، عکس در فایل PDF مدفون میشود و نیازی به تنظیم خاصی ندارد.
ASP.NET MVC #21
ذکر dataType درjQuery نوع اطلاعات برگشتی از سرور رو مشخص میکنه. برای مثال اگر xml ذکر شود، اطلاعات دریافتی از سرور را به فرمت xml در اختیار شما قرار میدهد.