تفاوت اعتبارسنجی ورودی ها با اعتبارسنجی مرتبط با قواعد تجاری
A next-level validation is domain validation, or as I've often seen referred, "business rule validation". This is more of a system state validation, "can I affect the change to my system based on the current state of my system". I might be checking the state of a single entity, a group of entities, an entire collection of entities, or the entire system. The key here is I'm not checking the request against itself, but against the system state.
Stack Overflow Developer Desktop Build - 2017
One of the things we're big on at Stack Exchange is hardware - we love it. More importantly, we love not waiting on it. With that in mind, we upgrade our developer machines every 2 years. In case it helps anyone else, I'm posting our current parts list here. This isn't set in stone, we review and update it to the latest tech every time we build a machine. We also customize the build for each developer if needed - for example those needing extra space or specific display connections, etc. I'll try and keep this page updated as we make changes
This article dives into the mock questions I would ask, along with responses that are my personal best guess to the answers. Could my answers not reflect actual opinions shared by the team at Microsoft? Sure, but I'm hoping folks from the .NET team can jump in to correct me if I am way off base.
This is a rather interesting time for .NET – what's being done shapes the future of .NET for the next decade. Let's ask the honest questions and hopefully all of us will understand the new .NET ecosystem a little better.
مدیریت تغییرات گریدی از اطلاعات به کمک استفاده از الگوی واحد کار مشترک بین ViewModel و لایه سرویس
در این صفحه با کلیک بر روی دکمه به علاوه، یک ردیف به ردیفهای موجود اضافه شده و در اینجا میتوان اطلاعات کاربر جدیدی به همراه سطح دسترسی او را وارد و ذخیره کرد و یا حتی اطلاعات کاربران موجود را ویرایش نمود. اگر بخواهیم مانند مراحلی که در قسمت قبل در مورد تعریف یک صفحه جدید در برنامه توضیح داده شد، عمل کنیم، به صورت خلاصه به ترتیب ذیل عمل شده است:
1) ایجاد صفحه تغییر مشخصات کاربر
ابتدا صفحه Views\Admin\AddNewUser.xaml به پروژه ریشه که Viewهای برنامه در آن تعریف میشوند، اضافه شده است. به همراه دو دکمه و یک ListView که تطابق بهتری با قالب متروی مورد استفاده دارد.
2) تنظیم اعتبارسنجی صفحه اضافه شده
مرحله بعد تعریف هر صفحهای در سیستم، مشخص سازی وضعیت دسترسی به آن است:
/// <summary> /// افزودن و مدیریت کاربران سیستم /// </summary> [PageAuthorization(AuthorizationType.ApplyRequiredRoles, "IsAdmin, CanAddNewUser")]
3) تغییر منوی برنامه جهت اشاره به صفحه جدید
در ادامه در فایل منوی برنامه Views\MainMenu.xaml تعریف دسترسی به صفحه Views\Admin\AddNewUser.xaml قید شده است:
<Button Style="{DynamicResource MetroCircleButtonStyle}" Height="55" Width="55" Command="{Binding DoNavigate}" CommandParameter="\Views\Admin\AddNewUser.xaml" Margin="2"> <Rectangle Width="28" Height="17.25"> <Rectangle.Fill> <VisualBrush Stretch="Fill" Visual="{StaticResource appbar_user_add}" /> </Rectangle.Fill> </Rectangle> </Button>
4) ایجاد ViewModel متناظر با صفحه
مرحله نهایی تعریف صفحه AddNewUser، افزودن ViewModel متناظر با آن است که سورس کامل آنرا در فایل ViewModels\Admin\AddNewUserViewModel.cs پروژه Infrastructure میتوانید ملاحظه کنید.
نکته مهم این ViewModel، ارائه خاصیت لیست کاربران از نوع ObservableCollection به View و گرید برنامه است:
public ObservableCollection<User> UsersList { set; get; }
/// <summary> /// جهت مقاصد انقیاد دادهها در دبلیو پی اف طراحی شده است /// لیستی از کاربران سیستم را باز میگرداند /// </summary> /// <param name="count">تعداد کاربر مد نظر</param> /// <returns>لیستی از کاربران</returns> public ObservableCollection<User> GetSyncedUsersList(int count = 1000) { _users.OrderBy(x => x.FriendlyName).Take(count) .Load(); // For Databinding with WPF. // Before calling this method you need to fill the context by using `Load()` method. return _users.Local; }
در اینجا از قابلیت خاصیتی به نام Local که یک ObservableCollection تحت نظر EF را بازگشت میدهد، استفاده شده است. برای استفاده از این خاصیت، ابتدا باید کوئری خود را تهیه و سپس متد Load را بر روی آن فراخوانی کرد. سپس خاصیت Local بر اساس اطلاعات کوئری قبلی پر و مقدار دهی خواهد شد.
علت انتخاب نام Synced برای این متد، تحت نظر بودن اطلاعات خاصیت Local است تا زمانیکه Context تعریف شده زنده نگه داشته شود. به همین جهت در برنامه جاری از روش زنده نگه داشتن Context به ازای یک ViewModel استفاده شده است.
به Context، توسط اینترفیس IUnitOfWork تزریق شده در سازنده کلاس ViewModel میتوان دسترسی یافت. چون در اینجا از تزریق وابستگیها استفاده شده است، وهلهای که IUnitOfWork کلاس AddNewUserViewModel را تشکیل میدهد، دقیقا همان وهلهای است که در کلاس UsersService لایه سرویس استفاده شده است. در نتیجه، در گرید برنامه هر تغییری اعمال شود، تحت نظر IUnitOfWork خواهد بود و صرفا با فراخوانی متد uow.ApplyAllChanges آن، کلیه تغییرات تمام ردیفهای تحت نظر EF به صورت خودکار در بانک اطلاعاتی درج و یا به روز خواهند شد.
همچنین در مورد ViewModelContextHasChanges نیز در قسمت قبل بحث شد. در اینجا پیاده سازی کننده آن صرفا خاصیت uow.ContextHasChanges است. به این ترتیب اگر کاربر، تغییری را در صفحه داده باشد و بخواهد به صفحه دیگری رجوع کند، با پیام زیر مواجه خواهد شد:
از همین خاصیت برای فعال و غیرفعال کردن دکمه ذخیره سازی اطلاعات نیز استفاده شده است:
/// <summary> /// فعال و غیرفعال سازی خودکار دکمه ثبت /// این متد به صورت خودکار توسط RelayCommand کنترل میشود /// </summary> private bool canDoSave() { // آیا در حین نمایش صفحهای دیگر باید به کاربر پیغام داد که اطلاعات ذخیره نشدهای وجود دارد؟ return ViewModelContextHasChanges; }
/// <summary> /// رخداد ذخیره سازی اطلاعات را دریافت میکند /// </summary> public RelayCommand DoSave { set; get; }
DoSave = new RelayCommand(doSave, canDoSave);
این بررسی نیز بسیار سبک و سریع است. از این جهت که تغییرات Context در حافظه نگهداری میشوند و مراجعه به آن مساوی مراجعه به بانک اطلاعاتی نیست.
[fail]: OmniSharp.MSBuild.ProjectManager Attemped to update project that is not loaded: c:\MVC3\MVC3.csproj [info]: OmniSharp.OmniSharpWorkspace Miscellaneous file: c:\MVC3\Models\AppContext.cs added to workspace
محدودیتهای کار با اشیاء COM در NET Core 2x.
پیاده سازی پشتیبانی از اشیاء COM در NET Core 2x. به همراه اینترفیس IDispatch نیست. به این معنا که از مفهوم «late binding» پشتیبانی نمیکند. حدود 10 سال قبل در زمان ارائهی C# 4.0، واژهی کلیدی dynamic نیز ارائه شد که یکی از مهمترین اهداف آن، ساده سازی کار با اشیاء COM و پشتیبانی از Late binding بود:
dynamic excel = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application", true)); excel.Visible = true; Console.WriteLine("Press Enter to close Excel."); Console.ReadLine(); excel.Quit();
System.__ComObject does not contain a definition for 'Visible'
یک نکته: NET Core 3x. از Late binding پشتیبانی میکند.
روش کار با اشیاء COM در NET Core 2x.
چون NET Core 2x. از late binding اشیاء COM پشتیبانی نمیکند، میتوان در اینجا از روش قدیمیتر کار با اشیاء COM که استفادهی از «Interop assemblies» نام دارد، استفاده کرد. Interop assemblies در حقیقت محصور کنندههای اشیاء COM هستند که امکان کار مستقیم با آنها را از طریق early binding میسر میکنند. در یک چنین حالتی، کدهای فوق برای دسترسی به اشیاء COM کار با اکسل، به صورت زیر که early binding نام دارد، تغییر میکند:
using Excel = Microsoft.Office.Interop.Excel; // ... var excel = new Excel.Application(); excel.Visible = true; Console.WriteLine("Press Enter to close Excel."); Console.ReadLine(); excel.Quit();
روش تولید Interop assemblies
هنوز خود NET Core. روشی را برای تولید Interop assemblies ارائه ندادهاست و تولید آنها یکی از معدود مواردی است که نیاز به نصب Visual Studio را دارد. برای این منظور یک پروژهی خالی (از هر نوعی) را که بر اساس NET Framework 4x. تهیه میشود، در VS آغاز کنید و سپس در solution explorer بر روی پروژهی ایجاد شده کلیک راست کرده و گزینهی Add > Reference را انتخاب کنید. در صفحهی باز شده، گزینهی COM آنرا باید انتخاب کنید. در اینجا است که میتوانید با انتخاب یکی از موارد، ارجاعی را به آن شیء COM اضافه کنید.
پس از اینکار:
- ابتدا این ارجاع اضافه شده را در solution explorer انتخاب کرده و در پایین صفحه، در قسمت برگهی خواص آن، گزینهی «Embed Interop Types» آنرا به false تنظیم کنید.
- سپس یکبار پروژه را نیز کامپایل کنید.
این مراحل سبب تولید یک فایل dll خواهند شد که Interop assembly نام دارد و هم در برنامههای NET. و هم NET Core.، قابل استفادهاست.
روش استفاده از Interop assemblies در برنامههای NET Core.
اکنون که یک فایل dll را از شیء COM انتخابی، در یک پروژهی مجزای مبتنی بر NET 4x. تولید کردیم، روش استفادهی از آن در یک برنامهی دیگر مبتنی بر NET Core. به صورت زیر است:
<ItemGroup> <Reference Include="Interop.WIA"> <HintPath>..\DNTScanner.Core.TypeLibrary\bin\Debug\Interop.WIA.dll</HintPath> <EmbedInteropTypes>True</EmbedInteropTypes> </Reference> </ItemGroup>
یک نکته: اگر EmbedInteropTypes را به true تنظیم کردید، نیاز به بستهی Microsoft.CSharp را نیز خواهید داشت:
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' "> <Reference Include="Microsoft.CSharp" /> </ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <PackageReference Include="Microsoft.CSharp" Version="4.5.0" /> </ItemGroup>
روش دیگر استفاده از Interop assemblies در برنامههای NET Core.
روش فوق، جهت کار با فایلهای dll ای است که خودمان تولید کردهایم. برای سایر حالاتی که این موارد در سیستم نصب شدهاند (مانند Office Primary Interop Assemblies (PIA))، پس از افزودن ارجاعی به COM reference مدنظر، فایل csproj همان پروژهی NET 4x. را باز کرده و قسمت COMReference آنرا در اینجا (در فایل csproj پروژهی NET Core.) کپی کنید:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <!-- The following 'COMReference' items were copied from a .NET Framework project. They were added by using the Visual Studio COM References window. See https://docs.microsoft.com/en-us/visualstudio/ide/managing-references-in-a-project?view=vs-2017. Observe the 'EmbedInteropTypes' tag value. See https://docs.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2017#comreference --> <ItemGroup> <COMReference Include="Microsoft.Office.Core"> <Guid>{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}</Guid> <VersionMajor>2</VersionMajor> <VersionMinor>8</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> <COMReference Include="Microsoft.Office.Interop.Excel"> <Guid>{00020813-0000-0000-C000-000000000046}</Guid> <VersionMajor>1</VersionMajor> <VersionMinor>9</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> <COMReference Include="VBIDE"> <Guid>{0002E157-0000-0000-C000-000000000046}</Guid> <VersionMajor>5</VersionMajor> <VersionMinor>3</VersionMinor> <Lcid>0</Lcid> <WrapperTool>primary</WrapperTool> <Isolated>False</Isolated> <EmbedInteropTypes>True</EmbedInteropTypes> </COMReference> </ItemGroup> </Project>
سپس یک نمونه از MS Office automation را توسط اشیاء COM آن به صورت زیر میتوان پیاده سازی کرد:
using System; using System.Reflection; using Excel = Microsoft.Office.Interop.Excel; namespace ExcelDemo { class Program { public static void Main(string[] args) { Excel.Application excel; Excel.Workbook workbook; Excel.Worksheet sheet; Excel.Range range; try { // Start Excel and get Application object. excel = new Excel.Application(); excel.Visible = true; // Get a new workbook. workbook = excel.Workbooks.Add(Missing.Value); sheet = (Excel.Worksheet)workbook.ActiveSheet; // Add table headers going cell by cell. sheet.Cells[1, 1] = "First Name"; sheet.Cells[1, 2] = "Last Name"; sheet.Cells[1, 3] = "Full Name"; sheet.Cells[1, 4] = "Salary"; // Format A1:D1 as bold, vertical alignment = center. sheet.get_Range("A1", "D1").Font.Bold = true; sheet.get_Range("A1", "D1").VerticalAlignment = Excel.XlVAlign.xlVAlignCenter; // Create an array to multiple values at once. string[,] saNames = new string[5, 2]; saNames[0, 0] = "John"; saNames[0, 1] = "Smith"; saNames[1, 0] = "Tom"; saNames[1, 1] = "Brown"; saNames[2, 0] = "Sue"; saNames[2, 1] = "Thomas"; saNames[3, 0] = "Jane"; saNames[3, 1] = "Jones"; saNames[4, 0] = "Adam"; saNames[4, 1] = "Johnson"; // Fill A2:B6 with an array of values (First and Last Names). sheet.get_Range("A2", "B6").Value2 = saNames; // Fill C2:C6 with a relative formula (=A2 & " " & B2). range = sheet.get_Range("C2", "C6"); range.Formula = "=A2 & \" \" & B2"; // Fill D2:D6 with a formula(=RAND()*100000) and apply format. range = sheet.get_Range("D2", "D6"); range.Formula = "=RAND()*100000"; range.NumberFormat = "$0.00"; // AutoFit columns A:D. range = sheet.get_Range("A1", "D1"); range.EntireColumn.AutoFit(); // Make sure Excel is visible and give the user control // of Microsoft Excel's lifetime. excel.Visible = true; excel.UserControl = true; } catch (Exception e) { Console.WriteLine($"Error: {e.Message} Line: {e.Source}"); } } } }
How to automate Microsoft Excel from Microsoft Visual C#.NET
This command allows cleaning up project and assembly references that have no actual usages in source code. You can apply this command on a project, solution folder, or the entire solution. Before deletion is complete, you will be able to see all references that are going to be removed and. if necessary, preserve the ones that you want to keep.
> ng generate <blueprint> <options>
ایجاد کامپوننتهای جدید توسط Angular CLI
دستور ایجاد یک کامپوننت جدید توسط Angular CLI به نحو زیر است:
> ng generate component customer
> ng g c customer
گزینههای ایجاد کدهای جدید در Angular CLI
اگر به اولین دستور بحث جاری دقت کنید، قسمت <options> نیز برای آن درنظر گرفته شدهاست. تعدادی از مهمترین گزینههایی را که در اینجا میتوان ذکر کرد به شرح زیر هستند:
گزینه | Alias (میانبر/نام مستعار) | توضیح |
flat-- | آیا باید برای آن پوشهای ایجاد نشود؟ (flat = بدون پوشه در اینجا) (پیش فرض آن ایجاد یک پوشهی جدید است). اگر میخواهیم ایجاد نشود، باید flat true-- را ذکر کرد. | |
inline-template-- | it- | آیا قالب کامپوننت، درون فایل ts. آن قرار گیرد؟ (پیش فرض آن، false است) |
inline-style-- | is- | آیا شیوه نامهی کامپوننت، داخل فایل ts. آن قرار گیرد؟ (پیش فرض آن، false است) |
spec-- | آیا فایل spec نیز تولید شود؟ (پیش فرض آن true است) اگر میخواهیم این فایل ایجاد نشود باید spec false-- را ذکر کرد. | |
view-encapsulation-- | ve- | تعیین نوع استراتژی view encapsulation مورد استفاده (مانند Emulated). |
change-detection-- | cd- | تعیین استراتژی change detection مورد استفاده (مانند OnPush). |
dry-run-- | d- | گزارش فایلهای تولیدی، بدون نوشتن و تولید آنها (پیش فرض آن false است) |
prefix-- | تعیین صریح prefix مورد استفادهی در حین مقدار دهی selectorها که در قسمت قبل در مورد آن بحث شد. |
برای مثال اگر خواستیم کامپوننتی را به همراه قالبها و شیوهنامههای inline (قرار گرفتهی داخل فایل ts. آن) تولید کنیم، میتوان از دستور ذیل کمک گرفت:
>ng generate component customer --inline-template --inline-style
>ng g c customer –it -is
اگر صرفا دستور ng generate component customer را اجرا کنیم (بدون هیچ گزینهی اضافهتری)، فایلهای ts (کلاس کامپوننت)، css (فایل شیوه نامه)، html (فایل قالب) و spec (فایل آزمون واحد کامپوننت) به صورت خودکار تولید خواهند شد.
همانطور که پیشتر نیز عنوان شد، اگر مطمئن نیستید که دستور درحال فراخوانی، چه فایلها و پوشههایی را ایجاد میکند، با ذکر پرچم dry-run-- و یا به صورت خلاصه d-، دستور مدنظر را شبیه سازی کنید تا صرفا گزارشی را از فایلهایی که قرار است تولید شوند، ارائه دهد.
نکتهی مهم دیگری که به همراه دستورات Angular CLI هستند، به روز رسانی خودکار فایل app.module.ts است:
>ng g c customer installing component create src\app\customer\customer.component.css create src\app\customer\customer.component.html create src\app\customer\customer.component.spec.ts create src\app\customer\customer.component.ts update src\app\app.module.ts
import { CustomerComponent } from './customer/customer.component'; @NgModule({ declarations: [ AppComponent, CustomerComponent ]})
مشاهدهی تغییرات انجام شدهی توسط Angular CLI به کمک سورس کنترل
همانطور که در قسمت قبل نیز عنوان شد، دستور ng new، کار آغاز یک مخزن Git را نیز به صورت خودکار انجام میدهد. در اینجا هر دستوری که توسط Angular CLI اجرا شود، به این مخزن کد commit خواهد شد.
برای مثال اگر کل پوشهی برنامه را توسط VSCode باز کنیم (کلیک راست در داخل ریشهی اصلی پروژه و انتخاب گزینهی Open With Code)، با مراجعهی به لیست تغییرات و بررسی diff آنها، به سادگی میتوان تشخیص داد که چه تغییراتی بر روی فایلها اعمال شدهاند.
ایجاد سایر اجزای جدید برنامه توسط Angular CLI
نام جزء | Alias | دستور |
service | s | ng g service customer-data |
pipe | p | ng g pipe init-caps |
class | cl | ng g class customer-model |
directive | d | ng g directive search |
interface | i | ng g interface orders |
enum | e | ng g enum gender |
module | m | ng generate module sales |
نکات تکمیلی
- در حین ایجاد یک directive جدید، پوشهای را برای آن ایجاد نمیکند. اگر میخواهید اینکار به صورت flat (بدون پوشه در اینجا) انجام نشود، گزینهی flat false-- را نیز قید کنید.
- در حین ایجاد یک سرویس جدید، اخطار «WARNING Service is generated but not provided, it must be provided to be used» را دریافت خواهید کرد. علت اینجا است که Angular CLI نمیداند که این سرویس را باید به کامپوننت خاصی اضافه کند یا به ماژول برنامه. به همین جهت یا باید به صورت دستی فایل src\app\app.module.ts را ویرایش و قسمت providers آنرا بر اساس نام این سرویس جدید تکمیل کرد و یا توسط سوئیچ m میتوان ماژول مدنظر را دقیقا ذکر کرد:
> ng g s sales -m app.module
این نکته در مورد تمام اجزایی که فایل app.module را به روز رسانی میکنند نیز صادق است. اگر برای مثال کامپوننتی قرار است ماژول جدید دیگری را به روز رسانی کند، میتوانید به صورت صریح نام ماژول آنرا قید کنید؛ در غیراینصورت از همان app.module پیش فرض استفاده خواهد شد.
- همانطور که مشاهده میکنید امکان تولید کلاس، اینترفیس و enum تایپاسکریپتی نیز در اینجا پیش بینی شدهاست. اگر خواستید کلاسی را درون پوشهی خاصی قرار دهید میتوانید محل پوشهی آنرا دقیقا ذکر کنید (در مورد اینترفیسها و enums و سایر اجزاء نیز به همین صورت):
> ng g cl models/customer
تغییر تنظیمات پیش فرض تولید کد پروژهی جاری
در قسمت قبل «تغییر پیش فرضهای عمومی Angular CLI» را بررسی کردیم. در اینجا نیز میتوان یکسری از خواص فایل angular-cli.json. را بازنویسی کرد؛ در قسمت defaults آن:
"defaults": { "styleExt": "css", "component": {} }
> ng set defaults.component.flat false > ng set defaults.directive.flat false > ng set defaults.styleExt sass
"defaults": { "styleExt": "sass", "component": { "flat": false }, "directive": { "flat": false } }
و اگر VSCode استفاده میکنید، به همراه intellisense کاملی در مورد اجزای مختلف این فایل json است (این intellisense را به صورت خودکار بر اساس اسکیمای این فایل و سرویس زبان Angular تهیه میکند).