معادل IActionResult در Minimal API's
در Minimal API's دیگر خبری از IActionResultها نیست؛ اما بجای آن IResult را داریم. برای مثال فرض کنید میخواهیم بدنهی lambda expression دو endpoint ای را که تا این مرحله توسعه دادیم، تبدیل به دو متد مجزای private کنیم:
public class AuthorModule : IModule { public IEndpointRouteBuilder RegisterEndpoints(IEndpointRouteBuilder endpoints) { endpoints.MapGet("/api/authors", async (IMediator mediator, CancellationToken ct) => await GetAllAuthorsAsync(mediator, ct)); endpoints.MapPost("/api/authors", async (IMediator mediator, AuthorDto authorDto, CancellationToken ct) => await CreateAuthorAsync(authorDto, mediator, ct)); return endpoints; } private static async Task<IResult> CreateAuthorAsync(AuthorDto authorDto, IMediator mediator, CancellationToken ct) { var command = new CreateAuthorCommand { AuthorDto = authorDto }; var author = await mediator.Send(command, ct); return Results.Ok(author); } private static async Task<IResult> GetAllAuthorsAsync(IMediator mediator, CancellationToken ct) { var request = new GetAllAuthorsQuery(); var authors = await mediator.Send(request, ct); return Results.Ok(authors); } }
Challenge, Forbid, SignIn, SignOut, Content, Text, Json, File, Bytes, Stream, Redirect, LocalRedirect, StatusCode NotFound, Unauthorized, BadRequest, Conflict, NoContent, Ok UnprocessableEntity, Problem, ValidationProblem, Created CreatedAtRoute, Accepted, AcceptedAtRoute
یک مثال: استفاده از متد Results.Problem جهت بازگشت پیام خطایی به کاربر:
try { return Results.Ok(await data.GetUsers()); } catch (Exception ex) { return Results.Problem(ex.Message); }
ساده سازی تعاریف هندلرهای endpoints در Minimal API's
تا اینجا هندلرهای یک endpoint را تبدیل به متدهایی مستقل کردیم و به صورت زیر فراخوانی شدند:
endpoints.MapGet("/api/authors", async (IMediator mediator, CancellationToken ct) => await GetAllAuthorsAsync(mediator, ct));
endpoints.MapGet("/api/authors", GetAllAuthorsAsync); endpoints.MapPost("/api/authors", CreateAuthorAsync);
غنی سازی اطلاعات Open API در Minimal API's
در اینجا چون با کنترلرها و اکشن متدها کار نمیکنیم، نمیتوانیم اطلاعات تکمیلی Open API را از طریق بکارگیری attributes مخصوص آنها اضافه کنیم. اولین تغییری که در Minimal API's جهت دریافت متادیتای endpoints قابل مشاهدهاست، چند سطر زیر است:
public static class ServiceCollectionExtensions { public static IServiceCollection AddApplicationServices(this IServiceCollection services, WebApplicationBuilder builder) { builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // ...
public class AuthorModule : IModule { public IEndpointRouteBuilder RegisterEndpoints(IEndpointRouteBuilder endpoints) { endpoints.MapGet("/api/authors", async (IMediator mediator, CancellationToken ct) => await GetAllAuthorsAsync(mediator, ct)) .WithName("GetAllAuthors") .WithDisplayName("Authors") .WithTags("Authors") .Produces(500); endpoints.MapPost("/api/authors", async (IMediator mediator, AuthorDto authorDto, CancellationToken ct) => await CreateAuthorAsync(authorDto, mediator, ct)) .WithName("CreateAuthor") .WithDisplayName("Authors") .WithTags("Authors") .Produces(500); return endpoints; }
البته اگر تا اینجا برنامه را اجرا کنید، برای مثال نامهایی که تعریف شدهاند، در Swagger ظاهر نمیشوند. برای رفع این مشکل میتوان به صورت زیر عمل کرد:
builder.Services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = builder.Environment.ApplicationName, Version = "v1" }); options.TagActionsBy(ta => new List<string> { ta.ActionDescriptor.DisplayName! }); });
تغییر خروجی endpoints از مدل دومین، به یک Dto
در endpoints فوق، اطلاعات دریافتی از کاربر، یک dto است که توسط AutoMapper به مدل دومین، نگاشت میشود. اینکار خصوصا از دیدگاه امنیتی جهت رفع مشکلی به نام mass assignment و عدم مقدار دهی خودکار خواصی از مدل اصلی که نباید مقدار دهی شوند، بسیار مفید است. در حین بازگشت اطلاعات به کاربر نیز باید چنین رویهای درنظر گرفته شود. برای مثال مدل User میتواند به همراه آدرس ایمیل و کلمهی عبور هش شدهی او نیز باشد و نباید API ما این اطلاعات را بازگشت دهد. بازگشتی از آن باید بسیار کنترل شده و صرفا بر اساس نیاز مصرف کننده تنظیم شود. به همین جهت یک Dto مخصوص را نیز برای بازگشت اطلاعات از سرور اضافه میکنیم تا اطلاعات مشخصی را بازگشت دهد:
namespace MinimalBlog.Api.Features.Authors; public record AuthorGetDto { public int Id { get; init; } public string Name { get; init; } = default!; public string? Bio { get; init; } public DateTime DateOfBirth { get; init; } }
public class AuthorProfile : Profile { public AuthorProfile() { CreateMap<AuthorDto, Author>().ReverseMap(); CreateMap<Author, AuthorGetDto>() .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName)); } }
الف) دستور و هندلر ایجاد نویسنده
public class CreateAuthorCommand : IRequest<AuthorGetDto>
- ابتدا نوع خروجی این هندلر نیز به AuthorGetDto تنظیم میشود:
public class CreateAuthorCommandHandler : IRequestHandler<CreateAuthorCommand, AuthorGetDto>
public async Task<AuthorGetDto> Handle(CreateAuthorCommand request, CancellationToken cancellationToken)
return _mapper.Map<AuthorGetDto>(toAdd);
ب) کوئری و هندلر بازگشت لیست نویسندهها
public class GetAllAuthorsQuery : IRequest<List<AuthorGetDto>>
public class GetAllAuthorsHandler : IRequestHandler<GetAllAuthorsQuery, List<AuthorGetDto>> { private readonly MinimalBlogDbContext _context; private readonly IMapper _mapper; public GetAllAuthorsHandler(MinimalBlogDbContext context, IMapper mapper) { _context = context ?? throw new ArgumentNullException(nameof(context)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); } public async Task<List<AuthorGetDto>> Handle(GetAllAuthorsQuery request, CancellationToken cancellationToken) { var authors = await _context.Authors.ToListAsync(cancellationToken); return _mapper.Map<List<AuthorGetDto>>(authors); } }
endpoints.MapGet("/api/authors", async (IMediator mediator, CancellationToken ct) => await GetAllAuthorsAsync(mediator, ct)) .WithName("GetAllAuthors") .WithDisplayName("Authors") .WithTags("Authors") .Produces<List<AuthorGetDto>>() .Produces(500); endpoints.MapPost("/api/authors", async (IMediator mediator, AuthorDto authorDto, CancellationToken ct) => await CreateAuthorAsync(authorDto, mediator, ct)) .WithName("CreateAuthor") .WithDisplayName("Authors") .WithTags("Authors") .Produces<AuthorGetDto>() .Produces(500);
پوشه بندی Features
تا اینجا تمام فایلهای متعلق به ویژگی Authors را در همان پوشه اصلی آن قرار دادهایم. در ادامه میتوان به ازای هر ویژگی خاص، 4 پوشهی Commands مخصوص Commands الگوی CQRS، پوشهی Models مخصوص تعریف DTO's، پوشهی Profiles مخصوص افزودن پروفایلهای AutoMapper و پوشهی Queries مخصوص تعریف کوئریهای الگوی CQRS را به نحوی که در تصویر فوق مشاهده میکنید، به پروژهی API اضافه کنیم.
پیاده سازی ویژگی Blogs
این پیاده سازی چون به همراه نکات جدیدی نیست و به همراه تعریف ماژول اصلی ویژگی، endpoints و الگوی CQRS ای است که تاکنون بحث شد، کدهای آن، به همراه کدهای پروژهی اصلی این پروژه که از قسمت اول قابل دریافت است، ارائه شدهاست.
اشتباه 1:
استفاده از
throw ex;
throw;
اشتباه 2:
درک اشتباه عملکرد متد replace :
string s = "Take this out";
s.Replace("this", "that"); //wrong
s = s.Replace("this", "that"); //correct
اگر از fxCop استفاده کنید، اینگونه خطاها را (عدم استفاده از مقدار بازگشتی) گوشزد میکند.
اشتباه 3:
استفادهی بی دقت از متغیرهای استاتیک در یک برنامه وب. دو مثال زیر را در نظر بگیرید:
public static string GetCookieName(Cookie c)
{
return c.Name;
}
static List<string> cookieList = new List<string>();
public static void AddToCookieList(Cookie c)
{
cookieList.Add(c.Name);
}
برنامههای وب ذاتا چند ریسمانی هستند و زمانیکه یک متغیر را از نوع استاتیک تعریف میکنید، این متغیر، هنگام مراجعهی کاربران بین آنها به اشتراک گذاشته میشود و امکان تخریب یا استفادهی ناصحیح از مقادیر آنها وجود خواهد داشت. در حالت اول نیازی به مباحث همزمانی و قفل کردن منابع نیست زیرا متغیری که در متد استفاده میشود، thread safe است اما cookieList در مثال دوم خیر و حتما هنگام استفاده از آن باید مباحث قفل کردن منابع را درنظر داشت. (حتی اگر برنامهی شما از نوع وبی هم نیست اما چند ریسمانی است این مطلب باز هم صادق میباشد)
اشتباه 4:
مقابله با خطاها به شکلی نادرست: (اصطلاحا خفه کردن خطاها!)
try
{
//something
File.Delete(blah);
}
catch{}
هرچند گاهی از اوقات اگر خطای حاصل برای ما اهمیتی نداشت میتوان از آن استفاده نمود، در غیراینصورت باید حتما از این روش پرهیز کرد.
اشتباه 5:
ارائهی برنامههای ASP.Net با گزینهی پیش فرض Debug=true در web.config که پیشتر در مورد آن در این سایت بحث شده است.
اشتباه 6:
عدم استفاده از امکانات ویژهی دات نت فریم ورک هنگام کار با رشتهها:
string s = "This ";
s += "is ";
s += "not ";
s += "the ";
s += "best ";
s += "way.";
StringBuilder sb = new StringBuilder();
sb.Append("This ");
sb.Append("is ");
sb.Append("much ");
sb.Append("better. ");
زمانیکه نیاز به کنار هم قرار دادن رشتههای مختلف وجود داشت و تعداد آنها نیز زیاد بود، مثال دوم توصیه میشود.
البته در مثال فوق که تعداد کمی رشته قرار است با هم جمع شوند، کامپایلر به اندازهی کافی هوشمند خواهد بود که تمام آنها را کنار هم قرار دهد و تفاوتی در کارآیی احساس نشود، حتی روش اول سریعتر از روش دوم خواهد بود، اما زمان استفاده از هزاران رشته، این تفاوت محسوس است.
اشتباه 7:
عدم استفاده از عبارت using هنگام استفاده از اشیایی از نوع Idisposable . مثالی در این مورد.
using (StreamReader reader=new StreamReader(file))
{
//your code here
}
اشتباه 8:
فراموش کردن بررسی نال بودن یک شیء هنگام استفاده از آن.
string val = Session["xyz"].ToString();
این نوع کد نویسی یکی از اشتباهات متداول تمامی تازه واردان به ASP.Net است. حتما باید پیش از استفاده از متد ToString بررسی شود که آیا این سشن نال است یا نه. در غیراینصورت حاصل کار فقط یک exception خواهد بود. (استفاده از افزونهی ری شارپر در این موارد کمک بزرگی است، زیرا به محض قرار گرفتن مکان نما روی شیءایی که احتمال نال بودن آن میسر است، یک راهنما را به شما ارائه خواهد کرد)
اشتباه 9:
بازگشت دادن یک property عمومی از نوع لیستهای جنریک.
با توجه به اینکه این نوع لیستها فقط خواندنی نیستند و امکان دستکاری اطلاعات آن توسط فراخوان وجود دارد، توصیه میشود از نوع جنریک IEnumerable استفاده شود. همچنین توصیه شده است هنگام انتخاب نوع پارامترهای ورودی یک متد نیز به این مورد دقت شود.
دیروز به من اطلاع دادند که در یکی از برنامهها دو تا گروه "تاسیسات مکانیکی" پیدا شده!!
تاسیسات مکانیکی
تاسیسات مکانیکی
استاندارد این شرکت، استفاده از kbdfa.dll مخصوص و نسبتا قدیمی است. بنابراین استاندارد مورد استفاده همان ی و ک عربی است. (کاری ندارم خوب است یا بد، یا باید اینطور باشد یا نه، بحث این است که فعلا اینطور است و قرار نیست چیزی عوض بشود!)
در مثال فوق، ی و ک عبارت دوم فارسی است. یعنی نصب kbdfa.dll روی ویندوز تازه نصب شده، فراموش شده بوده.
راه حلها:
الف) قبل از ثبت، یکسان سازی صورت گیرد. یعنی اجرای متدی شبیه به متد زیر بر روی هر ورودی متنی فارسی:
public string SafeFarsiStr(string input)
{
return input.Replace("ی", "ی").Replace("ک", "ک");
}
اسکریپتی را تهیه کردهام (مخصوص SQL Server 2005 به بعد) به صورت زیر که این تبدیل را برای شما انجام میدهد.
به صورت خودکار تمامی فیلدهای متنی کلیه جداول دیتابیس جاری شما را یافته و ی و ک آنها را یکسان میکند. البته همانطور که عرض شد، مطابق استاندارد این شرکت و استفاده از فایل kbdfa.dll قدیمی مورد استفاده، تمام ی و ک های فارسی به عربی تبدیل میشوند.
--اسکریپتی برای یک دست سازی ی و ک در تمامی رکوردهای تمامی جداول دیتابیس جاری
-- اسکریپت زیر ی و ک فارسی را به عربی تبدیل میکند
-- در صورت نیاز به حالت عکس ، جای مقادیر عددی یونیکد را تعویض نمائید
USE TestDb;
DECLARE @Table NVARCHAR(MAX),
@Col NVARCHAR(MAX)
DECLARE Table_Cursor CURSOR
FOR
--پیدا کردن تمام فیلدهای متنی تمام جداول دیتابیس جاری
SELECT a.name, --table
b.name --col
FROM sysobjects a,
syscolumns b
WHERE a.id = b.id
AND a.xtype = 'u' --User table
AND (
b.xtype = 99 --ntext
OR b.xtype = 35 -- text
OR b.xtype = 231 --nvarchar
OR b.xtype = 167 --varchar
OR b.xtype = 175 --char
OR b.xtype = 239 --nchar
)
OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @Table,@Col
WHILE (@@FETCH_STATUS = 0)
BEGIN
EXEC (
'update [' + @Table + '] set [' + @Col +
']= REPLACE(REPLACE(CAST([' + @Col +
'] as nvarchar(max)) , NCHAR(1740), NCHAR(1610)),NCHAR(1705),NCHAR(1603)) '
)
FETCH NEXT FROM Table_Cursor INTO @Table,@Col
END CLOSE Table_Cursor DEALLOCATE Table_Cursor
الف) برای آشنایی با انواع XType Datatype مورد استفاده در کوئری فوق به این آدرس مراجعه نمائید.
ب) همانطور که در مطالب قبلی این وبلاگ نیز ذکر شد، امکان استفاده از تابع replace بر روی فیلدهای text و ntext وجود ندارد. هیچ اشکالی ندارد! تمام آنها به nvarchar از نوع max دار cast شده و این مشکل به این صورت حل میشود.
ج) اس کیوال سرور اجازه تعریف یک جدول یا فیلد را به صورت متغیر بکار رفته در یک کوئری T-SQL نمیدهد. برای حل این موضوع باید عبارت SQL مورد نظر را به صورت یک رشته درآورد و سپس exec کرد.
د) مجبور شدم از معادلهای عددی برای دقت بیشتر کار استفاده کنم
(در کل از تابع UNICODE اس کیوال سرور برای بدست آوردن این اعداد میتوان استفاده کرد)
تذکر: این اسکریپت بر روی یک دیتابیس کاری تست شده و نتیجه رضایت بخش بوده؛ اما اگر شما روزی خواستید از آن استفاده کنید، حتما full backup را قبل از اجرای آن فراموش نکنید و پس از اجرا، تابع SafeFarsiStr فوق را برای ادامه کار حتما لحاظ نمائید یا از یک kbdfa.dll هماهنگ استفاده کنید.
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); FluentValidationModelValidatorProvider.Configure(); }
[Validator(typeof(PersonValidator))] public class Person { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public int Age { get; set; } } public class PersonValidator : AbstractValidator<Person> { public PersonValidator() { RuleFor(x => x.Id).NotNull(); RuleFor(x => x.Name).Length(0, 10); RuleFor(x => x.Email).EmailAddress(); RuleFor(x => x.Age).InclusiveBetween(18, 60); } }
public class PeopleController : Controller { public ActionResult Create() { return View(); } [HttpPost] public ActionResult Create(Person person) { if(! ModelState.IsValid) { // re-render the view when validation failed. return View("Create", person); } TempData["notice"] = "Person successfully created"; return RedirectToAction("Index"); } }
@Html.ValidationSummary() @using (Html.BeginForm()) { Id: @Html.TextBoxFor(x => x.Id) @Html.ValidationMessageFor(x => x.Id) <br /> Name: @Html.TextBoxFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name) <br /> Email: @Html.TextBoxFor(x => x.Email) @Html.ValidationMessageFor(x => x.Email) <br /> Age: @Html.TextBoxFor(x => x.Age) @Html.ValidationMessageFor(x => x.Age) <input type="submit" value="submit" /> }
- NotNull/NotEmpty
- Matches
- InclusiveBetween
- CreditCard
- Email
- EqualTo
- Length
public ActionResult Save([CustomizeValidator(RuleSet="MyRuleset")] Customer cust) { // ... }
public interface IValidatorFactory { IValidator<T> GetValidator<T>(); IValidator GetValidator(Type type); }
ObjectFactory.Configure(cfg => cfg.AddRegistry(new MyRegistry())); public class MyRegistry : Registry { public MyRegistry() { For<IValidator<Person>>() .Singleton() .Use<PersonValidator>(); } }
public class MyRegistry : Registry { public MyRegistry() { AssemblyScanner.FindValidatorsInAssemblyContaining<MyValidator>() .ForEach(result => { For(result.InterfaceType) .Singleton() .Use(result.ValidatorType); }); } }
protected void Application_Start() { RegisterRoutes(RouteTable.Routes); //Configure structuremap ObjectFactory.Configure(cfg => cfg.AddRegistry(new MyRegistry())); ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory()); //Configure FV to use StructureMap var factory = new StructureMapValidatorFactory(); //Tell MVC to use FV for validation ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(factory)); DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false; }
WPF
- آغاز کار با WPF
- آشنایی با WPF قسمت اول : ساختار سلسله مراتبی
- آشنایی با WPF قسمت دوم: Layouts بخش اول
- آشنایی با WPF قسمت سوم: Layouts بخش دوم
- آشنایی با WPF قسمت چهارم: کنترل ها
- آشنایی با WPF قسمت پنجم : DataContext بخش اول
- آشنایی با WPF قسمت پنجم : DataContext بخش دوم
- آشنایی با WPF قسمت ششم : DataContext بخش سوم
- انقیاد دادهها در WPF بخش اول
- انقیاد دادهها در WPF بخش دوم
- آشنایی با الگوی M-V-VM- قسمت اول
- M-V-VM - قسمت دوم
- آشنایی با الگوی M-V-VM - قسمت سوم
- آشنایی با الگوی M-V-VM - قسمت چهارم
- آشنایی با الگوی M-V-VM - قسمت پنجم
- Expression Blend WPF Tutorial
- طول و عرض WPF
- آموزش رایگان XAML از مایکروسافت
- سری آموزشی PRISM
- ویدیوهای رایگان آموزشی WPF
- ارتقاء از WinForms به WPF
- خلاصهای کاربردی در مورد Observable collection
- دو تنظیم ضروری VS.NET جهت کار با WPF و Silverlight
- معرفی WPF Extended toolkit
- WPF و قالبهایی جهت کنترل DataGrid
- خلاصهای از مبحث نمایش اطلاعات hierarchical در WPF
- WPF4 و ویندوز 7 : به خاطر سپاری لیست آخرین فایلهای گشوده شده توسط برنامه
- نمایش یک فایل PDF در WinForms ، WPF و سیلورلایت
- چند نکته در مورد WPF MediaElement و ویندوز XP
- تعیین Fallback font برای قلمهای فارسی در WPF
- آموزش ایجاد برنامههای چند زبانه در WPF
- آشنایی و استفاده از WCF Data Services در Visualstudio 2012
- بارگذاری UserControl در WPF به کمک الگوی MVVM
- معماری لایه بندی نرم افزار #1
- معماری لایه بندی نرم افزار #2
- معماری لایه بندی نرم افزار #3
- معماری لایه بندی نرم افزار #4
- نحوه نمایش تمام آیکونهای تعریف شده در یک قلم در WPF
- نحوه استخراج آیکونهای یک قلم در WPF
- مقیدسازی (DataBinding) در WPF زمانی که دسترسی به DataContext وجود ندارد
- فراخوانی یک متداز یک کنترل WPF از XAML
- آشنایی با Catel MVVM Frameowork
- استفاده از ItemsControl جهت ساختن کنترلهای پویا در WPF
- ساخت فرمهای generic در WPF و Windows Application
- استفاده از #F در پروژههای WPF
- Markup Extensions در XAML
- Debug کردن Binding در XAML
- Bind کردن Enum به ItemsSource در XAML
- دسترسی به فیلدهای Static در XAML
- نگاهی به درون سیستم Binding در WPF و یافتن مواردی که هنوز در حافظهاند
- بهبود کارآیی کنترلهای لیستی WPF در حین بارگذاری تعداد زیادی از رکوردها
- چگونه تشخیص دهیم UI Virtualization در WPF خاموش شده است؟
- اضافه کردن امکان ویرایش WPF DataGrid در صورت نامعتبر بودن سلول ها
- انقیاد RadioButtonها در WPF به یک Enum
- یکی کردن اسمبلیهای یک پروژهی WPF
- بستن یک پنجره از طریق ViewModel با استفاده از خصوصیتهای پیوست شده هنگام استفاده از الگوی MVVM
- حرکت روی سلولهای دیتا گرید با فشردن کلید Enter در برنامههای WPF
- دسترسی به Collectionها در یک ترد دیگر در WPF
- آموزش WAF
- آموزش WAF (بررسی ساختار همراه با پیاده سازی یک مثال)
- آموزش WAF (بررسی Commandها)
- آموزش WAF (مشاهده تغییرات خواص ViewModel در Controller)
- تصادفی کردن آیتمهای لیست با استفاده از Extension Method
- استفاده از AvalonEdit در WPF
- فرمت شرطی اطلاعات به کمک تریگرها در WPF
- first chance exception چیست؟
- معرفی کتابخانهی OxyPlot
- معرفی DNTProfiler
- چگونه برنامههای دات نت را خارج از ویژوال استودیو دیباگ کنیم؟
- اسکرول روان لیستهای مجازی سازی شده در WPF 4.5
- پیاده سازی INotifyPropertyChanged با استفاده از Unity Container
در ورژنهای قبلی ویژوال استودیو، در زمان بارگذاری پروژه، احتیاجی به اجرای نرم افزارهای تحریم گذر نبود؛ همانند ورژن 15.6. ولی در این ورژن که من نصب کردم بدلیل نصب خودکار کتابخانههای متریال دیزاین، باید از این گونه نرم افزارها نیز استفاده کرد.
درقسمت بعدی گزینه BlankApp را انتخاب و در قسمت Minimum Android Version که با انتخاب آن میتوانیم ورژن گوشیهای اندروید برای استفاده از این اپلیکیشن را انتخاب نماییم. به عنوان مثال با انتخاب اندروید 4.4 برنامه ما صرفا برای گوشیهای اندورید 4.4 به بالا جواب میدهد. بعد از تایید، پروژه باز شده که با این solution روبرو میشویم.
- قسمت Properties را اگر بازکنیم، با دو گزینه روبرو میشویم که یکی فایل android manifest هست و اگر روی properties آن کلیک و ویژگیهایی را انتخاب کنیم، بطور خودکار بر روی manifest تاثیر میگذارند. در قسمتهای بعد در این رابطه جداگانه بحث خواهیم کرد.
- در قسمت Asset که به معنای منابع اندروید میباشد، به عنوان مثال صفحات Razor، فونت و یا صفحات HTML و یا عکس و یا ... را میتوانیم قرار دهیم.
- در قسمت Resource که پوشههای آن layout ،mipmap ،values و resource.designer میباشند، در پوشه layout میتوانیم صفحات استاندارد اندروید را شروع به طراحی کنیم. درقسمت mipmap عکسها و یا فایلهای xml ایی را که قرار است استفاده کنیم، در پروژه قرار میدهیم. در قسمت value که بیشتر برای انتخاب و تغییر تم یا استفاده از Resourceها (همانند Asp.mvc که استفاده میکردیم) است که البته با ساختاری متفاوت در اندروید از آنها استفاده میکنیم، قرار میگیرند.
- در قسمت Resource.Designer که در مطالب بعد با آن آشنا خواهید شد، تمامی آیتمهای انتخابی از جمله Layout ها و عکسها و... با ذخیره کردن در این قسمت دخیره میشوند که بعد با رفرنس دادن از طریق resource پروژه میتوانیم از عکسها و لیآوتها در کد نویسی استفاده کنیم.
- در انتها جهت معرفی به mainactivity میرسیم که یک صفحه است شامل المنتها و اجزای مختلف و کاربر میتواند با آن ارتباط برقرار کند.
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)] public class MainActivity : AppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); } }
- در گزینه بعدی Mainluncher را میبینیم که تعیین کنندهی نقطه شروع اکتیویتی ما در بین اکتیویتیهای دیگر میباشد.
بدیهی است درایورهای مربوطه به گوشی اندروید را باید تهیه کرد که در سایت مربوط به سازنده و یا در سایتهای دیگر میتوانید دانلود کنید. اولین برنامه را مینویسیم که هدف از آن، اجرای 10 دکمه بصورت داینامیک هست و اینکه با کلیک بر روی هر کدام از دکمهها، رنگ آن آبی شود.
protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); LinearLayout ln; Button btn; // Set our view from the "main" layout resource SetContentView(Resource.Layout.activity_main); for (int i = 0; i < 5; i++) { btn = new Button(this); btn.Text = i.ToString(); ln= FindViewById<LinearLayout>(Resource.Id.linearLayout1); btn.Click += Btn_Click; ln.AddView(btn); } } private void Btn_Click(object sender, System.EventArgs e) { Button btntest = sender as Button; btntest.SetBackgroundColor(Android.Graphics.Color.Blue); } }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:minWidth="25px" android:minHeight="25px"> <LinearLayout android:orientation="vertical" android:minWidth="25px" android:minHeight="25px" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1" /> </RelativeLayout>
#define DEBUG ... #if DEBUG Console.WriteLine("You have defined DEBUG symbol"); #endif
#if STANDARD Console.WriteLine("You have defined STANDARD symbol"); #elif PROFESSIONAL Console.WriteLine("You have defined PROFESSIONAL symbol"); #elif ULTIMATE Console.WriteLine("You have defined ULTIMATE symbol"); #endif
#if STANDARD && EVAL Console.WriteLine("You have defined STANDARD and EVAL symbols"); #endif
پیش پردازندههای #warning و #error
#if STANDARD && EVAL Console.WriteLine("You have defined STANDARD and EVAL symbols"); #endif
#region و #endregion
#line
namespace CSPreProcessorDirectivesDemo { class Program { static void Main(string[] args) { inta a = 100; Console.ReadLine(); } } }
#line 400 "MyFile.cs" inta a = 100;
#pragma pragma-name pragma-arguments
#pragma warning disable #pragma warning restore
#pragma warning disable 414 #pragma warning disable 414, 3021
#checksum
#pragma checksum "filename" "{guid}" "checksum bytes"
// Guid for the interface IMyInterface. [Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4")] interface IMyInterface { void MyMethod(); } // Guid for the coclass MyTestClass. [Guid("936DA01F-9ABD-4d9d-80C7-02AF85C822A8")] public class MyTestClass : IMyInterface { public void MyMethod() {} }
class TestClass { static int Main() { #pragma checksum "file.cs" "{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "{012345678AB}" // New checksum } }
منابع :