نظرات مطالب
افزودن SQL Server CE 4.0 به لیست توزیع مجدد در InstallShield 2010

Features not supported in SQL Server Compact 4.0

علتش این است که مثلا LINQ to SQL دیگر با SQL CE 4 پشتیبانی نمی‌شود چون خود LINQ to SQL دیگر توسط MS توسعه جدی پیدا نمی‌کند و به EF سوئیچ شده و EF هم پروایدر رسمی برای SQL CE 4 داره.

مطالب
یافتن لیست اسمبلی‌های ارجاعی

اگر برنامه‌ی شما برای مثال با SMO مربوط به اس کیوال سرور 2008 کامپایل شود، روی سروری با SQL Server 2005 کار نخواهد کرد و پیغام می‌دهد که نگارش 10 اسمبلی Microsoft.SqlServer.Management.Sdk.Sfc یافت نشد.
یک راه حل آن، نصب Microsoft SQL Server 2008 Management Objects بر روی سرور است، یا راه حل دوم، پیدا کردن اسمبلی‌هایی که برنامه به آن‌ها ارجاع دارد و کپی کردن آن‌ها کنار فایل اجرایی برنامه در سرور. (درست کردن یک برنامه پرتابل دات نتی، یا نسبتا پرتابل!)
برای این منظور کلاس زیر تهیه شده است که مسیر فایل اجرایی یا dll یک پروژه را دریافت کرده و لیست تمام ارجاعات به آن‌را به صورت بازگشتی پیدا می‌کند. (البته در قسمت یافتن مسیر اسمبلی‌ها، اسمبلی‌های سیستمی که با خود دات نت فریم ورک نصب می‌شوند، حذف شده است)

using System.Collections.Generic;
using System.Reflection;
using System.IO;

namespace App
{
class CFindRef
{
#region Fields (2)

/// <summary>
/// لیستی جهت نگهداری نام اسمبلی‌ها
/// </summary>
private readonly List<string> _assemblies = new List<string>();
/// <summary>
/// لیستی جهت نگهداری مسیر اسمبلی‌ها
/// </summary>
private readonly List<string> _filePath = new List<string>();

#endregion Fields

#region Constructors (1)

/// <summary>
/// سازنده کلاس
/// </summary>
/// <param name="fileName">مسیر اولیه اسمبلی مورد نظر</param>
public CFindRef(string fileName)
{
ListReferences(fileName);
}

#endregion Constructors

#region Properties (2)

/// <summary>
/// لیست مسیر اسمبلی‌هایی که به آن‌ها ارجاعی وجود دارد منهای موارد سیستمی
/// </summary>
public List<string> ReferencedFiles
{
get
{
_filePath.Sort();
return _filePath;
}
}

/// <summary>
/// لیست کامل اسمبلی‌هایی که اسمبلی ما به آن‌ها وابسته است
/// </summary>
public List<string> ReferencedNames
{
get
{
_assemblies.Sort();
return _assemblies;
}
}

#endregion Properties

#region Methods (1)

// Private Methods (1)

/// <summary>
/// متدی بازگشتی جهت یافتن لیست ارجاعات
/// </summary>
/// <param name="path">مسیر یا نام اسمبلی</param>
private void ListReferences(string path)
{
//آیا تکراری است؟
if (_assemblies.Contains(path))
return;

Assembly asm;
// آیا فایل است یا نام کامل اسمبلی
if (File.Exists(path))
{
// load the assembly from a path
asm = Assembly.LoadFrom(path);
}
else
{
// سعی در بارگذاری اسمبلی
try
{
asm = Assembly.Load(path);
}
catch
{
asm = null; //جای بهبود دارد
}
}

if (asm == null) return;

// افزودن به لیست نام‌ها
_assemblies.Add(path);
string asmLocation = asm.Location;
//حذف موارد سیستمی از لیست مسیر فایل‌ها
if (asmLocation != null && !asmLocation.Contains("\\System.")
&& !asmLocation.Contains("\\mscorlib"))
_filePath.Add(asmLocation);

// پیدا کردن ارجاع‌ها
AssemblyName[] imports = asm.GetReferencedAssemblies();
// iterate
foreach (AssemblyName asmName in imports)
{
// فراخوانی بازگشتی جهت یافتن تمامی ارجاعات
ListReferences(asmName.FullName);
}
}

#endregion Methods
}
}

مثالی در مورد نحوه‌ی استفاده از آن:

CFindRef cfr = new CFindRef(@"C:\App\test.exe");
foreach (var asmRef in cfr.ReferencedFiles)
{
textBox1.Text += asmRef + Environment.NewLine;
//Application.DoEvents();
}
برای نمونه، برنامه‌ای که از SMO‌ مربوط به اس کیوال سرور 2008 استفاده می‌کند، این لیست ارجاعات را دارد:

