Cosidering I can't change class ReadTheFile, is there a way how to "chain" instance methods so I don't have to reference myObject multiple times? I am looking for something like
myObject.Read().Open(param1);
I can write myObject.Open(param1).Read(); which compiles however Read() is not executed.
//myObject.Open(param1).Read(); does execute but it's executed as StreamReader method not ReadTheFile method. Overlooked VS help...
class TestT211
{
static void Main(string[] args)
{
var myObject = new ReadTheFile();
myObject.Read(myObject.Open(#"C:\file.txt"));
}
}
public class ReadTheFile
{
private int _lineCounter = 0;
private string _lineOfText;
public StreamReader Open(string path)
{
return new StreamReader(path);
}
public void Read(StreamReader sr)
{
while ((_lineOfText = sr.ReadLine()) != null) {
Console.WriteLine(_lineOfText);
}
}
}
If you want to have a syntax like a Fluent API you need to change some point of your class. First you need to have Open return the current instance and then use that instance to call Read. But this assumes that you keep the StreamReader as an internal variable of the class
public class ReadTheFile : IDisposable
{
private int _lineCounter = 0;
private string _lineOfText;
private StreamReader _sr = null;
public ReadTheFile Open(string path)
{
_sr = new StreamReader(path);
return this;
}
public void Read()
{
if(_sr == null) return;
while ((_lineOfText = _sr.ReadLine()) != null) {
Console.WriteLine(_lineOfText);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if(_sr != null)
{
_sr.Close();
_sr = null;
}
}
}
}
and now you can write
using(ReadTheFile myObject = new ReadTheFile())
myObject.Open(#"C:\file.txt").Read();
Notice that having a StreamReader between your internal variables requires you to implement the IDisposable interface to properly close and dispose the Stream
If you want to add more functionality to class without access to it, you can use extensions. For example to use ReadTheFile to read StreamReader.
public static class StreamReaderExtension
{
public static StreamReader ReadEx(this StreamReader sr)
{
var readTheFile = new ReadTheFile();
readTheFile.Read(sr);
return sr;
}
}
and than call it with
myObject.Open(#"C:\file.txt").ReadEx();
You can add more methods, but cannot override the instance ones so you have to create a new name or new signature.
Using simple builder (it's overkill) but you could achive what you want:
class TestT211
{
static void Main(string[] args)
{
FileReaderBuilder.New.Open(#"C:\file.txt").Read();
}
}
public class ReadTheFile
{
private int _lineCounter = 0;
private string _lineOfText;
public StreamReader Open(string path)
{
return new StreamReader(path);
}
public void Read(StreamReader sr)
{
while ((_lineOfText = sr.ReadLine()) != null)
{
Console.WriteLine(_lineOfText);
}
}
}
public class FileReaderBuilder
{
private readonly ReadTheFile _file;
private StreamReader _streamReader;
private FileReaderBuilder()
{
_file = new ReadTheFile();
}
public FileReaderBuilder Open(string path)
{
_streamReader = _file.Open(path);
return this;
}
public FileReaderBuilder Read()
{
if (_streamReader == null)
{
throw new ArgumentNullException(nameof(_streamReader));
}
_file.Read(_streamReader);
return this;
}
public static FileReaderBuilder New => new FileReaderBuilder();
}
Please see my first Persistent Subscription below:
namespace PersistentSubscription
{
internal class Program
{
private static void Main()
{
var subscription = new PersistentSubscriptionClient();
subscription.Start();
}
}
public class PersistentSubscriptionClient
{
private IEventStoreConnection _conn;
private const string STREAM = "$ce-customer";
private const string GROUP = "a_test_group";
private const int DEFAULTPORT = 1113;
private static readonly UserCredentials User = new UserCredentials("admin", "changeit");
private EventStorePersistentSubscriptionBase _subscription;
public void Start()
{
var settings = ConnectionSettings.Create();
using (_conn = EventStoreConnection.Create(settings, new IPEndPoint(IPAddress.Loopback, DEFAULTPORT)))
{
_conn.ConnectAsync().Wait();
CreateSubscription();
ConnectToSubscription();
Console.WriteLine("waiting for events. press enter to exit");
Console.ReadLine();
}
}
private void ConnectToSubscription()
{
var bufferSize = 10;
var autoAck = true;
Action<EventStorePersistentSubscriptionBase, ResolvedEvent> eventAppeared = EventAppeared;
_subscription = _conn.ConnectToPersistentSubscription(STREAM, GROUP, eventAppeared, SubscriptionDropped, User, bufferSize, autoAck);
}
private void SubscriptionDropped(EventStorePersistentSubscriptionBase eventStorePersistentSubscriptionBase,
SubscriptionDropReason subscriptionDropReason, Exception ex)
{
ConnectToSubscription();
}
private static void EventAppeared(EventStorePersistentSubscriptionBase eventStorePersistentSubscriptionBase,
ResolvedEvent resolvedEvent)
{
MemoryStream stream = new MemoryStream(resolvedEvent.Event.Data);
IFormatter formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
try
{
CustomerCreated customerCreated = (CustomerCreated)formatter.Deserialize(stream);
Console.WriteLine(customerCreated);
}
catch (Exception e)
{
var test = "test";
}
}
private void CreateSubscription()
{
PersistentSubscriptionSettings settings = PersistentSubscriptionSettings.Create()
.DoNotResolveLinkTos()
.StartFromCurrent();
try
{
_conn.CreatePersistentSubscriptionAsync(STREAM, GROUP, settings, User).Wait();
}
catch (AggregateException ex)
{
if (ex.InnerException.GetType() != typeof(InvalidOperationException)
&& ex.InnerException?.Message != $"Subscription group {GROUP} on stream {STREAM} already exists")
{
throw;
}
}
}
}
}
and my first client below:
using System;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using EventStore.ClientAPI;
namespace WritingEvents
{
class Program
{
static void Main(string[] args)
{
const int DEFAULTPORT = 1113;
var settings = ConnectionSettings.Create();
using (var conn = EventStoreConnection.Create(settings, new IPEndPoint(IPAddress.Loopback, DEFAULTPORT)))
{
conn.ConnectAsync().Wait();
CustomerCreated c1 = new CustomerCreated { Id = Guid.NewGuid(), Name = "Maria" };
EventData customerCreated1 = GetEventDataFor(c1);
conn.AppendToStreamAsync("customer-100", ExpectedVersion.Any, customerCreated1).Wait();
}
}
private static EventData GetEventDataFor(CustomerCreated customerCreated)
{
IFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, customerCreated);
byte[] customerCreatedEventByteArray = stream.ToArray();
return new EventData(
Guid.NewGuid(),
"eventType",
true,
customerCreatedEventByteArray,
null
);
}
}
[Serializable]
public class CustomerCreated
{
public Guid Id { get; set; }
public string Name { get; set; }
}
}
I run the server and then then the client. I see an error when deserializing the CustomerCreated event on the server side. The error is: "End of stream was encountered before parsing was completed".
If I change this line:
private const string STREAM = "$ce-customer";
to this:
private const string STREAM = "customer-100";
Then deserialization works correctly on the server side.
How do I handle all customer events - not just customer 100?
I have --run-projections=all when starting Event Store. I have also enabled all projections:
This question helped me: Using the Event Store Client API (.NET), how to I write to a stream and link one event to another?
I simply had to change this:
PersistentSubscriptionSettings settings = PersistentSubscriptionSettings.Create()
.DoNotResolveLinkTos() //Specifically this line
.StartFromCurrent();
to this:
PersistentSubscriptionSettings settings = PersistentSubscriptionSettings.Create()
.ResolveLinkTos() //Specifically this line
.StartFromCurrent();
DoNotResolveLinkTos gets a link to the original event, whereas ResolveLinkTos gets the actual event itself. Therefore I was trying to deserialize the link object, which was causing the exception.
I'm making an application as follows: I have a webservice running on a local server, this webservice returns json like this:
[{"Id":1,"Titulo":"Live to win","Link":"https://www.youtube.com/embed/DPHlGVe8wxI","BandaId":1,"BandaNome":"Paul Stanley","GeneroId":1,"GeneroNome":"Rock","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":false},{"Id":2,"Titulo":"Welcome to the jungle","Link":null,"BandaId":2,"BandaNome":"Guns n´ roses","GeneroId":1,"GeneroNome":"Rock","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":3,"Titulo":"Something just like this","Link":null,"BandaId":3,"BandaNome":"The Chainsmokers","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":4,"Titulo":"Beliver","Link":null,"BandaId":4,"BandaNome":"Imagine Dragons","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":5,"Titulo":"Radioactive","Link":null,"BandaId":4,"BandaNome":"Imagine Dragons","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":6,"Titulo":"Friends - Original Mix","Link":null,"BandaId":5,"BandaNome":"Steener","GeneroId":2,"GeneroNome":"EDM","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":7,"Titulo":"Amanheceu","Link":null,"BandaId":6,"BandaNome":"Scalene","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":8,"Titulo":"Sonhador II","Link":null,"BandaId":6,"BandaNome":"Scalene","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":9,"Titulo":"Amianto","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":10,"Titulo":"Monstros","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":11,"Titulo":"Piloto Automático","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":2,"CriadorNome":"Usuário Teste","Ativo":true},{"Id":12,"Titulo":"Eutanásia","Link":null,"BandaId":7,"BandaNome":"Supercombo","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-23T16:42:54","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true},{"Id":13,"Titulo":"Shots - Broiler Remix","Link":null,"BandaId":4,"BandaNome":"Imagine Dragons","GeneroId":3,"GeneroNome":"Indie","DtCriacao":"2017-03-24T16:55:46","CriadorId":1,"CriadorNome":"Márcio Eric","Ativo":true}]
But now I need to create a Windows Services that communicates with this WebService and what it receives in a database, I spent all day doing this webservice in several ways, but I did not find anything about good practices with webservices or etc. I'll show you what I did, but I'd like some tips on windows services, thank you
Here is my code
Service:
private Timer _worker;
private readonly int _interval = Convert.ToInt32(ConfigurationManager.AppSettings["timer"]);
private readonly string _connection = ConfigurationManager.AppSettings["connection"];
//private readonly Connector _usuarioConnector;
private readonly Connector _bandaConnector;
//private readonly Connector _generoConnector;
private readonly Connector _musicaConnector;
private SqlConnection conn;
public Service1()
{
//_usuarioConnector = new Connector("UsuarioBase");
_bandaConnector = new Connector("BandaBase");
//_generoConnector = new Connector("GeneroBase");
_musicaConnector = new Connector("MusicaBase");
InitializeComponent();
}
protected override void OnStart(string[] args)
{
_worker = new Timer((Update), null, 0, _interval);
}
protected override void OnStop()
{
}
private void Update(Object state)
{
using (SqlConnection connection = new SqlConnection(
_connection))
{
SqlCommand command = new SqlCommand("insert into Genero(nome, descricao, dtcriacao, criadorid, ativo) values('teste', 'teste', getdate(), 1, 1)", connection);
command.Connection.Open();
command.ExecuteNonQuery();
EventLog.WriteEntry("Query executada", EventLogEntryType.Warning);
}
}
Loader
public class Loader
{
public static List<Usuario> LoadUsuarios(Connector usuarioConnector)
{
return (List<Usuario>)Newtonsoft.Json.JsonConvert.DeserializeObject(usuarioConnector.GetData(), typeof(List<Usuario>));
}
public static List<Banda> LoadBandas(Connector bandaConnector)
{
var bandasDtos = (List<BandaDto>)Newtonsoft.Json.JsonConvert.DeserializeObject(bandaConnector.GetData(), typeof(List<BandaDto>));
return bandasDtos.Select(dto => dto.ConvertToBanda()).ToList();
}
//public static List<Genero> LoadGeneros(Connector generoConnector)
//{
//var musicasDtos = (List<MusicaDto>)Newtonsoft.Json.JsonConvert.DeserializeObject(musicaConnector.GetData(), typeof(List<MusicaDto>));
//return musicasDtos.Select(dto => dto.ConvertToMusica()).ToList();
//}
public static List<Musica> LoadMusicas(Connector musicaConnector)
{
var musicasDtos = (List<MusicaDto>)Newtonsoft.Json.JsonConvert.DeserializeObject(musicaConnector.GetData(), typeof(List<MusicaDto>));
return musicasDtos.Select(dto => dto.ConvertToMusica()).ToList();
}
}
Connector
private string Host { get; }
private WebRequest _request;
public WebResponse Response;
public string ConnectionStatus;
private Stream _dataStream;
private StreamReader _reader;
public Connector(string config)
{
Host = ConfigurationManager.AppSettings[config];
}
public string GetData()
{
StartConnection();
_dataStream = Response.GetResponseStream();
_reader = new StreamReader(_dataStream);
var toReturn = _reader.ReadToEnd();
EndConnection();
return toReturn;
}
private void StartConnection()
{
_request = WebRequest.Create(Host);
_request.Credentials = CredentialCache.DefaultCredentials;
Response = _request.GetResponse();
ConnectionStatus = (((HttpWebResponse)Response).StatusDescription);
}
private void EndConnection()
{
_reader.Close();
Response.Close();
}
Edit1 In the current code it is doing a select, however in what I intend to do it will do inserts
I discovered, unfortunately the timer was not working, so I just changed it to a thread.
I'm new in Microsoft Message Queue in Windows Server, I need to push, if the EmployeeID is NULL.
The Employee Model Class is
public class Employee
{
public string EmployeeID { get; set; }
public string EmployeeName { get; set; }
}
public void ValidationProcess(Employee emp)
{
if((emp != null) || (emp.EmployeeID == null))
{
// Push into Validation Exception Queue using MSMQ
}
}
Once the Data pushed into that Validation Exception Queue, it should be processed by separate process. Every 1hr the process need to initiate and it should call the following method
public void ValidationExceptionProcess(object obj)
{
// Some Inner Process
// Log the Error
}
Kindly guide me how to create and process it.
First Step:
Install MSMQs as a windows feature on the server/pc
Then:
- Create the queue if it does not exist
- Push the message in the queue asynchronously
Useful guide
Code example for pushing and retrieving messages from msmq:
public class ExceptionMSMQ
{
private static readonly string description = "Example description";
private static readonly string path = #".\Private$\myqueue";
private static MessageQueue exceptionQueue;
public static MessageQueue ExceptionQueue
{
get
{
if (exceptionQueue == null)
{
try
{
if (MessageQueue.Exists(path))
{
exceptionQueue = new MessageQueue(path);
exceptionQueue.Label = description;
}
else
{
MessageQueue.Create(path);
exceptionQueue = new MessageQueue(path);
exceptionQueue.Label = description;
}
}
catch
{
throw;
}
finally
{
exceptionQueue.Dispose();
}
}
return exceptionQueue;
}
}
public static void PushMessage(string message)
{
ExceptionQueue.Send(message);
}
private static List<string> RetrieveMessages()
{
List<string> messages = new List<string>();
using (ExceptionQueue)
{
System.Messaging.Message[] queueMessages = ExceptionQueue.GetAllMessages();
foreach (System.Messaging.Message message in queueMessages)
{
message.Formatter = new XmlMessageFormatter(
new String[] { "System.String, mscorlib" });
string msg = message.Body.ToString();
messages.Add(msg);
}
}
return messages;
}
public static void Main(string[] args)
{
ExceptionMSMQ.PushMessage("my exception string");
}
}
An other widely used way to do that would also be to use out of the box loggers which already contains this functionality like Enterprise Library or NLog which provide easy interfaces to do that.
For retrieving messages I would recommend a separate windows service which would periodically read messages and process them. An good example on how to do that is given here: Windows service with timer
Update: Windows Service Example:
MSMQConsumerService.cs
public partial class MSMQConsumerService : ServiceBase
{
private System.Timers.Timer timer;
public MSMQConsumerService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.timer = new System.Timers.Timer(30000D); // 30000 milliseconds = 30 seconds
this.timer.AutoReset = true;
this.timer.Elapsed += new System.Timers.ElapsedEventHandler(this.ProcessQueueMessages);
this.timer.Start();
}
protected override void OnStop()
{
this.timer.Stop();
this.timer = null;
}
private void ProcessQueueMessages(object sender, System.Timers.ElapsedEventArgs e)
{
MessageProcessor.StartProcessing();
}
}
and the MessageProcessor.cs
public class MessageProcessor
{
public static void StartProcessing()
{
List<string> messages = ExceptionMSMQ.RetrieveMessages();
foreach(string message in messages)
{
//write message in database
}
}
}
I don't know if my title is correct. But here's what I want to know.
I have a Download class that returns certain events and has a couple of methods. Each instance of Download class can download a single file. And all those events and methods are related to the file being downloaded.
As it's a multi file downloader, multiple instantiations are required when more than a single file needs to be downloaded.
Each download has a download id, but that is not supplied to the Download class, to keep it independent from the other classes.
Now getting all the info from each instance of the file download and being able to control a single download, is the problem. How do I know which download is which?
Any solutions? Or design patterns you could recommend? I've hit a roadblock.
Download class:
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
namespace Mackerel_Download_Manager
{
public class Download
{
public event EventHandler<DownloadStatusChangedEventArgs> ResumablityChanged;
public event EventHandler<DownloadProgressChangedEventArgs> ProgressChanged;
public event EventHandler Completed;
public bool stop = true; // by default stop is true
public bool paused = false;
SemaphoreSlim pauseLock = new SemaphoreSlim(1);
string filename;
public void DownloadFile(Uri DownloadLink, string Path)
{
filename = System.IO.Path.GetFileName(Path);
stop = false; // always set this bool to false, everytime this method is called
var fileInfo = new FileInfo(Path);
long existingLength = 0;
if (fileInfo.Exists)
existingLength = fileInfo.Length;
var request = (HttpWebRequest)HttpWebRequest.Create(DownloadLink);
request.Proxy = null;
request.AddRange(existingLength);
try
{
using (var response = (HttpWebResponse)request.GetResponse())
{
long fileSize = existingLength + response.ContentLength; //response.ContentLength gives me the size that is remaining to be downloaded
bool downloadResumable; // need it for not sending any progress
if ((int)response.StatusCode == 206) //same as: response.StatusCode == HttpStatusCode.PartialContent
{
//Console.WriteLine("Resumable");
downloadResumable = true;
}
else // sometimes a server that supports partial content will lose its ability to send partial content(weird behavior) and thus the download will lose its resumability
{
if (existingLength > 0)
{
if (ResumeUnsupportedWarning() == false) // warn and ask for confirmation to continue if the half downloaded file is unresumable
{
return;
}
}
existingLength = 0;
downloadResumable = false;
}
OnResumabilityChanged(new DownloadStatusChangedEventArgs(downloadResumable));
using (var saveFileStream = fileInfo.Open(downloadResumable ? FileMode.Append : FileMode.Create, FileAccess.Write))
using (var stream = response.GetResponseStream())
{
byte[] downBuffer = new byte[4096];
int byteSize = 0;
long totalReceived = byteSize + existingLength;
var sw = Stopwatch.StartNew();
while (!stop && (byteSize = stream.Read(downBuffer, 0, downBuffer.Length)) > 0)
{
saveFileStream.Write(downBuffer, 0, byteSize);
totalReceived += byteSize;
float currentSpeed = totalReceived / (float)sw.Elapsed.TotalSeconds;
OnProgressChanged(new DownloadProgressChangedEventArgs(totalReceived, fileSize, (long)currentSpeed));
pauseLock.Wait();
pauseLock.Release();
}
sw.Stop();
}
}
if (!stop) OnCompleted(EventArgs.Empty);
}
catch (WebException e)
{
System.Windows.MessageBox.Show(e.Message, filename);
}
}
public void pause()
{
if (!paused)
{
paused = true;
// Note this cannot block for more than a moment
// since the download thread doesn't keep the lock held
pauseLock.Wait();
}
}
public void unpause()
{
if (paused)
{
paused = false;
pauseLock.Release();
}
}
public void StopDownload()
{
stop = true;
this.unpause(); // stop waiting on lock if needed
}
public bool ResumeUnsupportedWarning()
{
var messageBoxResult = System.Windows.MessageBox.Show("When trying to resume the download , Mackerel got a response from the server that it doesn't support resuming the download. It's possible that it's a temporary error of the server, and you will be able to resume the file at a later time, but at this time Mackerel can download this file from the beginning.\n\nDo you want to download this file from the beginning?", filename, System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == System.Windows.MessageBoxResult.Yes)
{
return true;
}
else
{
return false;
}
}
protected virtual void OnResumabilityChanged(DownloadStatusChangedEventArgs e)
{
var handler = ResumablityChanged;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnProgressChanged(DownloadProgressChangedEventArgs e)
{
var handler = ProgressChanged;
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnCompleted(EventArgs e)
{
var handler = Completed;
if (handler != null)
{
handler(this, e);
}
}
}
public class DownloadStatusChangedEventArgs : EventArgs
{
public DownloadStatusChangedEventArgs(bool canResume)
{
ResumeSupported = canResume;
}
public bool ResumeSupported { get; private set; }
}
public class DownloadProgressChangedEventArgs : EventArgs
{
public DownloadProgressChangedEventArgs(long totalReceived, long fileSize, long currentSpeed)
{
BytesReceived = totalReceived;
TotalBytesToReceive = fileSize;
CurrentSpeed = currentSpeed;
}
public long BytesReceived { get; private set; }
public long TotalBytesToReceive { get; private set; }
public float ProgressPercentage
{
get
{
return ((float)BytesReceived / (float)TotalBytesToReceive) * 100;
}
}
public float CurrentSpeed { get; private set; } // in bytes
public TimeSpan TimeLeft
{
get
{
var bytesRemainingtoBeReceived = TotalBytesToReceive - BytesReceived;
return TimeSpan.FromSeconds(bytesRemainingtoBeReceived / CurrentSpeed);
}
}
}
}
Download class is instantiated inside a Mackerel class, that starts the download for the given downloads.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
namespace Mackerel_Download_Manager
{
public static class Mackerel
{
//Main Menu functions
public static void ResumeDownload(string[] DownloadIDs)
{
foreach (var DownloadID in DownloadIDs)
{
var itemToResume = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
if (itemToResume.Running == false)
{
itemToResume.Running = true;
var download = new Download();
download.DownloadFile(itemToResume.DownloadLink, itemToResume.SaveTo);
var window = new Dialogs.DownloadProgress(itemToResume);
window.Show();
double progress = 0;
itemToResume.Status = string.Format("{0:0.00}%", progress);
Downloads.DownloadEntries.CollectionChanged += delegate
{
if (!itemToResume.Running) window.Close();
};
}
}
}
public static void StopDownload(string[] DownloadIDs)
{
foreach (var DownloadID in DownloadIDs)
{
var itemToStop = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
if (itemToStop.Running == true)
itemToStop.Running = false;
}
}
public static void StopAllDownloads()
{
foreach (var itemToStop in Downloads.DownloadEntries.Where(download => download.Running == true))
itemToStop.Running = false;
}
public static void RemoveDownload(string[] DownloadIDs) // this method is able to delete multiple downloads
{
foreach (var DownloadID in DownloadIDs)
{
// delete from the download list
var selectedDownload = Downloads.DownloadEntries.Where(download => download.DownloadID == DownloadID).FirstOrDefault();
var selectedDownloadIndex = Downloads.DownloadEntries.IndexOf(selectedDownload);
Downloads.DownloadEntries.RemoveAt(selectedDownloadIndex);
//delete from the harddrive
if (File.Exists(selectedDownload.SaveTo))
File.Delete(selectedDownload.SaveTo);
}
Downloads.Serialize(); // save current state of object
}
public static void RemoveCompletedDownloads() // this method just removes all completed downloads from Mackerel's download list (it doesn't delete them from the hard drive)
{
foreach (var itemToRemove in Downloads.DownloadEntries.Where(download => download.Status == "Complete").ToList())
{
Downloads.DownloadEntries.Remove(itemToRemove);
}
}
// Context Menu
public static void OpenDownloadProperties(string DownloadID) // Open "Download Properties" for the given download ID
{
var DownloadProperties = new Dialogs.Context_Menu.DownloadProperties(DownloadID);
DownloadProperties.Owner = Application.Current.MainWindow; // so that this dialog centers to its parent window, as its window is set to WindowStartupLocation="CenterOwner"
DownloadProperties.ShowDialog();
}
}
}
Full source code is here: https://github.com/Expenzor/mackerel-download-manager
Sounds like your making the download itself an object but are using "downloader" as the name instead. I would maybe suggest an array of downloaded objects or similar. The downloader class can have a method which creates a new object - perhaps a download object. Using an array or linked list etc will give you the opportunity to address the object and call them independently - know which is which.
Posting your code would help as well.