Failed to load project file BlazorServer.App.csproj. C:\Program Files\dotnet\sdk\5.0.103\Sdks\Microsoft.NET.Sdk.Razor\build\netstandard2.0\Microsoft.NET.Sdk.Razor.Component.targets(107,5): Error: The specified task executable location "C:\Program Files\dotnet\dotnet.exe /M" is invalid.
.NET Core, the reinvention of the Microsoft .NET Framework as an open source, cross-platform development choice, is a key focus of the upcoming features planned for the Visual Studio IDE.
In conjunction with .NET Standard -- a spec detailing what .NET APIs should be available on all .NET implementations -- .NET Core retains compatibility with .NET Framework, Xamarin and Mono while letting developers use Windows, macOS or Linux machines to code for mobile, cloud and embedded/IoT projects.
While still primarily driven by Microsoft dev teams, .NET Core is hosted on a GitHub repository and is supported by the .NET Foundation, an independent organization created by Microsoft three years ago to improve its open source development and collaboration.
Peeking at the Visual Studio Roadmap (updated last week) reveals .NET Core figures prominently in upcoming functionality for the IDE, both in the near-term and long:
3.Visual Studio 2017 15.6 منتشر شد
These are the customer-reported issues addressed in this release:
- Esc key doesn't close Peek Definition Window.
-
wpp.targets
file execution not working after update to version 15.6. - Project loaded via File->Open->CMake throws exception and CMake cache is not generated..
- Version 15.6 update crashes Xamarin code with NullReferenceException with next scenario.
- XCode Tools installation fails after version 15.6 update.
- Anaconda 5.0.0 Completion DB is not up to date and hitting refresh fails.
- Cannot run NUnit tests after version 15.6.1 update.
- Visual Studio 2017 fails to launch from the command line when launched using 8.3 file name path.
- Version 15.6.0 breaks when VisualStudio\Telemetry\TurnOffSwitch is set to 0.
- Visual Studio Community 2017 auto closes after version 15.6 update..
- CMake generation never stops after version 15.6.1 update.
- Opening a solution with tests, and clicking any toolbar item in Test Explorer causes VS to crash.
حجم تقریبی بروزرسانی از نسخه 15.6.2 به 15.6.3 برابر 1.47GB میباشد
ایجاد برنامههای جدید توسط Angular CLI
دستور خط فرمان ابتدایی ایجاد یک برنامهی جدید توسط Angular CLI به صورت ذیل است
> ng new my-app
پس از اجرای این دستور، برنامهی جدید ایجاد شده، در پوشهی جدید my-app قرار میگیرد.
گزینهی دیگر این دستور، استفاده از پرچم dry-run است:
> ng new my-app --dry-run
>ng new my-app --dry-run installing ng You specified the dry-run flag, so no changes will be written. create .editorconfig create README.md create src\app\app.component.css create src\app\app.component.html . . . Project 'my-app' successfully created.
گزینهی دیگر دستور ng new را که در قسمت قبل ملاحظه کردید:
> ng new my-app --skip-install
برای مشاهدهی سایر پرچمهای مرتبط با دستور ng new میتوان از پرچم help استفاده کرد:
> ng new --help
بررسی فایل angular-cli.json.
فایل angular-cli.json. حاوی تنظیمات Angular CLI است.
در ابتدای این فایل، نام برنامهی جدید را مشاهده میکنید. این نام، همانی است که توسط دستور ng new my-app تعیین گردید.
"project": { "name": "my-app" },
"apps": [ { "root": "src", "outDir": "dist",
یکی از تنظیمات مهم این فایل، مقدار prefix است:
"prefix": "app",
@Component({ selector: 'app-root',
تغییر این مقدار صرفا بر روی کامپوننتهای جدید تولید شدهی توسط Angular CLI تاثیرگذار خواهند بود. اگر میخواهید در ابتدای کار تولید یک برنامه، این مقدار را مشخص کنید، میتوان از پرچم prefix استفاده کرد و در صورت عدم ذکر آن، مقدار پیش فرض app برای آن درنظر گرفته میشود:
> ng new my-project --skip-install --prefix myCompany
عدم ایجاد مخزن Git به همراه ng new
با صدور فرمان ng new، کار ایجاد یک مخزن Git نیز به صورت خودکار انجام خواهد شد. برای نمونه اگر خواستید برنامهای را بدون نصب وابستگیها، بدون ایجاد تستها و بدون ایجاد مخزن git آن تولید کنید، میتوان از دستور ذیل استفاده کرد:
> ng new my-project --skip-install --skip-git --skip-tests --skip-commit
استفادهی از sass بجای css توسط Angular CLI
سیستم Build همراه با Angular CLI مبتنی بر webpack است و به خوبی قابلیت پردازش فایلهای sass را نیز دارا است. اگر خواستید حالت پیش فرض تولید فایلهای css این ابزار را که در فایل angular-cli.json. نمونهای از آن ذکر شدهاست، به همراه فایلهایی مانند app.component.css، به sass تغییر دهید:
"styles": [ "styles.css" ], "defaults": { "styleExt": "css", "component": {} }
> ng new my-project --skip-install --style sass
"styles": [ "styles.sass" ], "defaults": { "styleExt": "sass", "component": {} }
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.sass'] })
انجام تنظیمات مسیریابی پیش فرض پروژه جدید توسط Angular CLI
حالت پیش فرض تولید برنامههای جدید Angular CLI به همراه تنظیمات مسیریابی آن نیست. اگر علاقمند هستید تا مبحث مسیریابی را خلاصه کرده و به سرعت تنظیمات ابتدایی مسیریابی را توسط این ابزار تولید کنید، میتوان پرچم routing را نیز در اینجا ذکر کرد:
> ng new my-project --skip-install --routing
const routes: Routes = [ { path: '', children: [] } ];
imports: [ BrowserModule, FormsModule, HttpModule, AppRoutingModule ],
اجرای ابتدایی یک برنامهی مبتنی بر Angular CLI
پس از انتخاب پرچمهای مناسب جهت ایجاد یک پروژهی جدید مبتنی بر Angular CLI و همچنین نصب وابستگیهای آنها و یا عدم ذکر پرچم skip-install، اکنون نوبت به اجرای این پروژهاست. به همین جهت از طریق خط فرمان به ریشهی پوشهی برنامهی جدید ایجاد شده، وارد شوید. سپس دستور ذیل را صادر کنید:
>ng serve -o
http://localhost:4200/
نکتهی جالب این وب سرور در این است که تغییرات شما را به صورت خودکار دنبال کرده و بلافاصله ارائه میدهد. برای مثال فایل src\app\app.component.html را گشوده و به صورت ذیل تغییر دهید:
<h1> Test {{title}} </h1>
تغییر پیش فرضهای عمومی Angular CLI
تا اینجا مشاهده کردیم که اگر بخواهیم مقدار prefix پیش فرض را که به app تنظیم شدهاست به myCompany تغییر دهیم، یا میتوان از پرچم prefix در ابتدای کار فراخوانی دستور ng new استفاده کرد و یا میتوان فایل angular-cli.json. را نیز دستی ویرایش نمود. برای تغییر عمومی و سراسری مقدار پیش فرض app میتوان از دستور ng set استفاده کرد:
>ng set apps.prefix myCompany >ng set apps.prefix myCompany -g
و یا اگر بخواهید نوع شیوهنامهی مورد استفاده را ویرایش کنید، میتوان از یکی از دو دستور ذیل استفاده کرد (اولی محلی است و دومی عمومی):
>ng set defaults.styleExt sass >ng set defaults.styleExt sass -g
اجرای امکانات Linting پروژههای مبتنی بر Angular CLI
برای بررسی کیفیت کدهای نوشته شده، میتوان از امکانات Linting استفاده کرد. برای این منظور تنها کافی است دستور ذیل را در ریشهی پروژه اجرا نمود:
> ng lint
> ng lint --help
> ng lint --format stylish
>ng lint --fix
یک مثال: فایل src\app\app.component.ts را باز کنید و به عمد تعدادی مشکل را در آن ایجاد نمائید. برای نمونه دو سطر ابتدایی آنرا به صورت ذیل تغییر دهید:
import { Component } from '@angular/core' let number = 10;
>ng lint --format stylish /src/app/app.component.ts[3, 5]: Identifier 'number' is never reassigned; use 'const' instead of 'let'. /src/app/app.component.ts[1, 42]: Missing semicolon Lint errors found in the listed files.
اکنون اگر دستور ng lint --fix را فراخوانی کنیم، تغییرات ذیل به فایل src\app\app.component.ts اعمال خواهند شد:
import { Component } from '@angular/core'; const number = 10;
"C:\Program Files\Git\usr\bin\openssl.exe" genrsa -out private.key 2048 "C:\Program Files\Git\usr\bin\openssl.exe" req -new -x509 -key private.key -out publickey.cer -days 1398 "C:\Program Files\Git\usr\bin\openssl.exe" pkcs12 -export -out idp.pfx -inkey private.key -in publickey.cer
پس از نصب آن، برنامهی Angular 5 آنرا در مسیر C:\Program Files\Windows Admin Center\UX.zip خواهید یافت. همچنین مایکروسافت این برنامهها را نیز بر مبنای Angular 5 تهیه کردهاست:
- Microsofts streaming website (mixer.com)
-
Office portal
- Microsoft Store for Education
- Azure functions UI
برپایی پیشنیازها
در اینجا نیز از همان برنامهای که در قسمت 30، برای بررسی مثالهای React hooks ایجاد کردیم، استفاده خواهیم کرد. فقط در آن، کتابخانهی Axios را نیز نصب میکنید. به همین جهت در ریشهی پروژهی React این قسمت، دستور زیر را در خط فرمان صادر کنید:
> npm install --save axios
در ادامه میخواهیم در برنامهی React خود، لیست مطالب برنامهی backend را از سرور دریافت کرده و نمایش دهیم. همچنین یک search box را به همراه دکمههای search و clear نیز به آن اضافه کنیم.
دریافت اطلاعات اولیه از سرور، درون useEffect Hook
پس از نصب پیشنیازها و راه اندازی برنامهی backend، در ابتدا فایل src\config.json را جهت درج مشخصات آدرس REST Api آن، ایجاد میکنیم:
{ "apiUrl": "https://localhost:5001/api" }
import React from "react"; import {apiUrl} from "../../config.json"; export default function App() { return <></>; }
import App from "./components/part03/Search";
import axios from "axios"; import React, { useEffect, useState } from "react"; import { apiUrl } from "../../config.json"; export default function App() { useEffect(() => { axios .get(apiUrl + "/posts/search?query=") .then(response => console.log(response.data)); }); return <></>; }
اکنون میخواهیم این اطلاعات دریافتی را در برنامهی خود نیز نمایش دهیم. به همین جهت نیاز است تا response.data را درون state کامپوننت جاری قرار داده و در حین رندر کامپوننت، با تشکیل حلقهای بر روی آن، اطلاعات نهایی را نمایش دهیم. بنابراین نیاز به useState Hook خواهیم داشت که ابتدا آنرا import کرده و سپس آنرا تعریف و در قسمت then، فراخوانی میکنیم:
import axios from "axios"; import React, { useEffect, useState } from "react"; import { apiUrl } from "../../config.json"; export default function App() { const [results, setResults] = useState([]); useEffect(() => { axios.get(apiUrl + "/posts/search?query=").then(response => { console.log(response.data); setResults(response.data); }); });
همانطور که مشاهده میکنید، یک حلقهی بی پایان در اینجا رخ دادهاست! برای پایان آن، مجبور خواهیم شد ابتدا کنسول اجرایی برنامهی React را به صورت دستی خاتمه داده و سپس مرورگر را نیز refresh کنیم تا این حلقه، خاتمه پیدا کند.
علت این مشکل را در قسمت 30 بررسی کردیم؛ effect method تابع useEffect (همان متد در برگیرندهی قطعه کدهای axios.get در اینجا)، پس از هربار رندر کامپوننت، یکبار دیگر نیز اجرا میشود. یعنی این متد، هر دو حالت componentDidMount و componentDidUpdate کامپوننتهای کلاسی را با هم پوشش میدهد و چون در اینجا setState را با فراخوانی متد setResults داریم، یعنی درخواست رندر مجدد کامپوننت انجام شدهاست و پس از آن، مجددا effect method فراخوانی میشود و ... این حلقه هیچگاه خاتمه نخواهد یافت. به همین جهت مرورگر و برنامه، هر دو با هم هنگ میکنند!
در این برنامه فعلا میخواهیم که فقط در حالت componentDidMount، کار درخواست اطلاعات از backend صورت گیرد. به همین جهت پارامتر دوم متد useEffect را با یک آرایهی خالی مقدار دهی میکنیم:
useEffect(() => { // ... }, []);
//... export default function App() { // ... return ( <> <table className="table"> <thead> <tr> <th>Title</th> </tr> </thead> <tbody> {results.map(post => ( <tr key={post.id}> <td>{post.title}</td> </tr> ))} </tbody> </table> </> ); }
استفاده ازAsync/Await برای دریافت اطلاعات، درون یک useEffect Hook
اکنون میخواهیم درون effect method یک useEffect Hook، روش قدیمی استفادهی از callbackها و متد then را برای دریافت اطلاعات، با روش جدیدتر async/await که در قسمت 23 آنرا بیشتر بررسی کردیم، جایگزین کنیم.
useEffect(async () => { const { data } = await axios.get(apiUrl + "/posts/search?query="); console.log(data); setResults(data); }, []);
Warning: An effect function must not return anything besides a function, which is used for clean-up. It looks like you wrote useEffect(async () => ...) or returned a Promise.
برای رفع این مشکل، روش توصیه شده، ایجاد یک تابع مجزای async و سپس فراخوانی آن درون effect function است:
useEffect(() => { getResults(); }, []); const getResults = async () => { const { data } = await axios.get(apiUrl + "/posts/search?query="); console.log(data); setResults(data); };
پیاده سازی componentDidUpdate با یک useEffect Hook، جهت انجام جستجوهای پویا
تا اینجا با اضافه کردن پارامتر دومی به متد useEffect، رویداد componentDidUpdate آنرا از کار انداختیم، تا برنامه با هربار فراخوانی setState و اجرای مجدد effect function، در یک حلقهی بینهایت وارد نشود. اکنون این سؤال مطرح میشود که اگر یک textbox را برای جستجوی در عناوین نمایش داده شده، در بالای جدول آن قرار دهیم، نیاز است با هربار تغییر ورودی آن، کار فراخوانی مجدد effect function صورت گیرد، تا بتوان نتایج جدیدتری را از سرور دریافت و به کاربر نشان داد؛ این مشکل را چگونه باید حل کرد؟
برای دریافت عبارت وارد شدهی توسط کاربر و جستجو بر اساس آن، ابتدا متغیر state و متد تنظیم آنرا با استفاده از useState Hook و یک مقدار اولیهی دلخواه تنظیم میکنیم:
export default function App() { // ... const [query, setQuery] = useState("Title");
<input type="text" name="query" className="form-control my-3" placeholder="Search..." onChange={event => setQuery(event.target.value)} value={query} />
اکنون که متغیر query دارای مقدار شدهاست، میتوان از آن در متد axios.get، به نحو زیر و با ارسال یک کوئری استرینگ به سمت سرور، استفاده کرد:
const { data } = await axios.get( `${apiUrl}/posts/search?query=${encodeURIComponent(query)}` );
تا اینجا اگر برنامه را ذخیره کرده و اجرا کنید، با تایپ در textbox جستجو، تغییری در نتایج حاصل نمیشود؛ چون effect function تعریف شده که سبب اجرای مجدد axios.get میشود، طوری تنظیم شدهاست که فقط یکبار، آنهم پس از رندر اولیهی کامپوننت، اجرا شود. برای رفع این مشکل، با مقدار دهی آرایهای که به عنوان پارامتر دوم متد useEffect تعریف شده، میتوان اجرای مجدد effect function آنرا وابستهی به تغییرات متغیر query در state کامپوننت کرد:
useEffect(() => { getResults(); }, [query]);
دریافت اطلاعات جستجو، تنها با ارسال اطلاعات یک فرم به سمت سرور
تا اینجا کاربر با هر حرفی که درون textbox جستجو وارد میکند، یک کوئری، به سمت سرور ارسال خواهد شد. برای کاهش آن میتوان یک دکمهی جستجو را در کنار این textbox قرار داد تا تنها پس از کلیک بر روی آن، این جستجو صورت گیرد.
برای پیاده سازی این قابلیت، ابتدا وابستگی به query را از متد useEffect حذف میکنیم، تا دیگر با تغییر اطلاعات textbox، متد callback آن اجرا نشود (پارامتر دوم آنرا مجددا به یک آرایهی خالی تنظیم میکنیم). سپس یک دکمه را که از نوع button است و رویداد onClick آن به getResults اشاره میکند، در بالای جدول نتایج مطالب، قرار میدهیم:
<button className="btn btn-primary" type="button" onClick={getResults} > Search </button>
<form onSubmit={handleSearch}> <div className="input-group my-3"> <label htmlFor="query" className="form-control-label sr-only"></label> <input type="text" id="query" name="query" className="form-control" placeholder="Search ..." onChange={event => setQuery(event.target.value)} value={query} /> <div className="input-group-append"> <button className="btn btn-primary" type="submit"> Search </button> </div> </div> </form>
const handleSearch = event => { event.preventDefault(); getResults(); };
افزودن قابلیت پاک کردن textbox جستجو و معرفی useRef Hook
در ادامه میخواهیم یک دکمهی جدید را در کنار دکمهی Search، اضافه کنیم تا با کلیک کاربر بر روی آن، نه فقط محتوای وارد شدهی در textbox پاک شود، بلکه focus نیز به آن منتقل گردد. برای پاک کردن textbox، فقط کافی است متد setQuery را با یک رشتهی خالی ارسالی به آن فراخوانی کنیم. اما برای انتقال focus به textbox، نیاز به داشتن ارجاع مستقیمی به آن المان وجود دارد که با مفهوم آن در قسمت 18 آشنا شدیم: «برای دسترسی به یک المان DOM در React، باید یک reference را به آن نسبت داد. برای این منظور یک خاصیت جدید را در سطح کلاس کامپوننت ایجاد کرده و آنرا با React.RefObject مقدار دهی اولیه کرده و سپس ویژگی ref المان مدنظر را به این RefObject تنظیم میکنیم». برای انجام یک چنین کاری در اینجا، Hook ویژهای به نام useRef معرفی شدهاست. بنابراین برای پیاده سازی این نیازمندیها، ابتدا دکمهی Clear را در کنار دکمهی Search قرار میدهیم:
<button type="button" onClick={handleClearSearch} className="btn btn-info" > Clear </button>
import React, { useEffect, useRef, useState } from "react"; // ... export default function App() { // ... const searchInputRef = useRef(); const handleClearSearch = () => { setQuery(""); searchInputRef.current.focus(); };
البته این searchInputRef برای اینکه دقیقا به textbox تعریف شده اشاره کند، باید آنرا به ویژگی ref المان، انتساب داد:
<input type="text" id="query" name="query" className="form-control" placeholder="Search ..." onChange={event => setQuery(event.target.value)} value={query} ref={searchInputRef} />
نمایش «لطفا منتظر بمانید» در حین دریافت اطلاعات از سرور
البته در اینجا با هر بار کلیک بر روی دکمهی جستجو، نتیجهی نهایی به سرعت نمایش داده میشود؛ اما اگر سرعت اتصال کاربر کمتر باشد، با یک وقفه این امر رخ میدهد. به همین جهت بهتر است یک پیام «لطفا منتظر بمانید» را در این حین به او نمایش دهیم. به همین جهت در ابتدا state مرتبطی را به کامپوننت اضافه میکنیم:
const [loading, setLoading] = useState(false);
const getResults = async () => { setLoading(true); const { data } = await axios.get( `${apiUrl}/posts/search?query=${encodeURIComponent(query)}` ); console.log(data); setResults(data); setLoading(false); };
{loading ? ( <div className="alert alert-info">Loading results...</div> ) : ( <table className="table"> <thead> <tr> <th>Title</th> </tr> </thead> <tbody> {results.map(post => ( <tr key={post.id}> <td>{post.title}</td> </tr> ))} </tbody> </table> )}
برای آزمایش آن میتوان سرعت اتصال را در برگهی شبکهی ابزارهای توسعه دهندگان مرورگر، تغییر داد:
مدیریت خطاها در حین اعمال async
آخرین امکانی را که به این مطلب اضافه خواهیم کرد، مدیریت خطاهای اعمال async است که با try/catch صورت میگیرد:
// ... export default function App() { // ... const [error, setError] = useState(null); // ... const getResults = async () => { setLoading(true); try { const { data } = await axios.get( `${apiUrl}/posts/search?query=${encodeURIComponent(query)}` ); console.log(data); setResults(data); } catch (err) { setError(err); } setLoading(false); };
{error && <div className="alert alert-warning">{error.message}</div>}
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-30-part-03-frontend.zip و sample-30-part-03-backend.zip
Install-Package Microsoft.AspNet.FriendlyUrls -Pre
public static class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.EnableFriendlyUrls(); } }
void Application_Start(object sender, EventArgs e) { RouteConfig.RegisterRoutes(RouteTable.Routes); }
Response.Redirect("~\\Second");
Response.Redirect("~\\First?Id=1001"); string Id = Request.QueryString["Id"] as string;
http://localhost:57353/First/Products/NewProduct
- ()Request.GetFriendlyUrlFileVirtualPath
مسیر مجازی فایل را بر میگرداند - ()Request.GetFriendlyUrlSegments
بخشهای آدرس را بر میگرداند - ()Request.GetFriendlyUrlFileExtension
پسوند فایل آدرس را بر میگرداند
کلاس FriendlyUrl دو متود استاتیک و یک خاصیت دارد
- FriendlyUrl.Segments
شبیه ()Request.GetFriendlyUrlSegments مجموعه بخشهای آدرس صفحه جاری را بر میگرداند - FriendlyUrl.Resolve
آدرس صفحه جاری را بر میگرداند - FriendlyUrl.Href
امکان ساخت یک آدرس را با استفاده از مسیر مجازی، بخشها و پارامترها، فراهم مینماید
در آخر، فضای نام کتابخانه، Microsoft.AspNet.FriendlyUrls میباشد.
کاربر یا مدیر سیستم بهتر از هر کسی میتواند جنبههایهای مختلف اجرای برنامه را مشخص کند. به عنوان نمونه ممکن است مدیر سیستم بخواهد فایلهای یک برنامه را سمت هارد دیسک سیستم کاربر انتقال دهد یا اطلاعات مانیفست یک اسمبلی را رونویسی کند و مباحث نسخه بندی که در آینده در مورد آن صحبت میکنیم.
با ارائه یک فایل پیکربندی در شاخه برنامه میتوان به مدیر سسیتم اجازه داد تا کنترل بیشتر بر روی برنامه داشته باشد. ناشر برنامه میتواند این فایل را همراه دیگر فایلهای برنامه پکیج کند تا در شاخه برنامه نصب شود تا بعدا مدیر یا کاربر سیستم بتوانند آن را تغییر و ویرایش کنند. CLR هم محتوای این فایل را تفسیر کرده و قوانین بارگیری اسمبلیها و ... را تغییر میدهد. این فایل پیکربندی میتواند به صورت XML هم ارائه شود. مزیت قرار دادن یک فایل جداگانه نسبت به رجیستری این مزیت را دارد که هم قابل جابجایی و پشتیبانی گیری است و هم اینکه تغییر آن سادهتر است.
البته در آینده بیشتر در مورد این فایل صحبت میکنیم ولی در حال حاضر بهتر است اندکی طعم آن را بچشیم. فرض را بر این میگذاریم که ناشر میخواهد فایلهای اسمبلی MultiFileLibrary را در دایرکتوری جداگانهای قرار دهد و چیزی شبیه به ساختار زیر را در نظر دارد:
AppDir directory (contains the application’s assembly files) Program.exe Program.exe.config (discussed below) AuxFiles subdirectory (contains MultiFileLibrary’s assembly files) MultiFileLibrary.dll FUT.netmodule RUT.netmodule
حال با تنظیم بالا به دلیل اینکه CLR انتظار دارد این اسمبلی را در دایرکتوری برنامه بیابد و با این جابجایی قادر به انجام این کار نیست، استثنای زیر را صادر میکند:
System.IO.FileNotFoundException
<configuration> <runtime> <assemblyBinding xmlns="urn:schemasmicrosoftcom:asm.v1"> <probing privatePath="AuxFiles" /> </assemblyBinding> </runtime> </configuration>
نحوه عملکرد اسکن CLR برای بارگزاری اسمبلی ها
موقعی که CLR قصد بارگزاری اسمبلیهای خنثی را دارد، شاخههای زیر را به طور خودکار اسکن خواهد نمود ( مسیرهای FirstPrivatePath و SecondPrivatePath توسط فایل پیکربندی مشخص شده است)
AppDir\AsmName.dll AppDir\AsmName\AsmName.dll AppDir\firstPrivatePath\AsmName.dll AppDir\firstPrivatePath\AsmName\AsmName.dll AppDir\secondPrivatePath\AsmName.dll AppDir\secondPrivatePath\AsmName\AsmName.dll ...
این نکته ضروری است که اگر اسمبلی شما در یک دایرکتوری همنام خودش (در مثال ما MultiFileLibrary) قرار بگیرد، نیازی نیست این مسیر را در فایل پیکربندی ذکر کنید؛ زیرا CLR در صورت نیافتن دایرکتوری با این نام را اسکن خواهد نمود. بعد از آن اگر به هر نحوی CLR نتواند اسمبلی را در هیچ کدام از دایرکتوریهای گفته شده بیابد، با همان قوانین گفته شده اینبار به دنبال فایلی با پسوند exe خواهد بود و اگر باز هم جست و جوی آن نتیجهای را در بر نداشته باشد، استثنای زیر را صادر میکند:
FileNotFoundException
C:\AppDir\enUS\AsmName.dll C:\AppDir\enUS\AsmName\AsmName.dll C:\AppDir\firstPrivatePath\enUS\AsmName.dll C:\AppDir\firstPrivatePath\enUS\AsmName\AsmName.dll C:\AppDir\secondPrivatePath\enUS\AsmName.dll C:\AppDir\secondPrivatePath\enUS\AsmName\AsmName.dll C:\AppDir\enUS\AsmName.exe C:\AppDir\enUS\AsmName\AsmName.exe C:\AppDir\firstPrivatePath\enUS\AsmName.exe C:\AppDir\firstPrivatePath\enUS\AsmName\AsmName.exe C:\AppDir\secondPrivatePath\enUS\AsmName.exe C:\AppDir\secondPrivatePath\enUS\AsmName\AsmName.exe C:\AppDir\en\AsmName.dll C:\AppDir\en\AsmName\AsmName.dll C:\AppDir\firstPrivatePath\en\AsmName.dll C:\AppDir\firstPrivatePath\en\AsmName\AsmName.dll C:\AppDir\secondPrivatePath\en\AsmName.dll C:\AppDir\secondPrivatePath\en\AsmName\AsmName.dll C:\AppDir\en\AsmName.exe C:\AppDir\en\AsmName\AsmName.exe C:\AppDir\firstPrivatePath\en\AsmName.exe C:\AppDir\firstPrivatePath\en\AsmName\AsmName.exe C:\AppDir\secondPrivatePath\en\AsmName.exe C:\AppDir\secondPrivatePath\en\AsmName\AsmName.exe
نحوه اسکن کردن CLR میتواند به ما بگوید که عمل اسکن میتواند گاهی اوقات با زمان زیادی روبرو شود (به خصوص که قابلیت اسکن روی شبکه را هم دارد). برای محدود کردن ناحیه یا نواحی اسکن میتوانید یک یا چند المان culture را در فایل پیکربندی مشخص کنید. همچنین مایکروسافت ابزاری به نام FUSLogVw.exe را ارائه داده است که میتواند نواحی اسکن را در حین اجرای برنامه به ما گزارش دهد.
نام و محل فایل پیکربندی بسته به نوع برنامه میتواند متغیر باشد:
برای فایلهای اجرایی EXE فایل پیکربندی باید در شاخه فایل اجرایی باشد و باید نام فایل پیکربندی همانند فایل exe بوده و یک پسوند config. را به آن اضافه کرد.
برای برنامههای وب فرم، فایل web.config موجود است که در ریشه شاخه مجازی وب اپلیکیشن قرار میگیرد و هر زیر دایرکتوری هم میتواند یک web.config جداگانه داشته باشد که میتواند از web.config ریشه هم تنظمیاتش را ارث بری کند. برای نمونه آدرس زیر را در نظر بگیرد:
https://www.dntips.ir/newsarchive
یک فایل کانفیگ در ریشه قرار میگیرد و یکی هم در زیر شاخه newsArchive میتواند قرار بگیرد.
فصل دوم «نحوه ساخت و توزیع اسمبلی ها» از بخش اول «اصول اولیه CLR» پایان یافت. فصل بعدی در مورد اسمبلیهای اشتراکی است که بعد از آماده شدن این فصل، قسمتهای بعدی در دسترس عزیزان قرار خواهد گرفت.