C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.ConnectionInfo\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ConnectionInfo.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Dmf\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Dmf.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Management.Sdk.Sfc\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Management.Sdk.Sfc.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.ServiceBrokerEnum\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ServiceBrokerEnum.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.Smo\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.Smo.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.SqlClrProvider\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.SqlClrProvider.dll
C:\WINDOWS\assembly\GAC_MSIL\Microsoft.SqlServer.SqlEnum\10.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.SqlEnum.dll
با کپی کردن همین فایل‌ها کنار فایل اجرایی برنامه‌ای که قرار است روی سروری با SQL Server 2005 کار کند، مشکل برطرف می‌شود و برنامه بدون مشکل کار خواهد کرد.

برنامه آماده هم در این زمینه موجود است، برای مثال CheckAsm
مشاهده سایت

مطالب
مسیریابی در Angular - قسمت دوم - مسیریابی ماژول‌ها
اغلب برنامه‌های بزرگ Angular، ویژگی‌های مختلف خود را به ماژول‌های مجزایی تبدیل می‌کنند. این ماژول‌ها شبیه به مفهوم Area در ASP.NET MVC هستند و هدف آن‌ها نظم بخشیدن به کامپوننت‌های ویژه‌ی یک قسمت خاص از برنامه، در ناحیه‌‌ای مختص به آن می‌باشد. به علاوه ایجاد ماژول‌ها، قابلیت lazy loading مسیریابی‌ها را نیز مسیر می‌کند. هر برنامه‌ی Angular حداقل به همراه یک ماژول است که بر اساس قراردادی، AppModule نام گرفته‌است و در فایل src\app\app.module.ts قرار دارد. با توسعه‌ی برنامه و بیشتر شدن قابلیت‌های آن، استفاده‌ی از این تک ماژول پیش فرض، مشکل تداخل مسئولیت‌ها را به همراه خواهد داشت. برای رفع این مشکل توصیه شده‌است که کامپوننت‌های مرتبط به یک ویژگی خاص را درون ماژولی مختص به خود قرار دهید؛ برای مثال مانند ماژول محصولات، برای نظم دهی به کامپوننت‌های لیست محصولات، جزئیات محصولات، ویرایش محصولات و غیره، ماژول کاربران برای تعریف کامپوننت‌های لاگین، تغییر کلمه‌ی عبور و امثال آن. در این قسمت قصد داریم نحوه‌ی تنظیمات مسیریابی و هدایت کاربران را به ماژول‌های مختلف برنامه، بررسی کنیم.


تنظیم مسیریابی ماژول‌ها

در اینجا نیازی به تنظیم base path نیست و این تنظیم تنها یکبار به ازای کل برنامه انجام می‌شود. همانطور که در قسمت قبل نیز عنوان شد، ماژول مسیریابی Angular و یا همان RouterModule، به همراه سرویسی برای دسترسی به امکانات آن، تنظیمات مسیریابی و یک سری دایرکتیو مانند routerLink، جهت تعامل با آن است. از آنجائیکه سرویس ماژول مسیریابی در فایل src\app\app-routing.module.ts تعریف و تنظیم شده‌است، باید اطمینان حاصل کرد که این سرویس تنها یکبار در طول عمر برنامه وهله سازی می‌شود و از آنجائیکه هر ماژول تنظیمات مجزای مسیریابی خود را خواهد داشت، دیگر نمی‌توان از متد RouterModule.forRoot سراسری استفاده کرد و در اینجا باید از متد forChild این ماژول، جهت تعریف تنظیمات مسیریابی‌های ماژول‌های مختلف کمک گرفت. متد forChild نیز شبیه به همان آرایه‌ی تنظیمات مسیریابی متد forRoot را دریافت می‌کند.

یک مثال: در ادامه‌ی مثالی که در قسمت قبل به کمک Angular CLI ایجاد کردیم، ماژول جدید محصولات را به همراه تنظیمات ابتدایی مسیریابی آن ایجاد می‌کنیم:
 >ng g m product --routing
پس از اجرای این دستور، ماژول جدید محصولات در فایل src\app\product\product.module.ts و تنظیمات ابتدایی آن در فایل src\app\product\product-routing.module.ts تشکیل می‌شوند:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class ProductRoutingModule { }
همانطور که مشاهده می‌کنید، در حین تشکیل تنظیمات ابتدایی مسیریابی این ماژول جدید، اینبار از متد forChild استفاده شده‌است و نه متد forRoot که مختص به ماژول اصلی و سراسری برنامه‌است.
سپس ProductRoutingModule به قسمت imports ماژول محصولات به صورت خودکار اضافه شده‌است:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { ProductRoutingModule } from './product-routing.module';

@NgModule({
  imports: [
    CommonModule,
    ProductRoutingModule
  ],
  declarations: []
})
export class ProductModule { }

در ادامه کامپوننت جدید لیست محصولات را به این ماژول اضافه می‌کنیم:
 >ng g c product/ProductList
