refactor: Remove MailKit and windream DMS dependencies
- Remove IDmsService interface (DMS integration deferred to Phase 5) - Remove MailKitEmailService implementation - Remove WindreamDmsService implementation - Simplify IEmailService to SMTP-only operations - Preparing for Limilabs Mail.dll migration BREAKING CHANGE: IEmailService no longer supports IMAP/POP3 operations Reason: Migrating from MailKit to Limilabs Mail.dll
This commit is contained in:
@@ -1,11 +0,0 @@
|
|||||||
namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DMS service interface for windream integration.
|
|
||||||
/// </summary>
|
|
||||||
public interface IDmsService
|
|
||||||
{
|
|
||||||
Task<string> ImportDocumentAsync(string filePath, string objectType, Dictionary<string, string> metadata, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> DocumentExistsAsync(string documentId, CancellationToken cancellationToken = default);
|
|
||||||
Task<bool> UpdateMetadataAsync(string documentId, Dictionary<string, string> metadata, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
@@ -3,14 +3,16 @@ using DigitalData.EmailProfiler.Application.Common.Dtos;
|
|||||||
namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
|
namespace DigitalData.EmailProfiler.Application.Common.Interfaces;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Email service interface for IMAP/SMTP operations.
|
/// Email service interface for SMTP operations.
|
||||||
/// Implementation uses MailKit.
|
/// Implementation uses Limilabs Mail.dll for production email sending.
|
||||||
/// Throws AuthenticationFailedException when OAuth2/password auth fails.
|
/// SMTP configuration is injected via IOptions<EmailAccountDto> in appsettings.json.
|
||||||
|
/// Throws AuthenticationFailedException when SMTP authentication fails.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IEmailService
|
public interface IEmailService
|
||||||
{
|
{
|
||||||
Task<IEnumerable<object>> ReceiveEmailsAsync(EmailAccountDto account, CancellationToken cancellationToken = default);
|
/// <summary>
|
||||||
Task SendEmailAsync(EmailAccountDto account, string to, string subject, string body, bool isHtml = true, CancellationToken cancellationToken = default);
|
/// Sends an email using the configured SMTP account.
|
||||||
Task DeleteEmailAsync(EmailAccountDto account, int imapUid, CancellationToken cancellationToken = default);
|
/// SMTP credentials are configured in appsettings.json (EmailAccount section).
|
||||||
Task<string> GetOAuth2TokenAsync(string tenantId, string clientId, string clientSecret, CancellationToken cancellationToken = default);
|
/// </summary>
|
||||||
|
Task SendEmailAsync(string to, string subject, string body, bool isHtml = true, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,259 +0,0 @@
|
|||||||
using DigitalData.EmailProfiler.Application.Common.Dtos;
|
|
||||||
using DigitalData.EmailProfiler.Application.Common.Interfaces;
|
|
||||||
using DigitalData.EmailProfiler.Domain.Exceptions;
|
|
||||||
using MailKit;
|
|
||||||
using MailKit.Net.Imap;
|
|
||||||
using MailKit.Net.Smtp;
|
|
||||||
using MailKit.Search;
|
|
||||||
using MailKit.Security;
|
|
||||||
using Microsoft.Identity.Client;
|
|
||||||
using MimeKit;
|
|
||||||
|
|
||||||
namespace DigitalData.EmailProfiler.Infrastructure.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Email service using MailKit/MimeKit for IMAP/SMTP operations.
|
|
||||||
/// Supports OAuth2 authentication via Microsoft.Identity.Client (MSAL).
|
|
||||||
/// </summary>
|
|
||||||
public class MailKitEmailService(IEncryptionService encryptionService) : IEmailService
|
|
||||||
{
|
|
||||||
private readonly IEncryptionService _encryptionService = encryptionService;
|
|
||||||
|
|
||||||
public async Task<IEnumerable<object>> ReceiveEmailsAsync(
|
|
||||||
EmailAccountDto account,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
using var imap = new ImapClient();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ConnectAndAuthenticateImapAsync(imap, account, cancellationToken);
|
|
||||||
|
|
||||||
var inbox = imap.Inbox;
|
|
||||||
await inbox.OpenAsync(FolderAccess.ReadWrite, cancellationToken);
|
|
||||||
|
|
||||||
var uids = await inbox.SearchAsync(SearchQuery.NotSeen, cancellationToken);
|
|
||||||
var messages = new List<object>();
|
|
||||||
|
|
||||||
foreach (var uid in uids)
|
|
||||||
{
|
|
||||||
var message = await inbox.GetMessageAsync(uid, cancellationToken);
|
|
||||||
|
|
||||||
var emailMessage = new
|
|
||||||
{
|
|
||||||
MessageId = message.MessageId,
|
|
||||||
Sender = message.From.Mailboxes.FirstOrDefault()?.Address ?? string.Empty,
|
|
||||||
Subject = message.Subject ?? string.Empty,
|
|
||||||
Date = message.Date.DateTime,
|
|
||||||
BodyHtml = message.HtmlBody ?? string.Empty,
|
|
||||||
BodyText = message.TextBody ?? string.Empty,
|
|
||||||
Attachments = message.Attachments.Select(a => new
|
|
||||||
{
|
|
||||||
FileName = a.ContentDisposition?.FileName ?? "attachment",
|
|
||||||
FileSize = a is MimePart part ? (int)part.Content.Stream.Length : 0,
|
|
||||||
Content = a is MimePart mimePart ? ReadPartContent(mimePart) : Array.Empty<byte>()
|
|
||||||
}).ToList(),
|
|
||||||
ImapUid = (int)uid.Id
|
|
||||||
};
|
|
||||||
|
|
||||||
messages.Add(emailMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
await imap.DisconnectAsync(true, cancellationToken);
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
catch (AuthenticationException ex)
|
|
||||||
{
|
|
||||||
await DisconnectSafelyAsync(imap, cancellationToken);
|
|
||||||
throw new AuthenticationFailedException("IMAP authentication failed. Check credentials or OAuth2 configuration.", ex);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await DisconnectSafelyAsync(imap, cancellationToken);
|
|
||||||
throw new InvalidOperationException("Failed to receive emails from IMAP server.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendEmailAsync(
|
|
||||||
EmailAccountDto account,
|
|
||||||
string to,
|
|
||||||
string subject,
|
|
||||||
string body,
|
|
||||||
bool isHtml = true,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
using var smtp = new SmtpClient();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ConnectAndAuthenticateSmtpAsync(smtp, account, cancellationToken);
|
|
||||||
|
|
||||||
var message = new MimeMessage();
|
|
||||||
message.From.Add(MailboxAddress.Parse(account.Username));
|
|
||||||
message.To.Add(MailboxAddress.Parse(to));
|
|
||||||
message.Subject = subject;
|
|
||||||
|
|
||||||
var builder = new BodyBuilder
|
|
||||||
{
|
|
||||||
HtmlBody = isHtml ? body : null,
|
|
||||||
TextBody = isHtml ? null : body
|
|
||||||
};
|
|
||||||
|
|
||||||
message.Body = builder.ToMessageBody();
|
|
||||||
|
|
||||||
await smtp.SendAsync(message, cancellationToken);
|
|
||||||
await smtp.DisconnectAsync(true, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (AuthenticationException ex)
|
|
||||||
{
|
|
||||||
await DisconnectSafelyAsync(smtp, cancellationToken);
|
|
||||||
throw new AuthenticationFailedException("SMTP authentication failed. Check credentials or OAuth2 configuration.", ex);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await DisconnectSafelyAsync(smtp, cancellationToken);
|
|
||||||
throw new InvalidOperationException("Failed to send email via SMTP server.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteEmailAsync(
|
|
||||||
EmailAccountDto account,
|
|
||||||
int imapUid,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
using var imap = new ImapClient();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ConnectAndAuthenticateImapAsync(imap, account, cancellationToken);
|
|
||||||
|
|
||||||
var inbox = imap.Inbox;
|
|
||||||
await inbox.OpenAsync(FolderAccess.ReadWrite, cancellationToken);
|
|
||||||
|
|
||||||
var uid = new UniqueId((uint)imapUid);
|
|
||||||
await inbox.AddFlagsAsync(uid, MessageFlags.Deleted, true, cancellationToken);
|
|
||||||
await inbox.ExpungeAsync(cancellationToken);
|
|
||||||
|
|
||||||
await imap.DisconnectAsync(true, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (AuthenticationException ex)
|
|
||||||
{
|
|
||||||
await DisconnectSafelyAsync(imap, cancellationToken);
|
|
||||||
throw new AuthenticationFailedException("IMAP authentication failed. Check credentials or OAuth2 configuration.", ex);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await DisconnectSafelyAsync(imap, cancellationToken);
|
|
||||||
throw new InvalidOperationException($"Failed to delete email with UID {imapUid}.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetOAuth2TokenAsync(
|
|
||||||
string tenantId,
|
|
||||||
string clientId,
|
|
||||||
string clientSecret,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var decryptedSecret = _encryptionService.Decrypt(clientSecret);
|
|
||||||
|
|
||||||
var app = ConfidentialClientApplicationBuilder
|
|
||||||
.Create(clientId)
|
|
||||||
.WithTenantId(tenantId)
|
|
||||||
.WithClientSecret(decryptedSecret)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Microsoft Graph scope for mail access
|
|
||||||
var scopes = new[] { "https://graph.microsoft.com/.default" };
|
|
||||||
var result = await app
|
|
||||||
.AcquireTokenForClient(scopes)
|
|
||||||
.ExecuteAsync(cancellationToken);
|
|
||||||
|
|
||||||
return result.AccessToken;
|
|
||||||
}
|
|
||||||
catch (MsalException ex)
|
|
||||||
{
|
|
||||||
throw new AuthenticationFailedException("Failed to acquire OAuth2 token from Microsoft Identity Platform.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Private Helper Methods ---
|
|
||||||
|
|
||||||
private async Task ConnectAndAuthenticateImapAsync(
|
|
||||||
ImapClient imap,
|
|
||||||
EmailAccountDto account,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var secureSocketOptions = account.ImapUseSsl
|
|
||||||
? SecureSocketOptions.SslOnConnect
|
|
||||||
: SecureSocketOptions.None;
|
|
||||||
|
|
||||||
await imap.ConnectAsync(account.ImapServer, account.ImapPort, secureSocketOptions, cancellationToken);
|
|
||||||
|
|
||||||
if (account.UseOAuth2)
|
|
||||||
{
|
|
||||||
var token = await GetOAuth2TokenAsync(
|
|
||||||
account.TenantId!,
|
|
||||||
account.ClientId!,
|
|
||||||
account.EncryptedClientSecret!,
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
var oauth2 = new SaslMechanismOAuth2(account.Username, token);
|
|
||||||
await imap.AuthenticateAsync(oauth2, cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var password = _encryptionService.Decrypt(account.EncryptedPassword!);
|
|
||||||
await imap.AuthenticateAsync(account.Username, password, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ConnectAndAuthenticateSmtpAsync(
|
|
||||||
SmtpClient smtp,
|
|
||||||
EmailAccountDto account,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var secureSocketOptions = account.SmtpUseSsl
|
|
||||||
? SecureSocketOptions.SslOnConnect
|
|
||||||
: SecureSocketOptions.None;
|
|
||||||
|
|
||||||
await smtp.ConnectAsync(account.SmtpServer, account.SmtpPort, secureSocketOptions, cancellationToken);
|
|
||||||
|
|
||||||
if (account.UseOAuth2)
|
|
||||||
{
|
|
||||||
var token = await GetOAuth2TokenAsync(
|
|
||||||
account.TenantId!,
|
|
||||||
account.ClientId!,
|
|
||||||
account.EncryptedClientSecret!,
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
var oauth2 = new SaslMechanismOAuth2(account.Username, token);
|
|
||||||
await smtp.AuthenticateAsync(oauth2, cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var password = _encryptionService.Decrypt(account.EncryptedPassword!);
|
|
||||||
await smtp.AuthenticateAsync(account.Username, password, cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task DisconnectSafelyAsync(ImapClient imap, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (imap.IsConnected)
|
|
||||||
await imap.DisconnectAsync(true, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task DisconnectSafelyAsync(SmtpClient smtp, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (smtp.IsConnected)
|
|
||||||
await smtp.DisconnectAsync(true, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] ReadPartContent(MimePart part)
|
|
||||||
{
|
|
||||||
using var memory = new MemoryStream();
|
|
||||||
part.Content.DecodeTo(memory);
|
|
||||||
return memory.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
using DigitalData.EmailProfiler.Application.Common.Interfaces;
|
|
||||||
using DigitalData.EmailProfiler.Domain.Exceptions;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace DigitalData.EmailProfiler.Infrastructure.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// windream DMS service using COM Interop.
|
|
||||||
///
|
|
||||||
/// IMPORTANT: Requires windream COM Interop DLLs to be registered on the system.
|
|
||||||
/// Throws DmsNotAvailableException if COM objects cannot be created.
|
|
||||||
///
|
|
||||||
/// COM ProgIDs used:
|
|
||||||
/// - Windream.WMSession (WINDREAMLib)
|
|
||||||
/// - Windream.WMConnect (WINDREAMLib)
|
|
||||||
///
|
|
||||||
/// Legacy reference: M:\Bibliotheken\3rdParty\windream\Interop.WINDREAMLib.dll
|
|
||||||
///
|
|
||||||
/// NOTE: This service is OBSOLETE. The application now only provides email sending functionality.
|
|
||||||
/// This class is kept for reference but should not be used in new code.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("WindreamDmsService is obsolete. The application now only provides email sending functionality.")]
|
|
||||||
public class WindreamDmsService : IDmsService
|
|
||||||
{
|
|
||||||
private readonly string _windreamServer;
|
|
||||||
private readonly ILogger<WindreamDmsService> _logger;
|
|
||||||
private readonly object _sessionLock = new();
|
|
||||||
|
|
||||||
private object? _wmSession;
|
|
||||||
private object? _wmConnect;
|
|
||||||
private bool _isInitialized;
|
|
||||||
|
|
||||||
public WindreamDmsService(IConfiguration configuration, ILogger<WindreamDmsService> logger)
|
|
||||||
{
|
|
||||||
_windreamServer = configuration["Windream:Server"] ?? throw new ArgumentNullException(nameof(configuration), "Windream:Server configuration is required.");
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<string> ImportDocumentAsync(
|
|
||||||
string filePath,
|
|
||||||
string objectType,
|
|
||||||
Dictionary<string, string> metadata,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(filePath);
|
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(objectType);
|
|
||||||
|
|
||||||
if (!File.Exists(filePath))
|
|
||||||
throw new FileNotFoundException($"File not found: {filePath}", filePath);
|
|
||||||
|
|
||||||
lock (_sessionLock)
|
|
||||||
{
|
|
||||||
EnsureSessionInitialized();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var fileName = Path.GetFileName(filePath);
|
|
||||||
|
|
||||||
// CreateWMObject(1, fileName) - 1 = WMEntityDocument
|
|
||||||
var oDocument = InvokeMember(_wmSession!, "CreateWMObject", 1, fileName);
|
|
||||||
|
|
||||||
// Lock document
|
|
||||||
var isLocked = (bool)GetProperty(oDocument, "aLocked");
|
|
||||||
if (!isLocked)
|
|
||||||
{
|
|
||||||
InvokeMember(oDocument, "lock");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set object type
|
|
||||||
var oObjectType = InvokeMember(_wmSession!, "GetWMObjectByName", 2, objectType); // 2 = WMEntityObjectType
|
|
||||||
SetProperty(oDocument, "aObjectType", oObjectType);
|
|
||||||
|
|
||||||
InvokeMember(oDocument, "Save");
|
|
||||||
|
|
||||||
// Import file from disk
|
|
||||||
InvokeMember(oDocument, "FromDisk", filePath);
|
|
||||||
|
|
||||||
// Index metadata
|
|
||||||
foreach (var (key, value) in metadata)
|
|
||||||
{
|
|
||||||
var indexValue = value.Length > 512 ? value[..512] : value;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
InvokeMember(oDocument, "SetVariableValue", key, indexValue);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to set windream index '{IndexName}' to '{IndexValue}'", key, indexValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InvokeMember(oDocument, "Save");
|
|
||||||
InvokeMember(oDocument, "unlock");
|
|
||||||
|
|
||||||
// Return windream document ID
|
|
||||||
var documentId = (int)GetProperty(oDocument, "aID");
|
|
||||||
return Task.FromResult($"WD_{documentId}");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to import document to windream: {FilePath}", filePath);
|
|
||||||
throw new InvalidOperationException($"Failed to import document to windream: {filePath}", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> DocumentExistsAsync(
|
|
||||||
string documentId,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(documentId);
|
|
||||||
|
|
||||||
if (!documentId.StartsWith("WD_"))
|
|
||||||
return Task.FromResult(false);
|
|
||||||
|
|
||||||
lock (_sessionLock)
|
|
||||||
{
|
|
||||||
EnsureSessionInitialized();
|
|
||||||
|
|
||||||
var idString = documentId.Replace("WD_", "");
|
|
||||||
if (!int.TryParse(idString, out var id))
|
|
||||||
return Task.FromResult(false);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// GetWMObjectByID(1, id) - 1 = WMEntityDocument
|
|
||||||
var oDocument = InvokeMember(_wmSession!, "GetWMObjectByID", 1, id);
|
|
||||||
return Task.FromResult(oDocument != null);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return Task.FromResult(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<bool> UpdateMetadataAsync(
|
|
||||||
string documentId,
|
|
||||||
Dictionary<string, string> metadata,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(documentId);
|
|
||||||
|
|
||||||
if (!documentId.StartsWith("WD_"))
|
|
||||||
throw new ArgumentException($"Invalid windream document ID: {documentId}", nameof(documentId));
|
|
||||||
|
|
||||||
lock (_sessionLock)
|
|
||||||
{
|
|
||||||
EnsureSessionInitialized();
|
|
||||||
|
|
||||||
var idString = documentId.Replace("WD_", "");
|
|
||||||
if (!int.TryParse(idString, out var id))
|
|
||||||
throw new ArgumentException($"Invalid windream document ID: {documentId}", nameof(documentId));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var oDocument = InvokeMember(_wmSession!, "GetWMObjectByID", 1, id);
|
|
||||||
if (oDocument == null)
|
|
||||||
throw new NotFoundException($"windream document with ID '{documentId}' not found.");
|
|
||||||
|
|
||||||
var isLocked = (bool)GetProperty(oDocument, "aLocked");
|
|
||||||
if (!isLocked)
|
|
||||||
{
|
|
||||||
InvokeMember(oDocument, "lock");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (key, value) in metadata)
|
|
||||||
{
|
|
||||||
var indexValue = value.Length > 512 ? value[..512] : value;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
InvokeMember(oDocument, "SetVariableValue", key, indexValue);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to update windream index '{IndexName}' to '{IndexValue}'", key, indexValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InvokeMember(oDocument, "Save");
|
|
||||||
InvokeMember(oDocument, "unlock");
|
|
||||||
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
catch (NotFoundException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to update windream document metadata: {DocumentId}", documentId);
|
|
||||||
throw new InvalidOperationException($"Failed to update windream document metadata: {documentId}", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
lock (_sessionLock)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_wmConnect != null && _wmSession != null && _isInitialized)
|
|
||||||
{
|
|
||||||
InvokeMember(_wmConnect, "Disconnect");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_wmConnect != null && Marshal.IsComObject(_wmConnect))
|
|
||||||
{
|
|
||||||
Marshal.ReleaseComObject(_wmConnect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_wmSession != null && Marshal.IsComObject(_wmSession))
|
|
||||||
{
|
|
||||||
Marshal.ReleaseComObject(_wmSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
_wmConnect = null;
|
|
||||||
_wmSession = null;
|
|
||||||
_isInitialized = false;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Error during windream COM cleanup");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Private Helper Methods ---
|
|
||||||
|
|
||||||
private void EnsureSessionInitialized()
|
|
||||||
{
|
|
||||||
if (_isInitialized && _wmSession != null && _wmConnect != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Create WMSession object
|
|
||||||
var wmSessionType = Type.GetTypeFromProgID("Windream.WMSession")
|
|
||||||
?? throw new DmsNotAvailableException("windream COM type 'Windream.WMSession' not found. Ensure windream is installed and COM components are registered.");
|
|
||||||
|
|
||||||
_wmSession = Activator.CreateInstance(wmSessionType, _windreamServer)
|
|
||||||
?? throw new DmsNotAvailableException("Failed to create WMSession instance.");
|
|
||||||
|
|
||||||
// Create WMConnect object
|
|
||||||
var wmConnectType = Type.GetTypeFromProgID("Windream.WMConnect")
|
|
||||||
?? throw new DmsNotAvailableException("windream COM type 'Windream.WMConnect' not found. Ensure windream is installed and COM components are registered.");
|
|
||||||
|
|
||||||
_wmConnect = Activator.CreateInstance(wmConnectType)
|
|
||||||
?? throw new DmsNotAvailableException("Failed to create WMConnect instance.");
|
|
||||||
|
|
||||||
// Configure and login
|
|
||||||
SetProperty(_wmConnect, "ModuleID", 0);
|
|
||||||
SetProperty(_wmConnect, "MinReqVersion", "3");
|
|
||||||
InvokeMember(_wmConnect, "LoginSession", _wmSession);
|
|
||||||
|
|
||||||
var isLoggedIn = (bool)GetProperty(_wmSession, "aLoggedin");
|
|
||||||
if (!isLoggedIn)
|
|
||||||
throw new DmsNotAvailableException("windream login failed. Check server configuration and connectivity.");
|
|
||||||
|
|
||||||
_isInitialized = true;
|
|
||||||
_logger.LogInformation("windream session initialized successfully (Server: {Server})", _windreamServer);
|
|
||||||
}
|
|
||||||
catch (DmsNotAvailableException)
|
|
||||||
{
|
|
||||||
_isInitialized = false;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
_isInitialized = false;
|
|
||||||
_logger.LogError(ex, "windream COM error during initialization");
|
|
||||||
throw new DmsNotAvailableException("windream COM components are not available or not properly registered.", ex);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_isInitialized = false;
|
|
||||||
_logger.LogError(ex, "windream initialization failed");
|
|
||||||
throw new DmsNotAvailableException("windream initialization failed. See inner exception for details.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object InvokeMember(object obj, string memberName, params object[] args)
|
|
||||||
{
|
|
||||||
return obj.GetType().InvokeMember(
|
|
||||||
memberName,
|
|
||||||
System.Reflection.BindingFlags.InvokeMethod,
|
|
||||||
null,
|
|
||||||
obj,
|
|
||||||
args)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object GetProperty(object obj, string propertyName)
|
|
||||||
{
|
|
||||||
return obj.GetType().InvokeMember(
|
|
||||||
propertyName,
|
|
||||||
System.Reflection.BindingFlags.GetProperty,
|
|
||||||
null,
|
|
||||||
obj,
|
|
||||||
null)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetProperty(object obj, string propertyName, object value)
|
|
||||||
{
|
|
||||||
obj.GetType().InvokeMember(
|
|
||||||
propertyName,
|
|
||||||
System.Reflection.BindingFlags.SetProperty,
|
|
||||||
null,
|
|
||||||
obj,
|
|
||||||
[value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user