مدفون سازی فایلهای CSS و JS هر افزونه درون فایل DLL آن
به solution جاری، یک class library جدید را به نام MvcPluginMasterApp.Common اضافه کنید. از آن جهت قرار دادن کلاسهای عمومی و مشترک بین افزونهها استفاده خواهیم کرد. برای مثال قصد نداریم کلاسهای سفارشی و عمومی ذیل را هربار به صورت مستقیم در افزونهای جدید کپی کنیم. کتابخانهی Common، امکان استفادهی مجدد از یک سری کدهای تکراری را در بین افزونهها میسر میکند.
این پروژه برای کامپایل شدن نیاز به بستهی نیوگت ذیل دارد:
PM> install-package Microsoft.AspNet.Web.Optimization
پس از این مقدمات، کلاس ذیل را به این پروژهی class library جدید اضافه کنید:
using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using System.Web.Optimization; namespace MvcPluginMasterApp.Common.WebToolkit { public class EmbeddedResourceTransform : IBundleTransform { private readonly IList<string> _resourceFiles; private readonly string _contentType; private readonly Assembly _assembly; public EmbeddedResourceTransform(IList<string> resourceFiles, string contentType, Assembly assembly) { _resourceFiles = resourceFiles; _contentType = contentType; _assembly = assembly; } public void Process(BundleContext context, BundleResponse response) { var result = new StringBuilder(); foreach (var resource in _resourceFiles) { using (var stream = _assembly.GetManifestResourceStream(resource)) { if (stream == null) { throw new KeyNotFoundException(string.Format("Embedded resource key: '{0}' not found in the '{1}' assembly.", resource, _assembly.FullName)); } using (var reader = new StreamReader(stream)) { result.Append(reader.ReadToEnd()); } } } response.ContentType = _contentType; response.Content = result.ToString(); } } }
کلاس فوق در اسمبلی معرفی شده به آن، توسط متد GetManifestResourceStream به دنبال فایلها و منابع مدفون شده گشته و سپس محتوای آنها را بازگشت میدهد.
اکنون برای استفادهی از آن، به پروژهی MvcPluginMasterApp.Plugin1 مراجعه کرده و ارجاعی را به پروژهی MvcPluginMasterApp.Common فوق اضافه نمائید. سپس در فایل Plugin1.cs، متد RegisterBundles آنرا به نحو ذیل تکمیل کنید:
namespace MvcPluginMasterApp.Plugin1 { public class Plugin1 : IPlugin { public EfBootstrapper GetEfBootstrapper() { return null; } public MenuItem GetMenuItem(RequestContext requestContext) { return new MenuItem { Name = "Plugin 1", Url = new UrlHelper(requestContext).Action("Index", "Home", new { area = "NewsArea" }) }; } public void RegisterBundles(BundleCollection bundles) { var executingAssembly = Assembly.GetExecutingAssembly(); // Mostly the default namespace and assembly name are the same var assemblyNameSpace = executingAssembly.GetName().Name; var scriptsBundle = new Bundle("~/Plugin1/Scripts", new EmbeddedResourceTransform(new List<string> { assemblyNameSpace + ".Scripts.test1.js" }, "application/javascript", executingAssembly)); if (!HttpContext.Current.IsDebuggingEnabled) { scriptsBundle.Transforms.Add(new JsMinify()); } bundles.Add(scriptsBundle); var cssBundle = new Bundle("~/Plugin1/Content", new EmbeddedResourceTransform(new List<string> { assemblyNameSpace + ".Content.test1.css" }, "text/css", executingAssembly)); if (!HttpContext.Current.IsDebuggingEnabled) { cssBundle.Transforms.Add(new CssMinify()); } bundles.Add(cssBundle); BundleTable.EnableOptimizations = true; } public void RegisterRoutes(RouteCollection routes) { } public void RegisterServices(IContainer container) { } } }
این فایلها به صورت ذیل در پروژه تعریف گردیدهاند:
همانطور که مشاهده میکنید، باید به خواص هر کدام مراجعه کرد و سپس Build action آنها را به embedded resource تغییر داد، تا در حین کامپایل، به صورت خودکار در قسمت منابع اسمبلی ذخیره شوند.
یک نکتهی مهم
اینبار برای مسیردهی منابع، باید بجای / فایل سیستم، از «نقطه» استفاده کرد. زیرا منابع با نامهایی مانند namespace.folder.name در قسمت resources یک اسمبلی ذخیره میشوند:
مدفون سازی تصاویر ثابت هر افزونه درون فایل DLL آن
مجددا به اسمبلی مشترک MvcPluginMasterApp.Common مراجعه کرده و اینبار کلاس جدید ذیل را به آن اضافه کنید:
using System; using System.Collections.Generic; using System.Reflection; using System.Web; using System.Web.Routing; namespace MvcPluginMasterApp.Common.WebToolkit { public class EmbeddedResourceRouteHandler : IRouteHandler { private readonly Assembly _assembly; private readonly string _resourcePath; private readonly TimeSpan _cacheDuration; public EmbeddedResourceRouteHandler(Assembly assembly, string resourcePath, TimeSpan cacheDuration) { _assembly = assembly; _resourcePath = resourcePath; _cacheDuration = cacheDuration; } IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { return new EmbeddedResourceHttpHandler(requestContext.RouteData, _assembly, _resourcePath, _cacheDuration); } } public class EmbeddedResourceHttpHandler : IHttpHandler { private readonly RouteData _routeData; private readonly Assembly _assembly; private readonly string _resourcePath; private readonly TimeSpan _cacheDuration; public EmbeddedResourceHttpHandler( RouteData routeData, Assembly assembly, string resourcePath, TimeSpan cacheDuration) { _routeData = routeData; _assembly = assembly; _resourcePath = resourcePath; _cacheDuration = cacheDuration; } public bool IsReusable { get { return false; } } public void ProcessRequest(HttpContext context) { var routeDataValues = _routeData.Values; var fileName = routeDataValues["file"].ToString(); var fileExtension = routeDataValues["extension"].ToString(); var manifestResourceName = string.Format("{0}.{1}.{2}", _resourcePath, fileName, fileExtension); var stream = _assembly.GetManifestResourceStream(manifestResourceName); if (stream == null) { throw new KeyNotFoundException(string.Format("Embedded resource key: '{0}' not found in the '{1}' assembly.", manifestResourceName, _assembly.FullName)); } context.Response.Clear(); context.Response.ContentType = "application/octet-stream"; cacheIt(context.Response, _cacheDuration); stream.CopyTo(context.Response.OutputStream); } private static void cacheIt(HttpResponse response, TimeSpan duration) { var cache = response.Cache; var maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic); if (maxAgeField != null) maxAgeField.SetValue(cache, duration); cache.SetCacheability(HttpCacheability.Public); cache.SetExpires(DateTime.Now.Add(duration)); cache.SetMaxAge(duration); cache.AppendCacheExtension("must-revalidate, proxy-revalidate"); } } }
این IRouteHandler، نام و پسوند فایل را دریافت کرده و سپس به قسمت منابع اسمبلی رجوع، فایل مرتبط را استخراج و سپس بازگشت میدهد. همچنین برای کاهش سربار سیستم، امکان کش شدن منابع استاتیک نیز در آن درنظر گرفته شدهاست و هدرهای خاص caching را به صورت خودکار اضافه میکند.
سیستم bundling نیز هدرهای کش کردن را به صورت خودکار و توکار اضافه میکند.
اکنون به تعاریف Plugin1 مراجعه کنید و سپس این IRouteHandler سفارشی را به نحو ذیل به آن معرفی نمائید:
namespace MvcPluginMasterApp.Plugin1 { public class Plugin1 : IPlugin { public void RegisterRoutes(RouteCollection routes) { //todo: add custom routes. var assembly = Assembly.GetExecutingAssembly(); // Mostly the default namespace and assembly name are the same var nameSpace = assembly.GetName().Name; var resourcePath = string.Format("{0}.Images", nameSpace); routes.Insert(0, new Route("NewsArea/Images/{file}.{extension}", new RouteValueDictionary(new { }), new RouteValueDictionary(new { extension = "png|jpg" }), new EmbeddedResourceRouteHandler(assembly, resourcePath, cacheDuration: TimeSpan.FromDays(30)) )); } } }
مطابق تعریف آن، file و extension به صورت خودکار جدا شده و توسط routeData.Values در متد ProcessRequest کلاس EmbeddedResourceHttpHandler قابل دسترسی خواهند شد.
پسوندهایی که توسط آن بررسی میشوند از نوع png یا jpg تعریف شدهاند. همچنین مدت زمان کش کردن هر منبع استاتیک تصویری به یک ماه تنظیم شدهاست.
استفادهی نهایی از تنظیمات فوق در یک View افزونه
پس از اینکه تصاویر و فایلهای css و js را به صورت embedded resource تعریف کردیم و همچنین تنظیمات مسیریابی و bundling خاص آنها را نیز مشخص نمودیم، اکنون نوبت به استفادهی از آنها در یک View است:
@{ ViewBag.Title = "From Plugin 1"; } @Styles.Render("~/Plugin1/Content") <h2>@ViewBag.Message</h2> <div class="row"> Embedded image: <img src="@Url.Content("~/NewsArea/Images/chart.png")" alt="clock" /> </div> @section scripts { @Scripts.Render("~/Plugin1/Scripts") }
همچنین مسیر تصویر مشخص شدهی در آن، اینبار یک NewsArea اضافهتر دارد. فایل اصلی تصویر، در مسیر Images/chart.png قرار گرفتهاست اما میخواهیم این درخواستها را به مسیریابی جدید {NewsArea/Images/{file}.{extension هدایت کنیم. بنابراین نیاز است به این نکته نیز دقت داشت.
اینبار اگر برنامه را اجرا کنیم، میتوان به سه نکته در آن دقت داشت:
الف) alert اجرا شده از فایل js مدفون شده خوانده شدهاست.
ب) رنگ قرمز متن (تگ h2) از فایل css مدفون شده، گرفته شدهاست.
ج) تصویر نمایش داده شده، همان تصویر مدفون شدهی در فایل DLL برنامه است.
و هیچکدام از این فایلها، به پوشههای پروژهی اصلی برنامه، کپی نشدهاند.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
MvcPluginMasterApp-Part2.zip
ایجاد یک پروژهی Blazor WASM جدید
یک پوشهی جدید دلخواه را به نام BlazorWasmSQLite ایجاد کرده و با اجرای دستور dotnet new blazorwasm، یک پروژهی Blazor Web Assembly خالی جدید را در آن آغاز میکنیم. همانطور که از دستور نیز مشخص است، این پروژه از نوع hosted که به همراه Web API هم هست، نمیباشد.
افزودن Context و مدل EF-Core به برنامه
مدل برنامه به صورت زیر در پوشهی Models آن قرار میگیرد:
namespace BlazorWasmSQLite.Models; public class Car { public int Id { get; set; } public string Brand { get; set; } public int Price { get; set; } }
using Microsoft.EntityFrameworkCore; using BlazorWasmSQLite.Models; namespace BlazorWasmSQLite.Data; public class ClientSideDbContext : DbContext { public DbSet<Car> Cars { get; set; } = default!; public ClientSideDbContext(DbContextOptions<ClientSideDbContext> options) : base(options) { } }
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <ItemGroup> <!-- EF Core and Sqlite --> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" /> </ItemGroup> </Project>
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using BlazorWasmSQLite; using Microsoft.EntityFrameworkCore; using BlazorWasmSQLite.Data; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); builder.RootComponents.Add<HeadOutlet>("head::after"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); // Sets up EF Core with Sqlite builder.Services.AddDbContextFactory<ClientSideDbContext>(options => options .UseSqlite($"Filename=DemoData.db") .EnableSensitiveDataLogging()); await builder.Build().RunAsync();
ثبت تعدادی رکورد در بانک اطلاعاتی
در ادامه سعی میکنیم در فایل Index.razor، تعدادی رکورد را به بانک اطلاعاتی اضافه کنیم:
@page "/" @using Microsoft.Data.Sqlite @using Microsoft.EntityFrameworkCore @using BlazorWasmSQLite.Data @using BlazorWasmSQLite.Models <PageTitle>Index</PageTitle> <h1>Hello, world!</h1> Welcome to your new app. <SurveyPrompt Title="How is Blazor working for you?" /> @code { [Inject] private IDbContextFactory<ClientSideDbContext> _dbContextFactory { get; set; } = default!; protected override async Task OnInitializedAsync() { await using var db = await _dbContextFactory.CreateDbContextAsync(); await db.Database.EnsureCreatedAsync(); // create seed data if (!db.Cars.Any()) { var cars = new[] { new Car { Brand = "Audi", Price = 21000 }, new Car { Brand = "Volvo", Price = 11000 }, new Car { Brand = "Range Rover", Price = 135000 }, new Car { Brand = "Ford", Price = 8995 } }; await db.Cars.AddRangeAsync(cars); await db.SaveChangesAsync(); } await base.OnInitializedAsync(); } }
اولین سعی در اجرای برنامه
در ادامه سعی میکنیم تا برنامه را اجرا کنیم. با خطای زیر متوقف خواهیم شد:
crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: The type initializer for 'Microsoft.Data.Sqlite.SqliteConnection' threw an exception. System.TypeInitializationException: The type initializer for 'Microsoft.Data.Sqlite.SqliteConnection' threw an exception. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.DllNotFoundException: e_sqlite3 at SQLitePCL.SQLite3Provider_e_sqlite3.SQLitePCL.ISQLite3Provider.sqlite3_libversion_number()
رفع مشکل کار با SQLite با کامپایل ویژهی آن
برای دسترسی به کدهای native در Blazor WASM و مرورگر، باید آنها را توسط کامپایلر emcc به صورت زیر کامپایل کرد:
$ git clone https://github.com/cloudmeter/sqlite $ cd sqlite $ emcc sqlite3.c -shared -o e_sqlite3.o
مرحلهی بعد، معرفی این object file تولید شده به برنامه است. برای اینکار ابتدا باید dotnet workload install wasm-tools را نصب کرد (مهم). سپس به فایل csproj برنامه مراجعه کرده و فایل e_sqlite3.o را به آن معرفی میکنیم:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <ItemGroup> <!-- EF Core and Sqlite --> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" /> <NativeFileReference Include="Data\e_sqlite3.o" /> </ItemGroup> </Project>
سعی در اجرای مجدد برنامه
پس از نصب wasm-tools و ذکر NativeFileReference فوق، اکنون اگر برنامه را اجرا کنیم، برنامه بدون مشکل اجرا خواهد شد:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: BlazorWasmSQLite.zip
- توابع خط فرمان - Command Line API
- توابع کنسول - Console API
<div id="first" class="content">Content1 with css class and id</div> <div class="content"> Content2 with css class <a class="links" href="#">Link1</a> <a href="#">Link2</a> </div> <div> Content3 without css class and id </div> <input type="button" onclick="myFunc()" value="Run myFunc" /> <input type="text" id="myInput" /> <script type="text/javascript"> function myFunc() { loop(1000); loop(50000); } function loop(number) { for (var i = 0; i < number; i++) { } } </script>
$$("div.content");
profile("myFunc Testing"); myFunc(); profileEnd();
- $(id)
معادل دستور document.getElementById است که یک المنت با id داده شده بر میگرداند .
$("first");
- $$(selector)
آرایه ای از المنتهای مطابق با selector داده شده بر میگرداند .
$$("div.content")
به تفاوت دو دستور توجه کنید . خروجی دستور اول ، یک المنت است و خروجی دستور دوم یک آرایه از المنت که بین [ و ] قرار گرفته اند .
برای آشنایی بیشتر با CSS Seletorها به این لینک مراجعه کنید : http://www.w3.org/TR/css3-selectors
- $x(xPathExpression)
آرایه ای از المنت هایی را بر میگرداند که با xPath داده شده مطابقت داشته باشند .
var objects = $x("html/body/div[2]/a") for(var i = 0; i < objects.length; i++) { console.log(objects[i]); }
برای آشنایی بیشتر با عبارات xPath به این لینک مراجعه کنید : http://www.w3schools.com/xpath
- dir(object)
تمام خصوصیات شیء ارسال شده را لیست میکند .
var objects = $x("html/body/div[2]/a") dir(objects);
- dirxml(node)
سورس یک المنت را بصورت درختواره ( tree ) پرینت میکند . همچنین با کلیک بروی هر node ، فایرباگ آن node را در تب html نمایش میدهد .
var node = $("first"); dirxml(node);
توجه کنید که این دستور فقط یک node دریافت میکند . برای همین اگر از دستور $$("#first") استفاده میکنید ، چون این دستور یک آرایه بر میگرداند ، باید اولین عضو آرایه را دریافت و ارسال کنید .
یعنی :
var node = $$("#first")[0]; dirxml(node);
- clear()
این دستور محیط console را خالی میکند . عملکرد این دستور معادل کلیک دکمهی Clear ( در بالا - چپ تب کنسول ) است .
- inspect(object[,tabName])
توسط این دستور میتوانید یک شیء را در مناسبترین تب فایرباگ یا یکی از تبهای مورد نظر خود ، Inspect کنید .
var node = $("first"); inspect(node); // inspect in html tab inspect(node,'dom'); // inspect in dom tab
- keys(object)
آرایه ای از "نام" تمام خصوصیات شیء ارسال شده بر میگرداند .
var obj = $("first"); keys(obj)
- values(object)
آرایه ای از "مقدار" تمام خصوصیات شیء ارسال شده بر میگرداند .
var obj = $("first"); values(obj)
- debug(fn) and undebug(fn)
این متدها یک BreakPoint در ابتدای تابع مشخص شده اضافه/حذف میکنند . ( در تب Script ) . به همین ترتیب هنگامی که تابع مورد نظر فراخوانی شود ، در نقطه ای که BreakPoint قرار داده شده توقف خواهد کرد .
البته میشود BreakPoint را دستی هم قرار داد . در اصل این تابع ، این عملیات را سادهتر میکند .
debug(myFunc); myFunc(); undebug(myFunc);
- monitor(fn) and unmonitor(fn)
این متدها برای فعال/غیرفعال کردن Logging فراخوانیهای یک تابع استفاده میشوند .
در حالت عادی برای پی بردن به اینکه یک تابع اجرا میشود یا نه ، در تابع مورد نظر یک alert قرار میدهیم و تست میکنیم . که این روش در برنامه برنامههای بزرگ صحیح نیست . زیرا در این حالت باید بین حجم زیادی کد به دنبال تابع مورد نظر بگردیم و سپس alert را قرار بدهیم و بعد اطمینان از صحت عملکرد تابع مجدد آن را حذف کرد ، که با اتلاف زمان و به خطر انداختن کدها همراه است .
اما با استفاده از این متدها ، تنها نیاز به داشتن اسم تابع داریم ( و نه مکان تابع در کدهای برنامه ) .
تست monitor :
monitor(myFunc); // now click on "Run myFunc" button
unmonitor(myFunc); // now click on "Run myFunc" button
- monitorEvents(object[, types]) and unmonitorEvents(object[, types])
این متدها عملیات Event Logging برای یک شیء را فعال/غیرفعال میکنند . در کنار شیء مورد نظر ، میتوان نوع رویداد را هم به متد ارسال کرد . در این صورت عملیات Logging فقط برای همان گروه رویداد/رویداد ، فعال/غیرفعال میشود .
منظور از گروه رویداد ، مجموعه رویدادهای یک شیء است . مثلا mousemove , moseover , mousedown , ... در گروه mouse قرار میگیرند . یعنی میتوانید با ارسال کلمهی mouse فقط رویدادهای mouse را تحت نظر بگیرید یا اینکه فقط یک رویداد را مشخص کنید ، مثل mousedown .
راه سادهتر فعال کردن Event Logging ، رفتن به تب Html ، راست کلیک کردن بروی المنت مورد نظر و فعال کردن گزینهی Log Events میباشد .
var obj = $("myInput"); monitorEvents(obj,'keypress');
نتیجه پس از فشردن چند دکمهی کیبورد در myInput :
توضیحات بیشتر : http://getfirebug.com/wiki/index.php/MonitorEvents - profile([title]) and profileEnd()
این متدها ، JavaScript Profiler را فعال/غیرفعال میکنند . هنگام فعال کردن میتوانید یک عنوان هم برای پروفایل مشخص کنید . در قسمت قبلی مقاله در مورد این قابلیت توضیحاتی ارائه شد .
سه را برای اجرای Profiler وجود دارد :
1 - کلیک بروی دکمهی Profiler در بالای تب کنسول .
2 - استفاده از کد console.profile("ProfileTitle") در کدهای جاوا اسکریپت .
3 - استفاده از متد profile("Profile Title") در خط فرمان .
profile("myFunc Testing"); myFunc(); profileEnd();
ستونهای Profiler :Function : نام تابع اجرا شده .
Calls : تعداد دفعات فراخوانی تابع .
Percent : زمان اجرای تابع در زمان کل ، به درصد .
Own Time : زمان اجرای تابع به تنهایی . برای مثال در کد ما ، زمان اجرای تابع myFunc به تنهایی تقریبا صفر است زیرا عمیاتی در خود انجام نمیدهد و زمان صرف شده در این تابع ، برای اجرای 2 با تابع loop است . این زمان ( Own Time ) زمان اجرای تابع ، منهای زمان صرف شده برای فراخوانی توابع دیگر است .
Time : زمان اجرای تابع از نقطهی آغاز تا پایان . مجموع زمان اجرای خود تابع به همراه زمان اجرای توابع فراخوانی شده . در کد ما ، این زمان ، مجموع زمان اجرای خود تابع به همراه دو بار فراخوانی تابع loop است .
Avg : میانگین زمان اجرای هربار تابع . فرمول : Avg = Time / Calls
Min & Max : حداقل و حداکثر زمان اجرای تابع .
File : نام فایل و شماره خطی که تابع در آن قرار دارد .
فرض کنید تیم برنامهنویس متلب و تیم برنامهنویس دات نت در تعامل با یکدیگر هستند. وظیفه تیم برنامه نویسی متلب به شرح زیر میباشد :
1- نوشتن توابع در متلب و تست کردن آنها جهت توسعه و ارائه مناسب به تیم مقابل
2- درست کردن کامپوننت دات نت در متلب با استفاده از محیط Deployment Tool GUI (با اجرای دستور deploytool در متلب)
3- استفاده از یک پکیج بستهبندی شده از فایلهای قابل ارائه به تیم مقابل (اختیاری)
4- کپی پکیج در محل از قبل تعیین شده توسط دو تیم یا ارائه آن به تیم مقابل جهت استفاده
برای مثال M فایل (اصطلاح فایلها در متلب همانند کلاس در دات نت) makesquare.m را که در مسیر
matlabroot\toolbox\dotnetbuilder\Examples\VS8\NET\MagicSquareExample\MagicSquareComp
function y = makesquare(x) %MAKESQUARE Magic square of size x. % Y = MAKESQUARE(X) returns a magic square of size x. % This file is used as an example for the MATLAB % Builder NE product. % Copyright 2001-2012 The MathWorks, Inc. y = magic(x);
در صورتی که x برابر 5 انتخاب شود خروجی متلب بصورت زیر خواهد بود :
17 24 1 8 15 23 5 7 14 16 4 6 13 20 22 10 12 19 21 3 11 18 25 2 9
makeSqr | Project Name |
MLTestClass | Class Name |
makesquare.m | File to compile |
نام و مسیر پروژه را تعیین کنید سپس از منوی کشویی نوع پروژه، که دات نت اسمبلی باشد را انتخاب کنید. پنجرهای در به شکل زیر مشاهده خواهد شد :
در تب build اگر قصد استفاده از اپلیکیشن COM را دارید و یا فایلهایی جهت تکمیل پروژه قصد پیوست دارید را در قسمت پایین Add files را انتخاب کنید. و اگر قصد استفاده از اپلیکیشن دات نت را دارید قسمت بالایی Add classes را انتخاب کنید و نام کلاس را وارد کنید.
سپس برای کلاس مورد نظر فایلهای متلبی که قصد کامپایل کردن آنها را دارید از قسمت Add files پیوست کنید. در صورتیکه قصد اضافه کردن کلاس اضافی را داشتید مجددا مراحل را طی کنید. در انتها دکمه build را زده تا عملیات کامپایل آغاز شود. اما برای استفاده تیم برنامهنویسی دات نت احتیاج به کامپایلر متلب میباشد که این مهم در پکیجی که به این تیم ارائه خواهد شد مد نظر قرار خواهد گرفت.در قسمت تب Package گزینه Add MCR را انتخاب نمائید :
بعد از انتخاب، دو گزینه برای انتخاب وجود دارد که بطور خلاصه گزینه اول فایلهای کامپایلر متلب در داخل پروژه جهت ارائه قرار میگیرد. همچنین این گزینه جهت استفاده در مواقع درون شبکهای، مواردی که فضای دیسک و عملکرد و .... چندان اهمیت ندارد مورد استفاده قرار میگیرد. اما گزینه دوم عکس قضیه بالا عمل میکند و برای تعداد یوزر بالا و شبکهای و ... مورد استفاده میباشد.
در اینجا گزینه اول را انتخاب میکنیم. در صورتیکه فایلهای دیگری جهت ضمیمه به پکیج احتیاج است به آن اضافه میکینم.
سپس کلید پکیج را زده تا پکیج مورد نظر آماده شود. دقت داشته باشید که بعد از انتخاب کامپایلر متلب، حجم پکیج نزدیک به 400 مگابایت خواهد شد. پکیج مورد نظر بصورت یک فایل exe فشرده خواهد شد.
معمولا پکیج شامل فایلهای زیر باید باشد :
Documentation files | componentName.xml |
Program Database File, which contains debugging information | componentName.pdb (if Debug optionis selected) |
Component assembly file | componentName.dll |
MCR Installer (if not already installed on the target machine). | MCR Installer |
بعد از طی مراحل فوق نوبت به تیم برنامهنویسی دات نت میرسد. بعد از دریافت پکیج از تیم برنامهنویسی متلب در صورتیکه بر روی سیستم هدف کامپایلر متلب و یا خود متلب نصب نیست باید از داخل پکیج این کامپایلر نصب شود.
دقت داشته باشید که ورژن کامپایلر بر روی سیستم باید با ورژن پکیج دریافتی یکی باشد.
در VS یک پروژه کنسول ایجاد کنید و از فولدر پکیج پروژه دریافتی در زیرفولدر distrib فایل makeSqr.dll را به رفرنس برنامه VS اضافه کنید.
در ادامه از مسیر نصب کامپایلر فایل MWArray.dll را هم به رفرنس پروژه اضافه کنید. این فایل جهت تبادل داده اپلیکیشن با کامپایلر متلب مورد استفاده قرار میگیرد.
installation_folder\toolbox\dotnetbuilder\bin\architecture\framework_version
using System; using MathWorks.MATLAB.NET.Arrays; using MyComponentName;
static void Main(string[] args) { MLTestClass obj = new MLTestClass(); MWArray[] result = obj.makesquare(1, 5); MWNumericArray output = (MWNumericArray)result[0]; Console.WriteLine(output); }
1- MWNumericArray یک اینترفیس جهت تعیین و نمایش نوع آرایههای عددی در متلب است.
2- MWArray یک کلاس abstract جهت دسترسی، فرمتدهی و مدیریت آرایههای متلب میباشد.
3- عدد 1 مشخص کننده تعداد خروجی تابع متلب و عدد 5 ورودی تابع میباشد.
نکته:
ورژن فریمورک دات نت در هنگام کامپایل با ورژن Mwarray.dll باید یکی باشد.
<Routes> <Route path="/product/list" element={<ProductList/>} /> <Route path="/product/new" element={<NewProduct/>} /> </Routes>
<Routes> <Route path="/post" element={<Posts/>}> <Route path="images" element={<Images />} /> <Route path="text" element={<Text />} /> <Route path="/post" element={<Text />} /> </Route> </Routes>
<div className="list-group"> <Link to="/post/images" className="list-group-item list-group-item-action active" aria-current="true"> Images Post </Link> <Link to="/post/text" className="list-group-item list-group-item-action">Text Post</Link> </div> <Outlet />
<div className="list-group"> <Link to="images" className="list-group-item list-group-item-action active" aria-current="true"> Images Post </Link> <Link to="text" className="list-group-item list-group-item-action">Text Post</Link> </div> <Outlet />
<Routes> <Route path="/post" element={<Posts/>}> <Route path="images" element={<Images />} /> <Route path="text" element={<Text />} /> <Route path="/post" element={<Text />} /> </Route> </Routes>
<Routes> <Route path="/product/list" element={<ProductList/>} /> <Route path="/product/new" element={<NewProduct/>} /> <Route path="/post" element={<Posts/>}> <Route path="images" element={<Images />} /> <Route path="text" element={<Text />} /> <Route path="/post" element={<Text />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>
2.Visual Studio 2017 15.6 منتشر شد
These are the customer-reported issues addressed in this release:
- "Specified argument out of the range of valid values, parameter name: count"
git
tags. - Cannot open git commit details.
- VS2017 Installer crashes on Modify when SyncFusion WPF Templates are installed.
- Test Explorer loses position when tests are run.
- Test explorer test hierarchy constantly jumps to top.
- XAML editor always crashes when a change is made to the XAML file.
- Test Explorer keeps scrolling to the top when a new test starts.
- Viewing Git History shows: specified argument was out of range of values.
- Alt+Up/Down does not reposition file in .NET Framework projects.
- VS 15.6 deadlocks on solution load for .NET Core SDK project when a 'None Include' is added to file list.
- 15.6 preview 6 discovers test adapter, but fails to run tests.
- Test Explorer keeps scrolling to the top when a new test starts.
- Microsoft Security Advisories for .NET Core updates.
Microsoft Security Advisories for .NET Core
CVE-2018-0875: Microsoft is aware of a security vulnerability in the public versions of .NET Core where a malicious file or web request could cause a denial of service (DoS) attack.
- System administrators are advised to update their .NET Core runtimes to versions 1.0.10, 1.1.7 or 2.0.6. Developers are advised to update their .NET Core SDK to versions 1.1.8 or 2.1.101.
حجم تقریبی بروزرسانی از نسخه 15.6.1 به 15.6.2 برابر 1.2GB میباشد