به این ترتیب داخل پوشه‌ای به نام produc-list، کامپوننت product-list.component.ts تشکیل خواهد شد. در حقیقت این دستور اعمال ذیل را انجام می‌دهد:
 installing component
  create src\app\product\product-list\product-list.component.css
  create src\app\product\product-list\product-list.component.html
  create src\app\product\product-list\product-list.component.spec.ts
  create src\app\product\product-list\product-list.component.ts
  update src\app\product\product.module.ts
اگر به سطر آخر آن دقت کنید، کار به روز رسانی فایل ماژول محصولات، جهت درج این کامپوننت جدید، در قسمت declarations فایل product.module.ts نیز به صورت خودکار انجام شده‌است:
import { ProductListComponent } from './product-list/product-list.component';

@NgModule({
  imports: [
  ],
  declarations: [ProductListComponent]
})
export class ProductModule { }

اکنون که این ماژول جدید را به همراه یک کامپوننت نمونه در آن تعریف کردیم، برای افزودن مسیریابی به آن، به فایل src\app\product\product-routing.module.ts مراجعه کرده و آرایه‌ی  Routes آن‌را تکمیل می‌کنیم:
import { ProductListComponent } from './product-list/product-list.component';

const routes: Routes = [
  { path: 'products', component: ProductListComponent }
];
ابتدا کامپوننت لیست محصولات import شده و سپس آرایه‌ی Routes مسیری را به این کامپوننت تعریف کرده‌ایم.

در ادامه می‌خواهیم لینکی را به این مسیریابی جدید اضافه کنیم. در قسمت قبل منویی را به برنامه اضافه کردیم. به همین جهت به فایل src\app\app.component.html مراجعه کرده و routerLink جدیدی را به آن اضافه می‌کنیم:
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <a class="navbar-brand">{{title}}</a>
    <ul class="nav navbar-nav">
      <li>
        <a [routerLink]="['/home']">Home</a>
      </li>
      <li>
        <a [routerLink]="['/products']">Product List</a>
      </li>
    </ul>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>
پیشتر لینکی را به کامپوننت welcome در قسمت قبل اضافه کرده بودیم. در اینجا لینک جدیدی را به کامپوننت لیست محصولات، در ذیل آن تعریف کرده‌ایم.
در اینجا نیز نحوه‌ی تعریف لینک‌ها مانند قبل است و آرایه‌ی تنظیمات پارامترهای لینک باید به مقدار خاصیت path تعریف شده اشاره کند.

اکنون دستور ng serve -o را صادر کنید تا برنامه در حافظه ساخته شده و در مرورگر نمایش داده شود. در ادامه اگر بر روی لینک لیست محصولات کلیک کنید، صفحه‌ی ذیل را مشاهده خواهید کرد:


به این معنا که برنامه اطلاعی از این مسیریابی جدید نداشته و صفحه‌ی یافت نشدن مسیریابی را که در قسمت قبل تنظیم کردیم، نمایش داده‌است. برای رفع این مشکل باید به فایل src\app\app.module.ts مراجعه کرده و این ماژول جدید را به آن معرفی کنیم:
import { ProductModule } from './product/product.module';

