Angular与C# DLL API通信插件开发及DataService优化咨询
Great question! Let's walk through refining your Angular + Edge.js setup to make it more dynamic, maintainable, and user-friendly for button-triggered data fetching. First, let's address a couple of issues in your current DataService and then build out a robust solution.
Key Issues in Your Current Code
- Template String Misuse: You're using
${type}inside single quotes ('), which won't interpolate the variables—you need backticks (`) for that. - Asynchronous Handling: The
getDatamethod uses a callback but doesn't properly return the result asynchronously. Right now, the return value won't work as expected because the callback runs after the method exits. - Hardcoded Path: The DLL path is hardcoded, which makes the code less portable and harder to maintain.
Step 1: Refactor the DataService for Async & Maintainability
Let's rewrite the service to use Promises (so we can leverage async/await in components) and add configuration for the DLL path:
import { Injectable } from '@angular/core'; import { environment } from '../environments/environment'; const edge = require('edge'); @Injectable({ providedIn: 'root' }) export class DataService { private readonly dllPath = environment.siemensDllPath; constructor() { } getData(type: string, method: string): Promise<any> { return new Promise((resolve, reject) => { try { const dataFetcher = edge.func({ assemblyFile: this.dllPath, typeName: `CC.CommonInterfaces.${type}`, methodName: `${method}` }); dataFetcher(null, (error: any, result: any) => { if (error) { console.error('DLL call failed:', error); reject(new Error(`Failed to call method ${method} on type ${type}: ${error.message}`)); } else { console.log('DLL call succeeded:', result); resolve(result); } }); } catch (setupError) { reject(new Error(`Failed to initialize Edge.js function: ${setupError.message}`)); } }); } }
Add the DLL path to your environment.ts (and environment.prod.ts for production):
export const environment = { production: false, siemensDllPath: 'C:\\Program Files\\Common Files\\Siemens\\Automation\\Simatic OAM\\bin\\CC.CommonInterfaces.dll' };
Step 2: Build a Dynamic Component with Button Trigger
Create a component that lets users input (or select) the type and method, triggers the data fetch on button click, and shows loading/error states:
Component Template (data-fetcher.component.html)
<div class="data-fetch-container"> <div class="input-group"> <label>Type Name:</label> <input [(ngModel)]="selectedType" placeholder="e.g. MyDataInterface" class="input-field" > </div> <div class="input-group"> <label>Method Name:</label> <input [(ngModel)]="selectedMethod" placeholder="e.g. GetLatestData" class="input-field" > </div> <button (click)="handleFetchClick()" [disabled]="isLoading || !isInputValid()" class="fetch-btn" > {{ isLoading ? 'Fetching...' : 'Get Data' }} </button> <div *ngIf="errorMessage" class="error-alert"> {{ errorMessage }} </div> <div *ngIf="fetchedData" class="result-section"> <h3>Result:</h3> <pre>{{ fetchedData | json }}</pre> </div> </div>
Component Class (data-fetcher.component.ts)
import { Component } from '@angular/core'; import { DataService } from './data.service'; @Component({ selector: 'app-data-fetcher', templateUrl: './data-fetcher.component.html', styleUrls: ['./data-fetcher.component.css'] }) export class DataFetcherComponent { selectedType = ''; selectedMethod = ''; fetchedData: any; errorMessage = ''; isLoading = false; constructor(private dataService: DataService) {} isInputValid(): boolean { return !!this.selectedType.trim() && !!this.selectedMethod.trim(); } async handleFetchClick(): Promise<void> { if (!this.isInputValid()) return; this.isLoading = true; this.errorMessage = ''; this.fetchedData = null; try { this.fetchedData = await this.dataService.getData(this.selectedType, this.selectedMethod); } catch (err) { this.errorMessage = (err as Error).message; } finally { this.isLoading = false; } } }
Step 3: Enhance Dynamicity & Type Safety
To make the code even more robust and dynamic:
Add Enums for Valid Types/Methods: If you know the allowed
typeandmethodvalues upfront, create enums to avoid typos:export enum SiemensDataTypes { AssetProvider = 'AssetProvider', StatusMonitor = 'StatusMonitor' } export enum SiemensDataMethods { GetAllAssets = 'GetAllAssets', GetCurrentStatus = 'GetCurrentStatus' }Then replace the input fields with dropdowns using these enums for selection.
Add Caching: If some data doesn't change frequently, add a cache in
DataServiceto avoid redundant DLL calls:private cache = new Map<string, any>(); getData(type: string, method: string): Promise<any> { const cacheKey = `${type}-${method}`; if (this.cache.has(cacheKey)) { return Promise.resolve(this.cache.get(cacheKey)); } // ... existing Promise logic ... // After resolving, add to cache: this.cache.set(cacheKey, result); }
Critical Environment Note
Remember: Edge.js only works in a Node.js environment. This means your Angular app must be:
- An Electron application (runs in a Node.js + Chromium context)
- An Angular Universal app (server-side rendering, where the backend runs Node.js)
- A Node.js-based Angular app (not a pure browser-only app—browser JS can't directly call Windows DLLs)
If you need a browser-only solution, you'll need to build a Node.js backend API that calls the DLL, then have your Angular app hit that API via HTTP.
内容的提问来源于stack exchange,提问作者Moorky

