string.Concat(" ",item.Title);
در اینجا مشاهده میکنید که گروه دارایی جاری به همراه حسابهای کل آن در یک سطح نشان داده میشود که این امر خوانایی گزارش را کم میکند.
این گزارش قرار است در 3 سطح کل و معین و تفصیلی گرفته شود.
با تشکر
string.Concat(" ",item.Title);
در اینجا مشاهده میکنید که گروه دارایی جاری به همراه حسابهای کل آن در یک سطح نشان داده میشود که این امر خوانایی گزارش را کم میکند.
این گزارش قرار است در 3 سطح کل و معین و تفصیلی گرفته شود.
با تشکر
An item with the same key has already been added.
_uow.Update(employee, x => x.OwnedCollection(y => y.RemainingLessons));
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef _/\__ ---==/ \\ ___ ___ |. \|\ | __|| __| | ) \\\ | _| | _| \_/ | //|\\ |___||_| / \\\/\\ Entity Framework .NET Core CLI Commands 1.0.0-preview2-21431 Usage: dotnet ef [options] [command] Options: -h|--help Show help information -v|--verbose Enable verbose output --version Show version information --assembly <ASSEMBLY> The assembly file to load. --startup-assembly <ASSEMBLY> The assembly file containing the startup class. --data-dir <DIR> The folder used as the data directory (defaults to current working directory). --project-dir <DIR> The folder used as the project directory (defaults to current working directory). --content-root-path <DIR> The folder used as the content root path for the application (defaults to application base directory). --root-namespace <NAMESPACE> The root namespace of the target project (defaults to the project assembly name). Commands: database Commands to manage your database dbcontext Commands to manage your DbContext types migrations Commands to manage your migrations Use "dotnet ef [command] --help" for more information about a command.
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations add InitialDatabase
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef database update Applying migration '13950526050417_InitialDatabase'. Done.
using System.Collections.Generic; using System.Linq; using Core1RtmEmptyTest.Entities; using Microsoft.Extensions.DependencyInjection; namespace Core1RtmEmptyTest.Migrations { public static class ApplicationDbContextSeedData { public static void SeedData(this IServiceScopeFactory scopeFactory) { using (var serviceScope = scopeFactory.CreateScope()) { var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>(); if (!context.Persons.Any()) { var persons = new List<Person> { new Person { FirstName = "Admin", LastName = "User" } }; context.AddRange(persons); context.SaveChanges(); } } } } }
public void Configure(IServiceScopeFactory scopeFactory) { scopeFactory.SeedData();
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ApplicationDbContext>(ServiceLifetime.Scoped);
namespace Core1RtmEmptyTest.Entities { public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } }
An unhandled exception occurred while processing the request. SqlException: Invalid column name 'Age'.
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations add v2 D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef database update
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations script -o v2.sql
public Configuration() { AutomaticMigrationsEnabled = true; }
using Core1RtmEmptyTest.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Core1RtmEmptyTest.Migrations { public static class DbInitialization { public static void Initialize(this IServiceScopeFactory scopeFactory) { using (var serviceScope = scopeFactory.CreateScope()) { var context = serviceScope.ServiceProvider.GetService<ApplicationDbContext>(); // Applies any pending migrations for the context to the database. // Will create the database if it does not already exist. context.Database.Migrate(); } } } }
public class ItemViewModel { public int Id { get; set; } public string Name { get; set; } public string Country { get; set; } } public class ItemV2ViewModel : ItemViewModel { public double Price { get; set; } }
public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } public class ItemsV2Controller : ApiController { [ResponseType(typeof(ItemV2ViewModel))] public IHttpActionResult Get(int id) { var viewModel = new ItemV2ViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } }
config.Routes.MapHttpRoute("ItemsV2", "api/v2/items/{id}", new { controller = "ItemsV2", id = RouteParameter.Optional }); config.Routes.MapHttpRoute("Items", "api/items/{id}", new { controller = "Items", id = RouteParameter.Optional }); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
config.Routes.MapHttpRoute( "defaultVersioned", "api/v{version}/{controller}/{id}", new { id = RouteParameter.Optional }, new { version = @"\d+" }); config.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional } );
با این تنظیمات فعلا به مسیریابی ورژن بندی شدهای دست نیافتهایم. زیرا فعلا به هیچ طریق به Web API اشاره نکردهایم که به چه صورت از این پارامتر version برای پیدا کردن کنترلر ورژن بندی شده استفاده کند و به همین دلیل این دو مسیریابی نوشته شده در عمل نتیجه یکسانی را خواهند داشت. برای رفع مشکل مطرح شده باید فرآیند پیش فرض انتخاب کنترلر را کمی شخصی سازی کنیم.
IHttpControllerSelector مسئول پیدا کردن کنترلر مربوطه با توجه به درخواست رسیده میباشد. شکل زیر مربوط است به مراحل ساخت کنترلر بر اساس درخواست رسیده:
به جای پیاده سازی مستقیم این اینترفیس، از پیاده سازی کننده پیش فرض موجود (DefaultHttpControllerSelector) اسفتاده کرده و HttpControllerSelector جدید ما از آن ارث بری خواهد کرد.
public class VersionFinder { private static bool NeedsUriVersioning(HttpRequestMessage request, out string version) { var routeData = request.GetRouteData(); if (routeData != null) { object versionFromRoute; if (routeData.Values.TryGetValue(nameof(version), out versionFromRoute)) { version = versionFromRoute as string; if (!string.IsNullOrWhiteSpace(version)) { return true; } } } version = null; return false; } private static int VersionToInt(string versionString) { int version; if (string.IsNullOrEmpty(versionString) || !int.TryParse(versionString, out version)) return 0; return version; } public static int GetVersionFromRequest(HttpRequestMessage request) { string version; return NeedsUriVersioning(request, out version) ? VersionToInt(version) : 0; } }
کلاس VersionFinder برای یافتن ورژن رسیده در درخواست جاری مورد استفاده قرار خواهد گرفت. با استفاده از متد NeedsUriVersioning بررسی صورت میگیرد که آیا در این درخواست پارامتری به نام version وجود دارد یا خیر که درصورت موجود بودن، مقدار آن واکشی شده و درون پارامتر out قرار میگیرد. در متد GetVersionFromRequest بررسی میشود که اگر خروجی متد NeedsUriVersioning برابر با true باشد، با استفاده از متد VersionToInt مقدار به دست آمده را به int تبدیل کند.
public class VersionAwareControllerSelector : DefaultHttpControllerSelector { public VersionAwareControllerSelector(HttpConfiguration configuration) : base(configuration) { } public override string GetControllerName(HttpRequestMessage request) { var controllerName = base.GetControllerName(request); var version = VersionFinder.GetVersionFromRequest(request); return version > 0 ? GetVersionedControllerName(request, controllerName, version) : controllerName; } private string GetVersionedControllerName(HttpRequestMessage request, string baseControllerName, int version) { var versionControllerName = $"{baseControllerName}v{version}"; HttpControllerDescriptor descriptor; if (GetControllerMapping().TryGetValue(versionControllerName, out descriptor)) { return versionControllerName; } throw new HttpResponseException(request.CreateErrorResponse( HttpStatusCode.NotFound, $"No HTTP resource was found that matches the URI {request.RequestUri} and version number {version}")); } }
متد GetControllerName وظیفه بازگشت دادن نام کنترلر را برعهده دارد. ما نیز با لغو رفتار پیش فرض این متد و تهیه نام ورژن بندی شده کنترلر و معرفی این پیاده سازی از IHttpControllerSelector به شکل زیر میتوانیم به Web API بگوییم که به چه صورت از پارامتر version موجود در درخواست استفاده کند.
config.Services.Replace(typeof(IHttpControllerSelector), new VersionAwareControllerSelector(config));
حال با اجرای برنامه به نتیجه زیر خواهیم رسید:
راه حل دوم: برای زمانیکه Attribute Routing مورد استفاده شما است میتوان به راحتی با تعریف قالبهای مناسب مسیریابی، API ورژن بندی شده را ارائه دهید.
[RoutePrefix("api/v1/Items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [Route("{id:int}")] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } [RoutePrefix("api/V2/Items")] public class ItemsV2Controller : ApiController { [ResponseType(typeof(ItemV2ViewModel))] [Route("{id:int}")] public IHttpActionResult Get(int id) { var viewModel = new ItemV2ViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } }
اگر توجه کرده باشید در مثال ما، نامهای کنترلرها متفاوت از هم میباشند. اگر بجای در نظر گرفتن نامهای مختلف برای یک کنترلر در ورژنهای مختلف، آن را با یک نام یکسان درون namespaceهای مختلف احاطه کنیم یا حتی آنها را درون Class Libraryهای جدا نگهداری کنیم، به مشکل "یافت شدن چندین کنترلر که با درخواست جاری مطابقت دارند" برخواهیم خورد. برای حل این موضوع به راه حل سوم توجه کنید.
راه حل سوم: استفاده از یک NamespaceControllerSelector که پیاده سازی دیگری از اینترفیس IHttpControllerSelector میباشد. فرض بر این است که قالب پروژه به شکل زیر میباشد:
کار با پیاده سازی اینترفیس IHttpRouteConstraint آغاز میشود:
public class VersionConstraint : IHttpRouteConstraint { public VersionConstraint(string allowedVersion) { AllowedVersion = allowedVersion.ToLowerInvariant(); } public string AllowedVersion { get; private set; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { object value; if (values.TryGetValue(parameterName, out value) && value != null) { return AllowedVersion.Equals(value.ToString().ToLowerInvariant()); } return false; } }
کلاس بالا در واقع برای اعمال محدودیت خاصی که در ادامه توضیح داده میشود، پیاده سازی شده است.
متد Match آن وظیفه چک کردن برابری مقدار کلید parameterName موجود در درخواست را با مقدار allowedVersion ای که API از آن پشتیبانی میکند، برعهده دارد. با استفاده از این Constraint مشخص کردهایم که دقیقا چه زمانی باید Route نوشته شده انتخاب شود.
به روش استفاده از این Constraint توجه کنید:
namespace UriBasedVersioning.Namespace.Controllers.V1 { using Models.V1; RoutePrefix("api/{version:VersionConstraint(v1)}/items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [Route("{id}")] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } } namespace UriBasedVersioning.Namespace.Controllers.V2 { using Models.V2; RoutePrefix("api/{version:VersionConstraint(v2)}/items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [Route("{id}")] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } } }
با توجه به کد بالا میتوان دلیل استفاده از VersionConstraint را هم درک کرد؛ از آنجایی که ما دو Route شبیه به هم داریم، لذا باید مشخص کنیم که در چه شرایطی و کدام یک از این Routeها انتخاب شود. خوب، اگر برنامه را اجرا کرده و یکی از APIهای بالا را تست کنید، با خطا مواجه خواهید شد؛ زیرا فعلا این Constraint به سیستم Web API معرفی نشده است. تنظیمات زیر را انجام دهید:
var constraintsResolver = new DefaultInlineConstraintResolver(); constraintsResolver.ConstraintMap.Add(nameof(VersionConstraint), typeof (VersionConstraint)); config.MapHttpAttributeRoutes(constraintsResolver);
مرحله بعدی کار، پیاده سازی IHttpControllerSelector میباشد:
public class NamespaceControllerSelector : IHttpControllerSelector { private readonly HttpConfiguration _configuration; private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; public NamespaceControllerSelector(HttpConfiguration config) { _configuration = config; _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); } public HttpControllerDescriptor SelectController(HttpRequestMessage request) { var routeData = request.GetRouteData(); if (routeData == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } var controllerName = GetControllerName(routeData); if (controllerName == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } var version = GetVersion(routeData); if (version == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } var controllerKey = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", version, controllerName); HttpControllerDescriptor controllerDescriptor; if (_controllers.Value.TryGetValue(controllerKey, out controllerDescriptor)) { return controllerDescriptor; } throw new HttpResponseException(HttpStatusCode.NotFound); } public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { return _controllers.Value; } private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() { var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); var assembliesResolver = _configuration.Services.GetAssembliesResolver(); var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); foreach (var controllerType in controllerTypes) { var segments = controllerType.Namespace.Split(Type.Delimiter); var controllerName = controllerType.Name.Remove(controllerType.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); var controllerKey = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName); if (!dictionary.Keys.Contains(controllerKey)) { dictionary[controllerKey] = new HttpControllerDescriptor(_configuration, controllerType.Name, controllerType); } } return dictionary; } private static T GetRouteVariable<T>(IHttpRouteData routeData, string name) { object result; if (routeData.Values.TryGetValue(name, out result)) { return (T)result; } return default(T); } private static string GetControllerName(IHttpRouteData routeData) { var subroute = routeData.GetSubRoutes().FirstOrDefault(); var dataTokenValue = subroute?.Route.DataTokens.First().Value; var controllerName = ((HttpActionDescriptor[])dataTokenValue)?.First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty); return controllerName; } private static string GetVersion(IHttpRouteData routeData) { var subRouteData = routeData.GetSubRoutes().FirstOrDefault(); return subRouteData == null ? null : GetRouteVariable<string>(subRouteData, "version"); } }
سورس اصلی کلاس بالا از این آدرس قابل دریافت است. در تکه کد بالا بخشی که مربوط به چک کردن تکراری بودن آدرس میباشد، برای ساده سازی کار حذف شده است. ولی نکتهی مربوط به SubRoutes که برای واکشی مقادیر پارامترهای مرتبط با Attribute Routing میباشد، اضافه شده است. روال کار به این صورت است که ابتدا RouteData موجود در درخواست را واکشی کرده و با استفاده از متدهای GetControllerName و GetVersion، پارامترهای controller و version را جستجو میکنیم. بعد با استفاده از مقادیر به دست آمده، controllerKey را تشکیل داده و درون کنترلرهای موجود در برنامه به دنبال کنترلر مورد نظر خواهیم گشت.
کارکرد متد InitializeControllerDictionary :
همانطور که میدانید به صورت پیشفرض Web API توجهی به فضای نام مرتبط با کنترلرها ندارد. از طرفی برای پیاده سازی اینترفیس IHttpControllerSelector نیاز است متدی با امضای زیر را داشته باشیم:
public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
لذا در کلاس پیاده سازی شده، خصوصیتی به نام _controllers را که از به صورت Lazy و از نوع بازگشتی متد بالا میباشد، تعریف کردهایم. متد InitializeControllerDictionary کار آماده سازی دادههای مورد نیاز خصوصیت _controllers میباشد. به این صورت که تمام کنترلرهای موجود در برنامه را واکشی کرده و این بار کلیدهای مربوط به دیکشنری را با استفاده از نام کنترلر و آخرین سگمنت فضای نام آن، تولید و درون دیکشنری مورد نظر ذخیره کردهایم.
حال تنظیمات زیر را اعمال کنید:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { var constraintsResolver = new DefaultInlineConstraintResolver(); constraintsResolver.ConstraintMap.Add(nameof(VersionConstraint), typeof (VersionConstraint)); config.MapHttpAttributeRoutes(constraintsResolver); config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceControllerSelector(config)); } }
این بار برنامه را اجرا کرده و APIهای مورد نظر را تست کنید؛ بله بدون مشکل کار خواهد کرد.
نکته تکمیلی: سورس مذکور در سایت کدپلکس برای حالتی که از Centralized Routes استفاده میکنید آماده شده است. روش مذکور در این مقاله هم فقط قسمت Duplicate Routes آن را کم دارد که میتوانید اضافه کنید. پیاده سازی دیگری را از این راه حل هم میتوانید داشته باشید.
پیاده سازی Header-based versioning
public class HeaderVersionConstraint : IHttpRouteConstraint { private const string VersionHeaderName = "api-version"; public HeaderVersionConstraint(int allowedVersion) { AllowedVersion = allowedVersion; } public int AllowedVersion { get; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { if (!request.Headers.Contains(VersionHeaderName)) return false; var version = request.Headers.GetValues(VersionHeaderName).FirstOrDefault(); return VersionToInt(version) == AllowedVersion; } private static int VersionToInt(string versionString) { int version; if (string.IsNullOrEmpty(versionString) || !int.TryParse(versionString, out version)) return 0; return version; } }
public sealed class HeaderVersionedRouteAttribute : RouteFactoryAttribute { public HeaderVersionedRouteAttribute(string template) : base(template) { Order = -1; } public int Version { get; set; } public override IDictionary<string, object> Constraints => new HttpRouteValueDictionary { {"", new HeaderVersionConstraint(Version)} }; }
[RoutePrefix("api/items")] public class ItemsController : ApiController { [ResponseType(typeof(ItemViewModel))] [HeaderVersionedRoute("{id}", Version = 1)] public IHttpActionResult Get(int id) { var viewModel = new ItemViewModel { Id = id, Name = "PS4", Country = "Japan" }; return Ok(viewModel); } } [RoutePrefix("api/items")] public class ItemsV2Controller : ApiController { [ResponseType(typeof(ItemV2ViewModel))] [HeaderVersionedRoute("{id}", Version = 2)] public IHttpActionResult Get(int id) { var viewModel = new ItemV2ViewModel { Id = id, Name = "Xbox One", Country = "USA", Price = 529.99 }; return Ok(viewModel); } }
GET /api/Items HTTP 1.1 Accept: application/vnd.mediatype.versioning-v1+json GET /api/Items HTTP 1.1 Accept: application/vnd.mediatype.versioning-v2+json
public class MediaTypeVersionConstraint : IHttpRouteConstraint { private const string VersionMediaType = "vnd.mediatype.versioning"; public MediaTypeVersionConstraint(int allowedVersion) { AllowedVersion = allowedVersion; } public int AllowedVersion { get; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { if (!request.Headers.Accept.Any()) return false; var acceptHeaderVersion = request.Headers.Accept.FirstOrDefault(x => x.MediaType.Contains(VersionMediaType)); //Accept: application/vnd.mediatype.versioning-v1+json if (acceptHeaderVersion == null || !acceptHeaderVersion.MediaType.Contains("-v") || !acceptHeaderVersion.MediaType.Contains("+")) return false; var version = acceptHeaderVersion.MediaType.Between("-v", "+"); return VersionToInt(version)==AllowedVersion; } }
<p> <label for="FirstName">First Name:</label> <span id="FirstName">@ViewData["FirstName"]</span> </p>
<p> <label for="FirstName">First Name:</label> <span id="FirstName">@Model.FirstName</span> </p>
@if(Model.IsAnonymousUser) { <img src="@Url.Content("~/content/images/anonymous.jpg")" /> } else if(Model.IsAdministrator) { <img src="@Url.Content("~/content/images/administrator.jpg")" /> } else if(Model.Membership == Membership.Standard) { <img src="@Url.Content("~/content/images/member.jpg")" /> } else if(Model.Membership == Membership.Preferred) { <img src="@Url.Content("~/content/images/preferred_member.jpg")" /> }
public static string UserAvatar(this HtmlHelper<User> helper) { var user = helper.ViewData.Model; string avatarFilename = "anonymous.jpg"; if (user.IsAnonymousUser) { avatarFilename = "anonymous.jpg"; } else if (user.IsAdministrator) { avatarFilename = "administrator.jpg"; } else if (user.Membership == Membership.Standard) { avatarFilename = "member.jpg"; } else if (user.Membership == Membership.Preferred) { avatarFilename = "preferred_member.jpg"; } var urlHelper = new UrlHelper(helper.ViewContext.RequestContext); var contentPath = string.Format("~/content/images/{0}", avatarFilename) string imageUrl = urlHelper.Content(contentPath); return string.Format("<img src='{0}' />", imageUrl); }
@Html.UserAvatar()
[AllowAnonymous] public class HomeController : Controller { [HttpGet] public ActionResult Index() { return View(); } } [AllowAnonymous] public class SecurityController : Controller { [HttpGet] public ActionResult LogOn(string returnUrl) { if (User.Identity.IsAuthenticated) //remember me { if (CanRedirect(returnUrl)) { return Redirect(returnUrl); } return Redirect(FormsAuthentication.DefaultUrl); } return View(); // show the login page } }
function enableBootstrapStyleValidation() { $.validator.setDefaults({ ignore: "", highlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).addClass(errorClass).removeClass(validClass); } else { $(element).addClass(errorClass).removeClass(validClass); $(element).closest('.form-group').removeClass('has-success').addClass('has-error'); } $(element).trigger('highlited'); }, unhighlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).removeClass(errorClass).addClass(validClass); } else { $(element).removeClass(errorClass).addClass(validClass); $(element).closest('.form-group').removeClass('has-error').addClass('has-success'); } $(element).trigger('unhighlited'); } }); }
$("#form").validate({ ... rules: {...}, messages: {...}, ignore: "", ... });
import { Injectable } from "@angular/core"; @Injectable() export class BrowserStorageService { getSession(key: string): any { const data = window.sessionStorage.getItem(key); return JSON.parse(data); } setSession(key: string, value: any): void { const data = value === undefined ? null : JSON.stringify(value); window.sessionStorage.setItem(key, data); } removeSession(key: string): void { window.sessionStorage.removeItem(key); } removeAllSessions(): void { for (const key in window.sessionStorage) { if (window.sessionStorage.hasOwnProperty(key)) { this.removeSession(key); } } } getLocal(key: string): any { const data = window.localStorage.getItem(key); return JSON.parse(data); } setLocal(key: string, value: any): void { const data = value === undefined ? null : JSON.stringify(value); window.localStorage.setItem(key, data); } removeLocal(key: string): void { window.localStorage.removeItem(key); } removeAllLocals(): void { for (const key in window.localStorage) { if (window.localStorage.hasOwnProperty(key)) { this.removeLocal(key); } } } }
import { BrowserStorageService } from "./browser-storage.service"; import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { RouterModule } from "@angular/router"; @NgModule({ imports: [CommonModule, RouterModule], exports: [], // components that are used in app.component.ts will be listed here. declarations: [], // components that are used in app.component.ts will be listed here. providers: [BrowserStorageService] // singleton services of the whole app will be listed here. }) export class CoreModule { };
import { CoreModule } from "./core/core.module"; @NgModule({ imports: [ //... CoreModule, //... RouterModule.forRoot(appRoutes) ], //... }) export class AppModule { }
import { BrowserStorageService } from "./../../core/browser-storage.service"; import { Component, OnInit } from "@angular/core"; @Component({ selector: "app-browser-storage-sample-test", templateUrl: "./browser-storage-sample-test.component.html", styleUrls: ["./browser-storage-sample-test.component.css"] }) export class BrowserStorageSampleTestComponent implements OnInit { fromSessionStorage = ""; fromLocalStorage = "" sessionStorageKey = "sessionStorageKey1"; localStorageKey = "localStorageKey1" constructor(private browserStorage: BrowserStorageService) { } ngOnInit() { } sessionStorageSetItem() { this.browserStorage.setSession(this.sessionStorageKey, "Val1"); } sessionStorageGetItem() { this.fromSessionStorage = this.browserStorage.getSession(this.sessionStorageKey); } localStorageSetItem() { this.browserStorage.setLocal(this.localStorageKey, { key1: "val1", key2: 2 }); } localStorageGetItem() { this.fromLocalStorage = JSON.stringify(this.browserStorage.getLocal(this.localStorageKey)); } }
<h1>Browser storage sample</h1> <div class="panel"> <button class="btn btn-primary" (click)="sessionStorageSetItem()" type="button">sessionStorage -> Set Item</button> <button class="btn btn-success" (click)="sessionStorageGetItem()" type="button">sessionStorage -> Get Item</button> <div class="alert alert-info" *ngIf="fromSessionStorage"> {{fromSessionStorage}} </div> </div> <div class="panel"> <button class="btn btn-warning" (click)="localStorageSetItem()" type="button">localStorage -> Set Item</button> <button class="btn btn-success" (click)="localStorageGetItem()" type="button">localStorage -> Get Item</button> <div class="alert alert-info" *ngIf="fromLocalStorage"> {{fromLocalStorage}} </div> </div>
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; namespace Image2Base64Converter { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnOpen_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Multiselect = false; ofd.Filter = "Pictures |*.bmp;*.jpg;*.jpeg;*.png"; if (ofd.ShowDialog(this) == DialogResult.OK) { txtImagePath.Text = ofd.FileName; } } private void btnConvert_Click(object sender, EventArgs e) { if (!File.Exists(txtImagePath.Text)) { MessageBox.Show("File not found!"); return; } btnCopy.Enabled = false; btnConvert.Enabled = false; txtOutput.Enabled = false; BackgroundWorker bworker = new BackgroundWorker(); bworker.DoWork += (o, a) => { string header = "data:image/{0};base64,"; header = string.Format(header, txtImagePath.Text.GetExtension()); StringBuilder data = new StringBuilder(); byte[] buffer; BinaryReader head = new BinaryReader( new FileStream(txtImagePath.Text, FileMode.Open)); buffer = head.ReadBytes(6561); while (buffer.Length > 0) { data.Append(Convert.ToBase64String(buffer)); buffer = head.ReadBytes(6561); } head.Close(); head.Dispose(); this.Invoke(new Action(() => { txtOutput.ResetText(); txtOutput.AppendText(header); txtOutput.AppendText(data.ToString()); btnCopy.Enabled = true; btnConvert.Enabled = true; txtOutput.Enabled = true; })); }; bworker.RunWorkerAsync(); } private void btnCopy_Click(object sender, EventArgs e) { Clipboard.SetText(txtOutput.Text); } } public static class ExtensionMethods { public static string GetExtension(this string str) { string ext = string.Empty; for (int i = str.Length - 1; i >= 0; i--) { if (str[i] == '.') { break; } ext = str[i] + ext; } return ext; } } }
<div class="table-responsive"> <table class="table"> ... </table> </div>