@NgModule({
  declarations: [
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,

    ProductModule,

    AppRoutingModule
  ],
در اینجا در ابتدا ماژول محصولات import شده و سپس به قسمت لیست imports ماژول App اضافه گردیده‌است. اکنون مسیریابی به این کامپوننت جدید واقع شده‌ی در قسمت ماژول محصولات، کار می‌کند:


نکته 1: علت اینکه ProductModule را پیش از AppRoutingModule تعریف کردیم این است که AppRoutingModule دارای تعریف مسیریابی ** یا catch all است که در قسمت قبل آن‌را جهت مدیریت مسیرهای یافت نشده به برنامه افزودیم. اگر ابتدا AppRoutingModule تعریف می‌شد و سپس ProductModule، هیچگاه فرصت به پردازش مسیریابی‌های ماژول محصولات نمی‌رسید؛ چون مسیر ** پیشتر برنده شده بود.
نکته 2: می‌توان در قسمت import متد RouterModule.forRoot را نیز مستقیما قرار داد (بجای AppRoutingModule). اگر این کار صورت گیرد، ابتدا مسیریابی‌های موجود در ماژول‌ها پردازش می‌شوند و در آخر مسیرهای موجود در RouterModule.forRoot صرفنظر از محل قرارگیری آن در این لیست بررسی خواهد شد (حتی اگر در ابتدای لیست قرار گیرد). هرچند جهت مدیریت بهتر برنامه، این متد به AppRoutingModule منتقل شده‌است. بنابراین اکنون «نکته‌ی 1» برقرار است.


انتخاب استراتژی مناسب نامگذاری مسیرها

هنگام کار کردن با تعدادی ویژگی مرتبط به هم قرار گرفته‌ی داخل یک ماژول، بهتر است روش نامگذاری مناسبی را برای تنظیمات مسیریابی آن درنظر گرفت تا مسیرهای تعیین شده علاوه بر زیبایی، وضوح بیشتری را نیز پیدا کنند. به علاوه این نامگذاری مناسب، گروه بندی مسیریابی‌ها و lazy loading آن‌ها را نیز ساده‌تر می‌کند.
استراتژی ابتدایی که به ذهن می‌رسد، نامگذاری هر مسیر بر اساس عملکرد آن‌ها است مانند products برای نمایش لیست محصولات، product/:id برای نمایش جزئیات محصولی خاص که در اینجا id پارامتر مسیریابی است و productEdit/:id برای ویرایش جزئیات یک محصول مشخص. همانطور که مشاهده می‌کنید، هرچند این مسیرها متعلق به یک ماژول هستند، اما مسیرهای تعیین شده‌ی برای آن‌ها اینگونه به نظر نمی‌رسد. بنابراین بهتر است تمام ویژگی‌های قرار گرفته‌ی درون یک ماژول را با مسیر ریشه‌ی یکسانی شروع کنیم. به این ترتیب نمایش لیست محصولات همان products باقی خواهد ماند اما برای نمایش جزئیات محصولی خاص از مسیر products/:id استفاده می‌کنیم (همان اسم جمع ریشه‌ی مسیر؛ بجای اسم مفرد). اینبار مسیر ویرایش جزئیات یک محصول به صورت products/:id/edit تنظیم خواهد شد:
products
products/:id
products/:id/edit
در اینجا نام ریشه‌ی یکسانی را برای عناصر مختلف قرارگرفته‌ی درون یک ماژول انتخاب کرده‌ایم؛ تا ارتباط بین آن‌ها بهتر مشخص شود و همچنین در آینده بتوان مباحث گروه بندی و lazy loading را نیز بر این اساس پیاده سازی کرد.


فعالسازی یک مسیر با کدنویسی

تا اینجا نحوه‌ی فعالسازی یک مسیر را با استفاده از دایرکتیو routerLink بررسی کردیم. اما گاهی از اوقات نیاز است تا بتوان با کدنویسی نیز کاربران را به مسیری خاص هدایت کرد. برای مثال پس از عملیات logout می‌خواهیم مجددا صفحه‌ی اول سایت نمایش داده شود. برای اینکار از سرویس Router مسیریاب Angular کمک گرفته می‌شود. ابتدا آن‌را در سازنده‌ی یک کامپوننت تزریق کرده و سپس می‌توان به قابلیت‌های آن مانند استفاده‌ی از متد navigate آن، در کدهای برنامه دسترسی یافت.
باید درنظر داشت که دایرکتیو routerLink نیز در پشت صحنه از همین متد navigate سرویس Router استفاده می‌کند. بنابراین تمام پارامترهای آن در متد navigate نیز قابل استفاده هستند. برای مثال زمانیکه تعداد پارامترهای routerLink یک مورد است، می‌توان آرایه‌ی آن‌را به یک رشته خلاصه کرد. یک چنین قابلیتی با متد navigate نیز میسر است.
متد navigate تنها قسمت‌هایی از URL جاری را تغییر می‌دهد. اگر نیاز باشد تا کل آدرس تعویض شود، می‌توان از متد دیگر سرویس Router به نام navigateByUrl استفاده کرد. این متد تمام URL segments موجود را با مسیر جدیدی جایگزین می‌کند. به علاوه برخلاف متد navigate، تنها یک رشته را به عنوان پارامتر می‌پذیرد.

در ادامه مثال جاری می‌خواهیم پیاده سازی ابتدایی login و logout را به برنامه اضافه کنیم. به همین منظور ابتدا ماژول جدید user را به همراه تنظیمات ابتدایی مسیریابی آن اضافه می‌کنیم:
 >ng g m user --routing
به این ترتیب دو فایل src\app\user\user-routing.module.ts و src\app\user\user.module.ts به برنامه اضافه می‌شوند.
همانند ماژول قبلی، نیاز است UserModule را به قسمت imports فایل src\app\app.module.ts نیز معرفی کنیم:
import { UserModule } from './user/user.module';

@NgModule({
  declarations: [
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,

    ProductModule,
    UserModule,

    AppRoutingModule
  ],

سپس کامپوننت جدید لاگین را به ماژول user برنامه اضافه می‌کنیم:
 >ng g c user/login
که اینکار سبب به روز رسانی فایل user.module.ts جهت تکمیل قسمت declarations آن با LoginComponent نیز می‌شود.

در ادامه به فایل src\app\user\user-routing.module.ts مراجعه کرده و مسیریابی جدیدی را به کامپوننت لاگین تعریف می‌کنیم:
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  { path: 'login', component: LoginComponent}
];
ابتدا این کامپوننت import شده و سپس یک path جدید را به آن انتساب می‌دهیم.

مرحله‌ی بعد، فعالسازی این مسیریابی است، با تعریف لینکی به آن. به همین جهت به فایل src\app\app.component.html مراجعه کرده و منوی برنامه را تکمیل می‌کنیم:
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <a class="navbar-brand">{{title}}</a>
    <ul class="nav navbar-nav">
      <li>
        <a [routerLink]="['/home']">Home</a>
      </li>
      <li>
        <a [routerLink]="['/products']">Product List</a>
      </li>
    </ul>
    <ul class="nav navbar-nav navbar-right">
      <li>
        <a [routerLink]="['/login']">Log In</a>
      </li>
    </ul>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>
اکنون دستور ng serve -o را صادر کنید تا برنامه در حافظه ساخته شده و در مرورگر نمایش داده شود و سپس بر روی لینک login کلیک کنید تا قالب ابتدایی آن نمایش داده شود:



تکمیل کامپوننت login و افزودن لینک logout

در ادامه می‌خواهیم یک فرم لاگین مقدماتی را پس از کلیک بر روی لینک لاگین نمایش دهیم و هدایت به صفحه‌ی لیست محصولات را پس از لاگین و مخفی کردن لینک لاگین و نمایش لینک خروج را در این حالت پیاده سازی کنیم. برای این منظور ابتدا اینترفیس خالی کاربر را ایجاد می‌کنیم:
 >ng g i user/user
که سبب ایجاد فایل src\app\user\user.ts خواهد شد. این اینترفیس را به صورت زیر تکمیل می‌کنیم:
export interface IUser {
    id: number;
    userName: string;
    isAdmin: boolean;
}

پس از آن یک سرویس ابتدایی اعتبارسنجی کاربران را نیز اضافه خواهیم کرد:
 >ng g s user/auth -m user/user.module
که سبب افزوده شدن سرویس auth.service.ts و همچنین به روز رسانی خودکار قسمت providers ماژول user.module.ts نیز می‌شود:
 installing service
  create src\app\user\auth.service.spec.ts
  create src\app\user\auth.service.ts
  update src\app\user\user.module.ts
اگر نام ماژول را ذکر نکنیم، سرویس مدنظر تولید خواهد شد، اما قسمت providers هیچ ماژولی به صورت خودکار تکمیل نمی‌شود.

پس از ایجاد قالب ابتدایی فایل auth.service.ts آن‌را به نحو ذیل تکمیل کنید:
import { IUser } from './user';
import { Injectable } from '@angular/core';

@Injectable()
export class AuthService {
  currentUser: IUser;

  constructor() { }

  isLoggedIn(): boolean {
    return !this.currentUser;
  }

  login(userName: string, password: string): boolean {
    if (!userName || !password) {
      return false;
    }

    if (userName === 'admin') {
      this.currentUser = {
        id: 1,
        userName: userName,
        isAdmin: true
      };
      return true;
    }

    this.currentUser = {
      id: 2,
      userName: userName,
      isAdmin: false
    };
    return true;
  }

  logout(): void {
    this.currentUser = null;
  }
}
در اینجا اگر کاربر هر نوع کلمه‌ی عبور و یا نام کاربری را وارد کند، به سیستم وارد خواهد شد. اگر نامش admin باشد، دسترسی admin پیدا می‌کند و همچنین متدهای logout با null کردن یوزر وارد شده‌ی به سیستم و isLoggedIn برای مشخص بودن نال نبودن شیء کاربر جاری، به این سرویس اضافه شده‌اند.

سپس کامپوننت لاگین واقع در فایل src\app\user\login\login.component.ts را به نحو ذیل تکمیل کنید:
import { Router } from '@angular/router';
import { AuthService } from './../auth.service';

import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  errorMessage: string;
  pageTitle = 'Log In';

  constructor(private authService: AuthService,
    private router: Router) { }

  ngOnInit() {
  }

  login(loginForm: NgForm) {
    if (loginForm && loginForm.valid) {
      let userName = loginForm.form.value.userName;
      let password = loginForm.form.value.password;
      if (this.authService.login(userName, password)) {
        this.router.navigate(['/products']);
      }
    } else {
      this.errorMessage = 'Please enter a user name and password.';
    };
  }
}
در این کامپوننت دو سرویس AuthService، که پیشتر ایجاد کردیم و سرویس Router، به سازنده‌ی کلاس تزریق شده‌اند.
از AuthService برای اعتبارسنجی کاربر و لاگین او به سیستم استفاده می‌کنیم و از سرویس مسیریاب Angular جهت فراخوانی متد navigate آن به صفحه‌ی مشاهده‌ی محصولات، پس از لاگین کاربر استفاده شده‌است.

اکنون می‌خواهیم قالب این کامپوننت را نیز تکمیل کنیم. پیش از آن به فایل src\app\user\user.module.ts مراجعه کرده و در قسمت imports آن FormsModule را نیز اضافه کنید:
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    UserRoutingModule
  ],

سپس فایل src\app\user\login\login.component.html را به نحو ذیل تغییر دهید:
<div class="panel panel-default">
  <div class="panel-heading">
    {{pageTitle}}
  </div>

  <div class="panel-body">
    <form class="form-horizontal" novalidate (ngSubmit)="login(loginForm)" #loginForm="ngForm" autocomplete="off">
      <fieldset>
        <div class="form-group" [ngClass]="{'has-error': (userNameVar.touched || 
                                               userNameVar.dirty) && 
                                               !userNameVar.valid }">
          <label class="col-md-2 control-label" for="userNameId">User Name</label>
          <div class="col-md-8">
            <input class="form-control" id="userNameId" type="text" 
                   placeholder="User Name (required)" required 
                   (ngModel)="userName" name="userName" #userNameVar="ngModel" />
            <span class="help-block" *ngIf="(userNameVar.touched ||
                                             userNameVar.dirty) &&
                                             userNameVar.errors">
                            <span *ngIf="userNameVar.errors.required">
                                User name is required.
                            </span>
            </span>
          </div>
        </div>

        <div class="form-group" [ngClass]="{'has-error': (passwordVar.touched || 
                                               passwordVar.dirty) && 
                                               !passwordVar.valid }">
          <label class="col-md-2 control-label" for="passwordId">Password</label>

          <div class="col-md-8">
            <input class="form-control" id="passwordId" type="password" 
                   placeholder="Password (required)" required 
                  (ngModel)="password" name="password" #passwordVar="ngModel" />
            <span class="help-block" *ngIf="(passwordVar.touched ||
                                             passwordVar.dirty) &&
                                              passwordVar.errors">
                            <span *ngIf="passwordVar.errors.required">
                                Password is required.
                            </span>
            </span>
          </div>
        </div>

        <div class="form-group">
          <div class="col-md-4 col-md-offset-2">
            <span>
               <button class="btn btn-primary"
                       type="submit"
                       style="width:80px;margin-right:10px"
                       [disabled]="!loginForm.valid">
                               Log In
               </button>
            </span>
            <span>
                <a class="btn btn-default"
                   [routerLink]="['/welcome']">
                     Cancel
                </a>
            </span>
          </div>
        </div>
      </fieldset>
    </form>
    <div class="has-error" *ngIf="errorMessage">{{errorMessage}}</div>
  </div>
</div>
تا اینجا صفحه‌ی لاگین نمایش داده شده و کاربر می‌تواند به سیستم وارد شود. تا زمانیکه کلمه‌ی عبور و نام کاربری وارد نشده باشند، دکمه‌ی login این فرم غیرفعال باقی می‌ماند. پس از آن کاربر با هر نوع ترکیبی از کلمه‌ی عبور و نام کاربری می‌تواند به سیستم وارد شده و بلافاصله به صفحه‌ی لیست محصولات هدایت می‌شود.

اکنون می‌خواهیم پس از ورود او، نام او را نمایش داده و همچنین دکمه‌ی logout را بجای login در منوی بالای سایت نمایش دهیم. به همین جهت در قالب کامپوننت App که منوی برنامه در آن تنظیم شده‌است، نیاز است بتوانیم به سرویس Auth سفارشی دسترسی یافته و خروجی متد isLoggedIn آن‌را بررسی کنیم. به همین منظور به فایل src\app\app.component.ts مراجعه کرده و آن‌را به صورت ذیل تکمیل کنید:
import { Router } from '@angular/router';
import { AuthService } from './user/auth.service';
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  pageTitle: string = 'Routing Lab';

  constructor(private authService: AuthService,
    private router: Router) { }

  logOut(): void {
    this.authService.logout();
    this.router.navigateByUrl('/welcome');
  }
}
در اینجا دو سرویس Auth و Router به سازنده‌ی کامپوننت App تزریق شده‌اند. به این ترتیب می‌توان از شیء authService در قالب این کامپوننت برای دسترسی به متد isLoggedIn و سایر خواص این سرویس استفاده کرد. همچنین از سرویس مسیریاب Angular برای پیاده سازی logout و هدایت کاربر به صفحه‌ی welcome کمک گرفته‌ایم. در اینجا از متد navigateByUrl استفاده شده‌است؛ از این جهت که پس از Logout دیگر حفظ URL Segments موجود بی‌مفهوم است و تمام آن‌ها باید پاک شده و با آدرس جدید جایگزین شوند.
پس از این تغییرات، اکنون می‌توان قالب src\app\app.component.html را به نحو ذیل تکمیل کرد:
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <a class="navbar-brand">{{title}}</a>
    <ul class="nav navbar-nav">
      <li>
        <a [routerLink]="['/home']">Home</a>
      </li>
      <li>
        <a [routerLink]="['/products']">Product List</a>
      </li>
    </ul>
    <ul class="nav navbar-nav navbar-right">
      <li *ngIf="authService.isLoggedIn()">
        <a>Welcome {{ authService.currentUser.userName }}</a>
      </li>
      <li *ngIf="!authService.isLoggedIn()">
        <a [routerLink]="['/login']">Log In</a>
      </li>
      <li *ngIf="authService.isLoggedIn()">
        <a (click)="logOut()">Log Out</a>
      </li>
    </ul>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>
همانطور که مشاهده می‌کنید، دوبار لاگین بودن کاربر جاری توسط متد authService.isLoggedIn بررسی شده‌است. اگر خروجی این متد true باشد، نام کاربری شخص به همراه لینک Logout نمایش داده می‌شود. اگر خروجی این متد false باشد، تنها لینک login نمایان شده و مابقی گزینه‌ها (لینک لاگین و نمایش نام شخص) از صفحه حذف می‌شوند.

اکنون اگر برنامه را توسط دستور ng serve -o اجرا کنید، صفحه‌ی لاگین و منوی بالای صفحه چنین شکلی را خواهد داشت:


پس از لاگین، لینک لاگین از منو حذف شده و سپس نام کاربری و لینک به logout نمایان می‌شوند.


اینبار اگر بر روی logout کلیک کنید، نام کاربری و لینک logout از صفحه حذف و مجددا لینک لاگین نمایش داده می‌شود.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: angular-routing-lab-01.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
نظرات مطالب
Implementing second level caching in EF code first
ظاهرا متد GetCacheKey به ازای کوئری‌های مختلف نتیجه یکسانی رو بر میگردونه و نهایتا همیشه دیتای کش شده نمایش داده میشه. مثلا دو کوئری زیر :
ctx.Entity.SingleOrDefault(a=>a.ID==1);
ctx.Entity.SingleOrDefault(a=>a.ID==2);
نظرات مطالب
مرتب سازی رکوردها به صورت اتفاقی در Entity framework
خیر. کوئری‌های EF طوری طراحی شدن که قابل انتقال به بانک‌های اطلاعاتی دیگر هم باشند و بتونید با تغییر کانکشن استرینگ بدون نگرانی آنچنانی، واقعا از یک بانک اطلاعاتی دیگر استفاده کنید.
در EF می‌شود raw sql هم نوشت: (^). در این حالت برنامه شما صرفا به بانک اطلاعاتی مورد استفاده گره خواهد خورد و اگر مثلا این BINARY_CHECKSUM در SQLite وجود خارجی نداشت، این برنامه و سیستم دیگر قابل انتقال نخواهند بود.
نظرات اشتراک‌ها
دوراهی انتخاب NHibernate و Entityframework
مشکل عمومی در بین برنامه نویس‌ها وجود دارد و آن هم این است که فکر می‌کنند آنی که سریع‌تر است بهتر است. خیر! در ADO.NET خام تمام مسایلی که توضیح دادم مانند کش، ترجمه کوئری، نگاشت‌ها و رعایت بسیاری از best practices که در EF لحاظ شده، وجود ندارند. 50 قسمتی مطلب در موردش در سایت هست. در طول زمان همین کلاس‌های sql helper برای لحاظ این الگوها باید تغییر کنند و اینجا است که دست آخر به این نتیجه خواهید رسید، EF از تمام کارهای دست ساز بسیاری از برنامه نویس‌ها، سریعتر و بهینه‌تر است.
کار اصولی با بانک اطلاعاتی صرفا یک select ساده نیست که بر اساس آن کارآیی و یا بهتر بودن روشی را مشخص کنید. 
مطالب
ستون محاسباتی (computed column)
برخی از داده‌ها از ترکیب و ادغام شدن چند داده دیگر بدست می‌آیند. مثلا شماره دانشجویی از ترکیب چند صفت مختلف بوجود می‌آید (مثل نیمسال ورودی، کددانشگاه، کدرشته تحصیلی...).
برای پیاده سازی اینگونه ستون‌ها SQL Server یک قابلیتی به نام computed column ارائه داده است. برای تعریف این چنین ستون هایی بعد از نام ستون از کلمه AS استفاده می‌کنیم. عبارتی که ستون محاسباتی را تشکیل می‌دهد می‌تواند شامل این موارد باشد: تابع، نام ستون غیر محاسباتی و مقادیر ثابت ولی امکان استفاده از subquery وجود ندارد.
ستون‌های محاسباتی بطور پیشفرض مجازی هستند (بطور فیزیکی بر روی دیسک ذخیره نشده اند). یعنی هر موقع که query اجرا می‌شود آنها نیز مجدد محاسبه شده و نمایش داده می‌شوند.
برای اینکه نوع ذخیره سازی را از مجازی به فیزیکی تبدیل کنیم باید در هنگام ساخت جدول (یا تغییر آن) از کلید واژه PERSISTED استفاده کنیم. وقتی بطور فیزیکی ذخیره شده باشد با هر بار ویرایش یکی از ستون‌های تشکلیل دهنده ستون محاسباتی هم ویرایش می‌شود.
ستون محاسباتی بعد از تبدیل شدن از مجازی به فیزیکی می‌تواند به عنوان کلید اولیه و ایندکس در نظر گرفته شود.

به مثال زیر توجه کنید:
جدولی داریم با دو ستون، قرار هست بر اساس ترکیب مقادیر دو ستون جستجویی انجام دهیم. ضمن اینکه ترکیب دو ستون باید منحصر بفرد باشد. برای این منظور یک unique index روی دو ستون لحاظ می‌کنیم.
create table t1
(
col1 char(1),
col2 char(1)
)

create unique nonclustered index ix_uq on t1 (col1 , col2);

insert t1 
values('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'E'), ('E', 'F'),
('G', 'H'), ('I', 'J'), ('K', 'L'), ('M', 'N'), ('O', 'P');

اکنون به دنبال سطری میگردیم که ترکیب مقادیر دو ستون آن برابر با OP باشد. پس query زیر را اجرا میکنیم
select col1 + col2
from t1
where col1 + col2 = 'OP'
اما همانطور که در تصویر زیر مشاهده می‌شود عمل Index Seek صورت نگرفته است. زمانی که از ستون به عنوان یک عبارت استفاده شود Index Seek نخواهیم داشت. منظور عبارت، الحاق مقداری با ستون، قرار گرفتن ستون در یک تابع و ... می‌باشد.


برای اینکه Index Seek داشته باشیم بایستی مقادیر را جداگانه مقایسه کنیم(ستون‌ها به صورت عبارت محاسباتی نباشند)
select col1 + col2
from t1
where col1  = 'O' and col2 = 'P'

ولی ما می‌خواهیم شرط بر اساس ترکیب دو ستون باشد. خب اینجا هست که Computed Columns مطرح میشوند.
alter table t1 add col3 as col1 + col2 persisted

create clustered index ix1 on t1 (col3)
با دستور اول یک ستون محاسباتی از نوع persisted به جدول اضافه نمودیم. و با دستور دوم یک Index روی ستون محاسباتی ایجاد نمودیم.
حال مجددا عمل جستجو را انجام میدهیم ولی به کمک ستون محاسباتی که اخیرا ایجاد نمودیم:
select *
from t1
where col3 = 'OP';


حالا مشاهده می‌شود که شاخص ix1 اسکن نشده است. و از آنجایی که شاخص از نوع Clustered است مشکل Covering هم نخواهیم داشت.


اشتراک‌ها
Entity Framework 6.2 منتشر شد


- Reduce start up time by loading finished code first models from a persistent cache
- Fluent API to define indexes
- DbFunctions.Like() to enable writing LINQ queries that translate to LIKE in SQL
- Migrate.exe should support -script option
- EF6 does not work with primary key from sequence
- Update error numbers for SQL Azure Execution Strategy
- Bug: Retrying queries or SQL commands fails with “The SqlParameter is already contained by another SqlParameterCollection”
- Bug: Evaluation of DbQuery.ToString() frequently times out in the debugger

Entity Framework 6.2 منتشر شد
بازخوردهای دوره
تزریق وابستگی‌های AutoMapper در لایه سرویس برنامه
- محل تعریف نگاشت‌ها و کلاس‌های پروفایل، مهم نیست. چون اساسا هرجایی که قرار گیرند، دو وابستگی بیشتر نخواهند داشت: کلاس‌های مدل و کلاس‌های ViewModel.
- محل فراخوانی اولیه‌ی تعاریف نگاشت‌ها جهت معرفی آن‌ها به سیستم، مهم است.
+ اگر از کاربر اطلاعاتی را دریافت می‌کنید، در لایه UI هست که کار نگاشت اطلاعات دریافتی از کاربر و از ViewModelها به Modelهای اصلی برنامه انجام می‌شود (توسط متد Mapper.Map). اگر قرار است اطلاعاتی را بازگشت دهید، متدهای جدیدی مانند Project To بسیار بهینه‌تر هستند از روش قدیمی Mapper.Map و این متد را بهتر است در لایه سرویس استفاده کنید. متد Project To کارش بهینه سازی کوئری SQL ارسالی به سرور هست. اگر از روش Mapper.Map در لایه UI استفاده کنید، این قابلیت را از دست خواهید داد؛ چون Mapper.Map به معنای کار با اشیاء درون حافظه و LINQ to Objects است. کار متد ویژه‌ی Project To افزونه‌ای برای کار با Entity Framework و بهینه سازی آن است.