I want to cleanup and refactor this library, discussions are OPEN!

Intro
Here are things that I'd like to cleanup going forward. Since this has ramifications on all existing projects using FluentFTP, and since these will be major breaking changes to the API, I'd like community feedback on these changes so that they are not hasty, disruptive, or out-of-touch with the actual userbase.
I would like these to be part of the v40 release so its easy for existing users to understand that v39 is the "legacy API" and that v40+ will use the "newer API". I have never undertaken such a large systematic refactoring before.
Topic 1

Problem: We have a lot of old/weird/unused functionality that is probably broken or not well tested or not being used.

EnableThreadSafeDataConnections - Crazy feature which spawns new FTP connections for each file transferred
QuickTransferLimit - Implemented but disabled
FtpClient.Connect(Uri) - Static constructors
FtpClient.GetPublicIP() - Static methods
DereferenceLink - does anyone even use this?
200 different constructors for FtpClient - I would just like 1 or 2 constructors for common patterns.

Solution: Drop these features entirely.

Topic 2

Problem: We have a massive set of properties directly within the FtpClient class which clutters up its API.
Solution: Store all configuration setting properties into child classes whose references are stored within the FtpClient. This groups up the properties into logical categories and also eliminates them from the FtpClient API.

client.Connect() becomes client.Connect(FtpProfile)
client.FXPProgressInterval becomes client.FXP.ProgressInterval
client.DownloadZeroByteFiles becomes client.Download.ZeroByteFiles
client.TransferChunkSize becomes client.Download.ChunkSize and client.Upload.ChunkSize

Topic 3

Problem: Most FTP servers don't support directly downloading/uploading from an absolute path (eg: /root/animals/duck.txt, therefore we should always set the working directory to the folder (eg: /root/animals/) and then download the file (duck.txt). And we should not allow the user to manually modify the working directory because it would conflict with this system.
Solution: Drop support for GetWorkingDirectory and SetWorkingDirectory and instead always automatically set the working directory before file transfers or even getting the file size, etc. This would even fix issues where there are spaces in directory paths, and many other things. It would put us in parity with FileZilla.

Topic 4

Problem: Our logging is a mess. We should be able to integrate with NLog and other standard .NET loggers.
Solution: Drop support for FtpTrace and its outdated API and instead support a client.Logger property which can be set to our default logger or an NLog instance, etc.

Topic 5

Problem: Poor implementation of timezone conversion. Fixed implementation that does not allow users to customize it.
Solution: Implement a strategy pattern so users can select from:

NoTimeConversion
TimeZoneConversion - a new method that allows for DST to be observed (missing feature)
TimeOffsetConversion - users specify which is their PC timezone and the server timezone and we do simple math to convert it (current method)

Topic 6

Problem: We have 2 implementations of each function, a synchronous version and an async version. This causes bloat, mistakes/mismatches in the implementation, and weird behavior.
Solution: Drop support for synchronous API. Drop support for <.NET 3.5 as it doesn't support async.

Topic 7

Problem: Too much bloat with older IAsyncResult style async functions.
Solution: Remove it. Anyone who wants async can just use async/await.

Lets talk!
Please start the discussions below! Add your comments and mention which topic you are talking about. You can comment on any topic(s) that you like.

@FanDjango @tommysor @ilc-ilopez @fire-lizard @daviddenis-stx @hez2010 @n0ix @aliquid @wakabayashik @Mortens4444 @radiy @WolfspiritM @Lukazoid @iamjay @peterfortuin @taoyouh @artiomchi @sierrodc @jblacker @L3Z4 @jnyrup @Adhara3 @brucemcmillan

Hi,
Foreword
as I already suggested in other issues, I would try, whenever possible, to avoid behaviour by enums, favoring a strategy pattern instead. This would have the benefit to help (not solve) Topic 2 for example, moving specific configuration only where necessary.  To be clear I would favour something like new XxxFtpClient() which is an FTPClient of type Xxx ad internally it is built by injecting specific strategies for that server type. It's not easy but I see a potentially big architectural advantage.
Topic 3
This is the one I am mostly involved in. I agree that not allowing browsing the tree would help and that would also be strategy oriented. User provides the full path, it's up to the implementation use it as it is or navigate to the folder and then download
Topic 4
Having a simple IFtpClientLogger interface is a good way to go, you just need 5 or 6 logging methods and the user can easily plug in any implementation. Do not add any specific dependency.
Topic 6
I am not a huge fan of async code, to put it mild. But I get this is a feature largely used nowadays, the only thing I could say is that at the moment I'm only using sync methods from FluentFTP.
Hope this helps
A

Topic 3/4/5: Agreed (more or less)
Forward;

as I already suggested in other issues, I would try, whenever possible, to avoid behaviour by enums, favoring a strategy pattern instead. This would have the benefit to help (not solve) Topic 2 for example, moving specific configuration only where necessary. To be clear I would favour something like new XxxFtpClient() which is an FTPClient of type Xxx ad internally it is built by injecting specific strategies for that server type. It's not easy but I see a potentially big architectural advantage.

Ok, please suggest the implementation. For example how would functionality be divided into strategies. For something like downloading a file, we don't really have strategies. Unless we should? Like SimpleFileDownload and ChunkedFileDownload etc. And what should the API look from the client? client.Downloader = new SimpleFileDownload()? Please suggest the concrete implementation so I can get a better idea.

I'm not expert on FluentFTP client internals, so I'm still talking in a very theoretical way, based mainly on the Find What Is Varying and Encapsulate It basic principle.
So I would ideally try to have something like the following (pseudocode):
public class FTPClient
{
  public FTPClient(IDownloadStrategy downloadStrategy, IUploadStrategy uploadStrategy, IAuthenticationStrategy authStrategy)
  {
    // Of course, as many strategies as required
  }
}

public static class FritzBoxFtpClient
{
  public static FTPClient Create() // Here custom properties for this kind of client, should help with cluttered config
  {
    return new FTPClient(); // Inject strategies for this specific client
  }
}

I do expect having, for each strategy type, a number N of implementations which is less than the actual number of clients, i.e. I guess a BasicDownloadStrategy may be reused for multiple clients.
I used the static factory method to avoid inheritance and because that would force to put all different behaviours into strategies and not in method overloading (mixing the two would kill code readability).
The other advantage is that this pattern would allow native extendibility. E.g. a user has a new client, maybe a custom one. He can create his own strategies  and inject into FTPClient constructor and he should have something working. Basically, you would end up having some core classes + a bunch of add-ons (each client)

Just coming from my insular view for the IBM parts of this:
I am worried about:
Topic 3
Anyone writing a FTP Client GUI and wanting to give the user a way to walk the server-OS's file system will want to have these two functions, right? And the IBM parts really heavily use PWD/CWD.
Topic 6

I am not a huge fan of async code, to put it mild. But I get this is a feature largely used nowadays, the only thing I could say is that at the moment I'm only using sync methods from FluentFTP

Same for me.
I find it strange that C# religion goes for "not duplicating code" but makes it so difficult to have the same basic algorithm present in both sync and async modes. Certainly in an C(++) environment I could envision a single code set (using the preprocessor) that represents both flavors in a single source. How to do in C#?

Anyone writing a FTP Client GUI and wanting to give the user a way to walk the server-OS's file system will want to have these two functions, right? And the IBM parts really heavily use PWD/CWD.

Maybe we can have a strategy for this?

ManualDirectoryStrategy - the current get/set PWD/CWD
AutomaticDirectoryStrategy - a new one which can auto set the dir based on the file commands

Names are just suggestions, you can suggest better ones. I also don't like having the word "Strategy" as a suffix, is there a better naming scheme? <Variant> <Group> "Strategy" seems like a very Java-esque too-many-words naming scheme and I would like a better one!

Topic 6: I am not a huge fan of async code,

Same for me, however lots of contributors were excited to contribute async versions for some reason... And it makes the library look more "modern".

This may be silly or very naif.
What about adding sync stuff as ExtensionMethods? That would allow keeping the class basically sync, users would be happy as they can call the method directly on the object.
A

This may be silly or very naif. What about adding sync stuff as ExtensionMethods? That would allow keeping the class basically sync, users would be happy as they can call the method directly on the object.

No that would not solve the main problem of duplicating code.

I would like to see

Drop net3.5 support and keep only async variant of APIs, for those who want to use blocking sync call, they can always do FooAsync().ConfigureAwait(false).GetAwaiter().GetResult().
Bringing IAsyncEnumerable support again to support "list streaming", without the dependency of Rx/Lx.Net.

Bringing IAsyncEnumerable support again to support "list streaming", without the dependency of Rx/Lx.Net.

I'm not against it but it caused #703 and so it was reverted. If you have a solution for it without causing that issue, you can please do it and file a PR.

No, that would not solve the main problem of duplicating code.

Ok but then this is why

I don't know why users can't just create a background worker/thread and use the sync API,

If they could, we also could in an extension method, right?

I don't like providing main library API as extension methods. Extensions are meant when the third parties want to extend something without inheriting it.

Bringing IAsyncEnumerable support again to support "list streaming", without the dependency of Rx/Lx.Net.

I'm not against it but it caused #703 and so it was reverted. If you have a solution for it without causing that issue, you can please do it and file a PR.

Hi,
According to #703, the issue was 'only' the reference to System.Linq.Async.
Please take a look here: https://github.com/RobertoSarati/FluentFTP_asyncenum. (fork reset to release 33.1.6).
As far as I can see the package System.Linq.Async is referenced only if ASYNCPLUS is defined, that is netstandard 2.0/2.1 and net5.0.
I've removed the package reference and without any other change I was able to compile the project. I've created a .net6 console app referencing the project and I'm able to use GetListingAsyncEnumerable without any issues. Maybe I'm missing the root cause (ps: I've removed net4, net45, net50 from csproj because I don't have them installed on my machine).
Btw, do you still want to support net3.5, net4, net45? According to microsoft .net framework < 4.6.2 are no more supported. So, what if target framework list is just [netstandard2.0;netstandard2.1;net50+]?

Can I at least delete the older IAsyncResult style async functions from the library to remove bloat? Even I have never used them personally. Plus anyone who wants async can just use async/await.

Can I at least delete the older IAsyncResult style async functions from the library to remove bloat? Even I have never used them personally. Plus anyone who wants async can just use async/await.

Imho the "sync/async" programming style should be as close as possible to what is today available in HttpClient so, for sure, no IAsyncResult. About sync method... I'm fine to have only aysnc/await (I'll use only these ones and I dont' want many methods in the intellisense dropdown), but if the community wants to keep sync style, I've no objections. But for me, we can switch to implement only async/await methods.
Later I'll check if  GetListingAsyncEnumerable works in current master branch.

What I've planned for v40:
To delete

EnableThreadSafeDataConnections
QuickTransferLimit
FtpClient.Connect(Uri)
FtpClient.GetPublicIP()
DereferenceLink
IAsyncResult functions (async implementation for .NET 2 and older)
FtpTrace

To refactor:

client.Logger (strategy pattern)

client.TimeConversion (strategy pattern)

NoTimeConversion
TimeZoneConversion
TimeOffsetConversion

Split FtpClient into 2 clients  - FtpClient and FtpClientAsync

Solves the issue of this messy API:

Do you guys think this is a decent solution?

At present I don't see any advantage to the user for splitting the download/upload/FXP parts, because even if I did refactor the "downloader" to use strategy pattern, I don't have any alternate strategies to offer the user.

1;3;4;7;8: ok
2;5;6: never used => ok

About logging: Such as HttpClient, why not just implement an EventSource? I think all libraries implement proper sinks (nlog, serilog ...). Really simple explanation here (it is the same implementation used by HttpClient and other .net libraries).
About TimeZone conversion: In my opinion the library should not handle conversions at all. There are other packages focused on this functionality. BUT if there are ftp servers that specify the timezone I think your solution is fine.
About AsyncClient and SyncClient: Fine for me.

First of all thanks for raising this discussion.
Topic 4: Logging
Building on top of the suggestion by @Adhara3 here's a rough prototype of how the logging could be structured
Define an interface in FluentFtp to separate the functionalities of FtpTrace.
interface IFluentFtpLogger
{
    void Log(FtpTraceLevel eventType, object message);
}

class NoOpLogger : IFluentFtpLogger
{
    public void Log(FtpTraceLevel eventType, object message)
    {
        // The default logger that does nothing.
    }
}

class ConsoleLogger : IFluentFtpLogger
{
    public void Log(FtpTraceLevel eventType, object message)
    {
        // Write to Console
        throw new NotImplementedException();
    }
}

class FileLogger : IFluentFtpLogger
{
    public void Log(FtpTraceLevel eventType, object message)
    {
        // Write to file
        throw new NotImplementedException();
    }
}

/// <summary>
/// Allows combining multiple loggers
/// </summary>
class CompositeLogger : IFluentFtpLogger
{
    private readonly IFluentFtpLogger[] m_loggers;

    public CompositeLogger(params IFluentFtpLogger[] loggers)
    {
        m_loggers = loggers;
    }

    public void Log(FtpTraceLevel eventType, object message)
    {
        foreach (var logger in m_loggers)
        {
            logger.Log(eventType, message);
        }
    }
}

To allow hooking into the widely used ILogger<T> interface from Microsoft.Extensions.Logging.Abstractions
we publish a separate package FluentFtp.Logging that simply contains an adapter to bridge between ILogger<T> and IFluentFtpLogger.
This avoids forcing a dependency on Microsoft.Extensions.Logging.Abstractions, but still makes it easy to integrate with popular logging libraries like Serilog, NLog, etc.
class LoggerAdapter : IFluentFtpLogger
{
    private readonly ILogger<IFtpClient> logger;

    public void Log(FtpTraceLevel eventType, object message)
    {
        var loglevel = ConvertLogLevel(eventType);
        if (logger.IsEnabled(loglevel))
        {
            logger.Log(loglevel, message.ToString());
        }
    }

    private static LogLevel ConvertLogLevel(FtpTraceLevel eventType)
    {
        return eventType switch
        {
            FtpTraceLevel.Verbose => LogLevel.Trace,
            FtpTraceLevel.Info => LogLevel.Information,
            FtpTraceLevel.Warn => LogLevel.Warning,
            FtpTraceLevel.Error => LogLevel.Error,
            _ => LogLevel.Information // Fallback
        };
    }
}

Topic 6: Target frameworks
FluentFtp currently supports frameworks that have been EOL for years.
Retaining that support seems to add significant complexity (currently 492 #ifs).

net20, net40 and net45 are no longer supported.
net35 SP1 is technically still supported until 2029, but I would recommend for FluentFtp to drop support for it.
.net 5 is no longer supported.

I we want to support older but still supported frameworks, we could go with net50;netstandard2.1;netstandard2.0;net472;net462.
https://docs.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting
https://github.com/dotnet/docs/issues/8896
To see what TFM of FluentFtp an application will use, use Nuget Tools
Topic 6: Async
async is important in modern C# to avoid thread starvation, so I highly recommend not to drop that part.
It cannot always be delegated (nicely) to a background thread, e.g. a web api that as part of its work downloads from an FTP server.
In regards to whether we should also keep the sync APIs, other clients have mostly dropped sync versions.

HttpClient is mostly async - some sync APIs were added in .NET 6, but mainly for backwards compatibility or legacy code and because httpClient.Send() could be implemented better than httpClient.SendAsync().Wait().
AWS S3 client has transitioned to async only.

Topic 7: Removing IAsyncResult
🔥🔥🔥
Concurrency
One thing that surprised me when using FtpClient compared to e.g. HttpClient was that is inherently not suited for concurrent actions as it carries state, e.g. LastReply.
I haven't thought this through, but maybe something like this would di
var client = new FtpClient();

... // setup the ftp client

using FtpConnection connection = client.CreateConnection();
byte[] content = await connect.DownloadAsync();

I'm aware that this would be yet another major restructuring of the project, so this is mostly a shower thought for now.

@jnyrup Thanks for the long and detailed answer!
Yes concurrency is not currently supported.
I dislike "too many nuget packages" so would it cause any harm to add a dependency on Microsoft.Extensions.Logging.Abstractions ?

I would probably drop the FtpTraceLevel way to go, I was thinking more to something like this
public interface IFtpLogger
{
  void Trace(string message); // I personally see no advantages in passing an object
  void Debug(string message);
  void Info(string message);
  void Warning(string message);
  void Error(string message);
  void Error(string message, Exception exception);
}

I would also probably provide only the composite and the NoOpLogger.
I also agree that if a dependency should be added, it should be added in a separate, dedicated package.
@robinrodricks

At present I don't see any advantage to the user for splitting the download/upload/FXP parts, because even if I did refactor the "downloader" to use strategy pattern, I don't have any alternate strategies to offer the user.

It would just force the user to change from: client.DownloadFile to client.Downloader.DownloadFile so that does not offer any major advantage at present.
I would not insist any further on this, I just wanted to clarify that:

Strategy may not be the right term, but the idea is to hide different behaviours behind small classes to improve testability, favour aggregation and modularity and have a cleaner code.  The idea is that there should be a "strategy" every time in current code you do if(serverType == xxx)
It is an implementation detail, so users are not involved directly, FTPClient would act as a proxy for the strategies. Again, this would just be a more object oriented way of building the software.

Concurrency
This is interesting. As a user, the async interface would give me the natural feeling that the client is concurrent, so be careful here. As suggested by @jnyrup  is a user expect to run the client as the HttpClient object, then it may be worth thinking a way to overcome this, maybe providing a FtpClientPool? Just as food for thoughts
A

Topic 4: Logging

I dislike "too many nuget packages" so would it cause any harm to add a direct dependency on Microsoft.Extensions.Logging.Abstractions ?

In terms of functionality of FluentFtp taking a direct dependency on M.E.L.A should "just work", but in general there are more considerations to take into account when adding extra dependencies.
When I'm about to add an additional nuget package, I have to evalute all transitive package references, in order to get a full view on licenses, vulnerabilities, maintenance status, etc. to avoid creating technical debt/security issues from day one.
For M.E.L.A this should not be a problem.
Increased size of your application as extra unused dlls are included.
For M.E.L.A it should be about 62K if FluentFtp is the only nuget referencing it.
Note, the earliest non-deprecated of this nuget package targets netstandard20 which is net461 or later.
So taking a direct dependency on Microsoft.Extensions.Logging.Abstractions prevents FluentFtp from targeting net35/net40/net45 (a non-issue to me since I would drop those targets).
So I think it mostly comes down to ones view on providing a single package with all batteries included or a more modular approach, where consumers don't have to depend on something they don't use.

And then do as you proposed above. (NoOpLogger, FileLogger...) Maybe one change would be instead of having a single instance of logger, we could have a list of loggers directly in the client for simplicity? Making CompositeLogger not required.
API would be: client.Loggers = new List<ILogger{....} or client.AddLogger(myLogger)

Established logging frameworks already provide knobs to combine multiple loggers (sinks) into a single ILogger, so the CompositeLogger should only be necessary when using basic included loggers FileLogger and ConsoleLogger.
We could even go in the more extremen direction and say implementations of how to log is not a responsibility of FluentFtp and not provide any logger, but only an interface and the NoOpLogger().
As an endorsement, the general "best-practice" is to use constructor-based dependency injection, but since a NoOpLogger can serve as a reasonable default property-based injection seems justified for FluentFtp.
So I would probably go with public XyzLoggingInterface Logger { get; set;} = new NoOpLogger();.
Two benefits of using the methods exposed on ILogger are the support for:

structured logging.
IsEnabled, which may make it cheaper when logging is disabled (e.g. having different loglevels between dev and prod).

Topic 6: Target frameworks

Proposed frameworks:
net60;net50;net45;net40;net35;netstandard2.0;netstandard2.1

I shouldn't be necessary to have both net50 and net60, unless you want to use net60 specific features or import different nuget packages based on the framework, as a net60 applications can consume a net50 nuget.
Adding unnecessarily targets to a nuget package just increases the binary size of the nuget package.
Out of curiosity, why do you intend to keep support for net35, net40 and net45?
Topic 6: Async

I am planning on splitting FtpClient into 2 clients - FtpClient and AsyncFtpClient, do you agree with this? Both will inherit from BaseFtpClient which will have some reused properties/functions.

I wouldn't do that.
It e.g. prevents reusing the same client for sync and async invocation paths.
I'm curious what the intended goal for this is.
If the intention is to reduce the number of available methods listed with intelliSense, you could consider creating two interfaces.
public interface IAsyncFtpClient
{
    public Task<byte> DownloadAsync();
}

public interface ISyncFtpClient
{
    public byte Download();
}

public class FtpClient : IAsyncFtpClient, ISyncFtpClient
{}

Strategies

The idea is that there should be a "strategy" every time in current code you do if(serverType == xxx)

I agree here that ideally, the FtpClient should not be aware of which server it is connected to, but just that it is entering server dependent behavior and hence delegate the implementation to a dedicated handler.

Topic 4: Logging
Agreed on MELA.
Topic 6: Target frameworks

Out of curiosity, why do you intend to keep support for net35, net40 and net45?

Well just to support a broader user base. I was aware of enterprises that find it harder to upgrade to newer .NET versions. What is your take on this?  If we are to support MELA then we would be forced to drop net 3.5/4/4.5 which is ok with me provided its ok with the user base.
I specifically wanted net5/6 so that users with those 2 frameworks can use FluentFTP without forcing you to upgrade to THE latest version.
Topic 6: Async

I wouldn't do that. It e.g. prevents reusing the same client for sync and async invocation paths.

Would anyone do that? A lot of the users on this thread mentioned they use either sync or async but not both. I can't imagine how you would use both, considering that FluentFTP does not allow concurrent operations anyway.

I'm curious what the intended goal for this is.

To clean up and reduce the API surface of each client class. I prefer less interfaces.

Strategies
I agree here that ideally, the FtpClient should not be aware of which server it is connected to, but just that it is entering server dependent behavior and hence delegate the implementation to a dedicated handler.

We already have implemented a server-specific strategy system which implements this as far as possible. Some zOS specific code is still remaining in the core which should be refactored out as far as possible.

Topic 6: Target frameworks

I was aware of enterprises that find it harder to upgrade to newer .NET versions. What is your take on this?

MS ended support for .net2.0 over 10 years ago and for net40 over 5 years ago.
net462 was introduced 5 years ago, so I would no longer regard it as a "newer .NET versions".
I admire your concern about not only supporting the latest and greatest (this a quite debated topic among OSS maintainers), but at one point most OSS projects drop some support in order to move forward and keep the project fun and maintainable.
So there no "right" thing here, e.g. Newtonsoft.Json still supports .net20.
Personally I would say that those "enterprises" can either use the current version of FluentFTP or have to upgrade their application to at least net462 to get the latest and greatest of FluentFTP.

I specifically wanted net5/6 so that users with those 2 frameworks can use FluentFTP without forcing you to upgrade to THE latest version. For example some businesses don't want to keep re-purchasing the latest VS version.

.NET5 is targeted in FluentFTP to add support for TLS 1.3.
But net6 applications can also consume that.
Adding a net6 target only increases the binary size of the FluentFTP nuget package as it includes a fluentFtp.dll for both net5 and net6.
Similarly, you don't have to add a net7 target when it is released in November, they can also consume a .net5 package.
Topic 6: Async

In fact I specifically don't like this method of mixing sync and async code as it may not be well tested usage.

very nit picking, but splitting the code into two classes does not make it more tested.

Topic 6: Async

very nit picking, but splitting the code into two classes does not make it more tested.

As in, in our tests we only test async methods and sync methods separately. Combining them in the same FtpClient might produce weird/undefined behavior.

It doesn't sound unreasonable to me, but I can't recall having seen that design choice elsewhere.

Its more of a thing to clean up the API and codebase, rather than for testing actually.
And to go in the direction where we have 2 clients. Maybe sometime say 5 years from now, "no one" uses FtpClient then we can outright delete that codebase and have just the dedicated AsyncFtpClient.

Topic 6: Target frameworks
Thanks for your long justification and explanation. As you said, its such a debatable thing. I guess because of MELA and other reasons, I'm ok dropping support for older .NET frameworks. I hope no companies cry because we no longer support their [outdated] .NET version.
So are you proposing this?
net60;net50;netstandard2.0;netstandard2.1

From how I read the code you could probably target as few frameworks as net50;netstandard2.0;netstandard2.1.
This code snippet currently distinguishes between .net frameworks and others, so if we need to keep that distinction we also need to have net462 or net472.
https://github.com/robinrodricks/FluentFTP/blob/51e1d85b220ce8d16c10b947778ebd32b4c63a26/FluentFTP/Client/FtpClient_Stream.cs#L1174-L1186

Hmmpf. One of my customers (Fortune 500) is still on 4.7.2

Can we support the same frameworks MELA supports?
net50;netstandard2.0;net461

Hmmpf. One of my customers (Fortune 500) is still on 4.7.2

net472 can consume netstandard2.0 nuget packages.
https://nugettools.azurewebsites.net/6.0.0/get-nearest-framework?project=net472&package=net50
netstandard2.0
netstandard2.1

https://docs.microsoft.com/en-us/dotnet/standard/library-guidance/cross-platform-targeting#multi-targeting

For increased compatibility with supported frameworks we could go net50;netstandard2.0;netstandard2.1;net472;net462. https://github.com/dotnet/docs/issues/8896

Can we support the same frameworks MELA supports?

All targets we pick should be able to consume MELA 2.1.0.

I'm good with this : net50;netstandard2.0;netstandard2.1;net472;net462
I'm good with MELA 2.1.0, it is non-restrictive on framework choice.
Are we all in agreement with this choice?

Hmmpf. One of my customers (Fortune 500) is still on 4.7.2

net472 can consume netstandard2.0 nuget packages.

Yes, thanks. But I wanted to say: netstandard2.0 is kind of bottom line still

Are we all in agreement with this choice? @FanDjango ?

Agreement from me?
I have no right to an opinion on this for total lack of knowledge/experience on/of this topic.
I have just checked: One of my clients actually needs application binaries that utilize .NET Framework 4.7 (not 4.7.2 as earlier stated) on systems in protected environments where updates cannot so easily be done (and won't be done). I am currently linking in the FluentFTP binaries from net45 into my application, which targets net47.
Current functionality suffices for me, I can use a frozen version status today, that is always a choice.
If you have advice for me on how to proceed beyond that, I will happily follow that.

@FanDjango  would this help? net50;netstandard2.0;netstandard2.1;net472;net462;net45

A net47 application will consume the net462 target.
https://nugettools.azurewebsites.net/6.0.0/get-nearest-framework?project=net47&package=net50
netstandard2.0
netstandard2.1
net472
net462
net45

We already have implemented a server-specific strategy system which implements this as far as possible. Some zOS specific code is still remaining in the core which should be refactored out as far as possible.

That was a very welcome first step. Beware that the base class still exposes the server type enum, this could allow having server depending code all around the code. The first question here is: what breaks if you remove the ServerType property?

That enum is the only way for users to detect the FTP server as the
handlers are internal and not accessible to library users.

Some zOS specific code is still remaining in the core

Yes, I am sorry for causing this. I believe though that at the time it was the expedient way to get the needed functionality "in there" and confirming that the approach simply works for z/OS (which can be a PITA for FTP). Now would be the time for refactoring it "out of there", I certainly agree.
@robinrodricks You are definitely the designated best for doing that, of course, but you can be assured I will be available for testing the changes on a live server.

That enum is the only way for users to detect the FTP server

I for one need this enum

I for one need this enum

I'm curious: why? I mean, on one side the lib tries to hide the server differences, on the other side it must expose the kind. I am just curious.

I understand. Hmmm. I am giving my user a GUI to browse/search the filesystem in order to find the files he wants to process. If it is a z/OS server, the semantics for file names differ in some cases (partitioned data sets), the server capabilities are (greatly) expanded, and additional functions exist (like adressing the Job Entry Subsystem). All of these I want to be "greyed out" or not presented to the user when he is NOT connected to a z/OS FTP Server.
If connected to an OS/400 type of FTP Server, there also subtle things to do differently which you might not do on FTP Servers sitting on a standard unix file system.
I dunno if there are other examples out there, but for me it looks like a "special" IBM thing (such as it always is on proprietary systems). Although: one other such system used to be TANDEM, always special things to do on that server.
So, I don't really believe in "hiding" the server difference to the degree that the library users doesn't even need to know the server type.
If this is not possible, I will just issue a SYST command to find out for myself, but since the connect logic already does that internally, why not make the results available (in some form, does not need to be an enum).

@FanDjango  it's fine, I just needed some context. Thanks

I'm curious: why? I mean, on one side the lib tries to hide the server differences, on the other side it must expose the kind. I am just curious.

The idea was to give the user maximum power. If we have some info, we allow them to read that info and do whatever they want with it. Perhaps they are building a generic FTP client and can display the "currently connected server type" to the user. Or for logging, or for error handling. So we don't try to hide anything from users. However, that being said we DO try to hide our own internal classes which they should not need to access.

It makes perfect sense. The important thing, to me, is that the enum is not used internally for stuff like if(serverType == xxx). These are my 2 cents.
Cheers
A

the enum is not used internally for stuff like if(serverType == xxx)

The only ones I see currently are: IBMzOSFTP and OpenVMS.
I am working on the IBM ones currently, moving them all into ServerHandler. constructs in one form or the other.
In some cases, it is not so totally clear cut how precise you want to be with the knife - the current if... constructs more or less reflect the easy way to get things working but not the best organisation of the code if you want to refactor the servertype out to an alternative function/bool or whatever.
If the highest priority is to simply get these if (serverType == xxxx) out. then fine, as a first step that can be done. But somehow I feel that perhaps one would like to replace complete functions instead of small code snippets.
Here is an example: API Function FileExists. Four separate serverHandlers inside the function or simply a big custom FileExists for z/OS, which then duplicates a lot of the original code - with the nightmare of maintaining changes to those duplications. Why so much duplication in a custom function for z/OS: Because, if you are in the unix realm, you could just switch back to the default function.
Thus, I am thinking that perhaps in addition to serverHandler.IsCustomFunction(...) and serverHandler.CustomFunction(....), we would need a third construct that checks if a fallback to the original Function is needed.
I suggest therefore that in addition to being able to replace entire API functions or to influence small parts of them internally with booleans, we need to conditionally replace entire API functions.
Working on it...

the enum is not used internally for stuff like if(serverType == xxx)

I have eliminated them ALL (PR #928). Now go ahead with the original plan/discussion

Maybe we can have a strategy for this?

ManualDirectoryStrategy - the current get/set PWD/CWD
AutomaticDirectoryStrategy - a new one which can auto set the dir based on the file commands

Maybe a "Freedom strategy, for those who want to navigate, to use the listing APIs etc.
And a "AI-Guided" strategy, for those who just want to download/upload stuff ( with a well-defined local/remote path).

Hi @FanDjango  and anyone else who has some time. Please check out this branch and test it and see if it works for you!
https://github.com/robinrodricks/FluentFTP/tree/v40

@robinrodricks
Ok - if I
remove c# and VB examples from the solution
remove the two test projects from the solution
remove ssl validation and TLS connection from my z/OS Test project
then I can build.
Then the entire z/OS Test Suite that I have accumulated over the last year runs fine.
Congratulations from me.

Thanks @FanDjango, do you get serious errors with c# and VB examples, test projects? Or do you mean your own projects? What kind of errors do you get, can you list them here please.

Here's what I did, to be totally fresh and clean, no changes, no own projects at first, nothing, nada:

Deleted my complete fork
Forked anew
Cloned my (forked) repo into VS2022
Successfully DEBUG CLEAN, then REBUILD branch master, all ok.
switched to branch V40, after fetching from upstream into my fork
DEBUG CLEAN branch v40, all ok
REBUILD branch v40: Here are the errors:

Note: Many previous lines all ok, I am starting the output from place of first error occurence.
You need to scroll right to see the error messages.
4>------ Rebuild All started: Project: CSharpExamples, Configuration: Debug Any CPU ------
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DeleteDirectory.cs(21,16,21,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DeleteDirectory.cs(24,16,24,36): error CS1061: 'FtpClient' does not contain a definition for 'DeleteDirectoryAsync' and no accessible extension method 'DeleteDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectFTPSCertificate.cs(25,16,25,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\Connect.cs(35,16,35,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadManyFiles.cs(32,15,32,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadManyFiles.cs(35,15,35,31): error CS1061: 'FtpClient' does not contain a definition for 'UploadFilesAsync' and no accessible extension method 'UploadFilesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\Connect.cs(44,16,44,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\CreateDirectory.cs(21,16,21,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\CreateDirectory.cs(23,16,23,36): error CS1061: 'FtpClient' does not contain a definition for 'CreateDirectoryAsync' and no accessible extension method 'CreateDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DeleteFile.cs(21,16,21,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DeleteFile.cs(23,16,23,31): error CS1061: 'FtpClient' does not contain a definition for 'DeleteFileAsync' and no accessible extension method 'DeleteFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DirectoryExists.cs(23,16,23,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DirectoryExists.cs(25,20,25,40): error CS1061: 'FtpClient' does not contain a definition for 'DirectoryExistsAsync' and no accessible extension method 'DirectoryExistsAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxyHttp11.cs(28,27,28,47): error CS0246: The type or namespace name 'FtpClientHttp11Proxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxySocks4.cs(28,27,28,47): error CS0246: The type or namespace name 'FtpClientSocks4Proxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxySocks5.cs(29,27,29,47): error CS0246: The type or namespace name 'FtpClientSocks5Proxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxySocks4a.cs(28,27,28,48): error CS0246: The type or namespace name 'FtpClientSocks4aProxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadDirectory.cs(30,15,30,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadDirectory.cs(34,15,34,37): error CS1061: 'FtpClient' does not contain a definition for 'DownloadDirectoryAsync' and no accessible extension method 'DownloadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadDirectory.cs(37,15,37,37): error CS1061: 'FtpClient' does not contain a definition for 'DownloadDirectoryAsync' and no accessible extension method 'DownloadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxyHttp11.cs(72,27,72,47): error CS0246: The type or namespace name 'FtpClientHttp11Proxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxySocks5.cs(73,27,73,47): error CS0246: The type or namespace name 'FtpClientSocks5Proxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxySocks4.cs(72,27,72,47): error CS0246: The type or namespace name 'FtpClientSocks4Proxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectProxySocks4a.cs(72,27,72,48): error CS0246: The type or namespace name 'FtpClientSocks4aProxy' could not be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadFileWithProgress.cs(34,15,34,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadFileWithProgress.cs(47,15,47,30): error CS1061: 'FtpClient' does not contain a definition for 'UploadFileAsync' and no accessible extension method 'UploadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadFile.cs(27,15,27,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadFile.cs(30,15,30,32): error CS1061: 'FtpClient' does not contain a definition for 'DownloadFileAsync' and no accessible extension method 'DownloadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadFile.cs(33,15,33,32): error CS1061: 'FtpClient' does not contain a definition for 'DownloadFileAsync' and no accessible extension method 'DownloadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadFile.cs(30,15,30,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadFile.cs(33,15,33,30): error CS1061: 'FtpClient' does not contain a definition for 'UploadFileAsync' and no accessible extension method 'UploadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadFile.cs(36,15,36,30): error CS1061: 'FtpClient' does not contain a definition for 'UploadFileAsync' and no accessible extension method 'UploadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadFile.cs(39,15,39,30): error CS1061: 'FtpClient' does not contain a definition for 'UploadFileAsync' and no accessible extension method 'UploadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadFileWithProgress.cs(34,15,34,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadFileWithProgress.cs(47,15,47,32): error CS1061: 'FtpClient' does not contain a definition for 'DownloadFileAsync' and no accessible extension method 'DownloadFileAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\FileExists.cs(25,16,25,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\FileExists.cs(29,20,29,35): error CS1061: 'FtpClient' does not contain a definition for 'FileExistsAsync' and no accessible extension method 'FileExistsAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadManyFiles.cs(31,15,31,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadManyFiles.cs(34,15,34,33): error CS1061: 'FtpClient' does not contain a definition for 'DownloadFilesAsync' and no accessible extension method 'DownloadFilesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadDirectoryWithRules.cs(40,15,40,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadDirectoryWithRules.cs(48,15,48,37): error CS1061: 'FtpClient' does not contain a definition for 'DownloadDirectoryAsync' and no accessible extension method 'DownloadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\DownloadDirectoryWithRules.cs(56,15,56,37): error CS1061: 'FtpClient' does not contain a definition for 'DownloadDirectoryAsync' and no accessible extension method 'DownloadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ExecuteFTPCommand.cs(24,16,24,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ExecuteFTPCommand.cs(27,30,27,42): error CS1061: 'FtpClient' does not contain a definition for 'ExecuteAsync' and no accessible extension method 'ExecuteAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetFileSize.cs(21,16,21,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetFileSize.cs(23,57,23,73): error CS1061: 'FtpClient' does not contain a definition for 'GetFileSizeAsync' and no accessible extension method 'GetFileSizeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetChecksum.cs(36,16,36,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetChecksum.cs(39,31,39,47): error CS1061: 'FtpClient' does not contain a definition for 'GetChecksumAsync' and no accessible extension method 'GetChecksumAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadDirectoryWithRules.cs(40,15,40,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadDirectoryWithRules.cs(48,15,48,35): error CS1061: 'FtpClient' does not contain a definition for 'UploadDirectoryAsync' and no accessible extension method 'UploadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadDirectoryWithRules.cs(56,15,56,35): error CS1061: 'FtpClient' does not contain a definition for 'UploadDirectoryAsync' and no accessible extension method 'UploadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\Rename.cs(24,16,24,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\Rename.cs(28,16,28,27): error CS1061: 'FtpClient' does not contain a definition for 'RenameAsync' and no accessible extension method 'RenameAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\SetWorkingDirectory.cs(22,16,22,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\SetWorkingDirectory.cs(23,16,23,40): error CS1061: 'FtpClient' does not contain a definition for 'SetWorkingDirectoryAsync' and no accessible extension method 'SetWorkingDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetWorkingDirectory.cs(21,16,21,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetWorkingDirectory.cs(23,65,23,89): error CS1061: 'FtpClient' does not contain a definition for 'GetWorkingDirectoryAsync' and no accessible extension method 'GetWorkingDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadDirectory.cs(30,15,30,27): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadDirectory.cs(34,15,34,35): error CS1061: 'FtpClient' does not contain a definition for 'UploadDirectoryAsync' and no accessible extension method 'UploadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\UploadDirectory.cs(37,15,37,35): error CS1061: 'FtpClient' does not contain a definition for 'UploadDirectoryAsync' and no accessible extension method 'UploadDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetNameListing.cs(27,16,27,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetNameListing.cs(29,34,29,53): error CS1061: 'FtpClient' does not contain a definition for 'GetNameListingAsync' and no accessible extension method 'GetNameListingAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetNameListing.cs(32,35,32,55): error CS1061: 'FtpClient' does not contain a definition for 'DirectoryExistsAsync' and no accessible extension method 'DirectoryExistsAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetNameListing.cs(33,30,33,50): error CS1061: 'FtpClient' does not contain a definition for 'GetModifiedTimeAsync' and no accessible extension method 'GetModifiedTimeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetNameListing.cs(34,46,34,62): error CS1061: 'FtpClient' does not contain a definition for 'GetFileSizeAsync' and no accessible extension method 'GetFileSizeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetModifiedTime.cs(22,16,22,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetModifiedTime.cs(25,21,25,41): error CS1061: 'FtpClient' does not contain a definition for 'GetModifiedTimeAsync' and no accessible extension method 'GetModifiedTimeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\ConnectFTPS.cs(24,16,24,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetListing.cs(45,16,45,28): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetListing.cs(48,37,48,52): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsync' and no accessible extension method 'GetListingAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetListing.cs(55,58,55,78): error CS1061: 'FtpClient' does not contain a definition for 'GetModifiedTimeAsync' and no accessible extension method 'GetModifiedTimeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetListing.cs(62,54,62,70): error CS1061: 'FtpClient' does not contain a definition for 'GetFileSizeAsync' and no accessible extension method 'GetFileSizeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetListing.cs(63,58,63,78): error CS1061: 'FtpClient' does not contain a definition for 'GetModifiedTimeAsync' and no accessible extension method 'GetModifiedTimeAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.CSharpExamples\GetListing.cs(64,50,64,63): error CS1061: 'FtpClient' does not contain a definition for 'GetChmodAsync' and no accessible extension method 'GetChmodAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
4>Done building project "CSharpExamples.csproj" -- FAILED.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Attributes\Internal\SkippableFactTestCase.cs(17,217,17,221): warning CS8625: Cannot convert null literal to non-nullable reference type.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpServer.cs(21,14,21,91): warning CS8601: Possible null reference assignment.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpServer.cs(18,10,18,25): warning CS8618: Non-nullable field '_container' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpContainer.cs(14,17,14,27): warning CS8618: Non-nullable field 'ServerName' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpContainer.cs(15,17,15,28): warning CS8618: Non-nullable field 'DockerImage' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpContainer.cs(16,17,16,36): warning CS8618: Non-nullable field 'DockerImageOriginal' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpContainer.cs(17,17,17,29): warning CS8618: Non-nullable field 'DockerGithub' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpContainer.cs(18,17,18,30): warning CS8618: Non-nullable field 'FixedUsername' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
2>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\Docker\DockerFtpContainer.cs(19,17,19,30): warning CS8618: Non-nullable field 'FixedPassword' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DeleteFile.vb(20,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DeleteFile.vb(21,23): error BC30456: 'DeleteFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadDirectory.vb(28,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadDirectory.vb(31,23): error BC30456: 'DownloadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadDirectory.vb(34,23): error BC30456: 'DownloadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\ConnectFTPS.vb(23,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DeleteDirectory.vb(22,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DeleteDirectory.vb(25,23): error BC30456: 'DeleteDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\ConnectFTPSCertificate.vb(23,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadFile.vb(26,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadFile.vb(29,23): error BC30456: 'DownloadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadFile.vb(32,23): error BC30456: 'DownloadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadManyFiles.vb(29,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadManyFiles.vb(32,23): error BC30456: 'UploadFilesAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadFileWithProgress.vb(33,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadFileWithProgress.vb(46,23): error BC30456: 'UploadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\Connect.vb(29,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\Connect.vb(37,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadFile.vb(29,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadFile.vb(32,23): error BC30456: 'UploadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadFile.vb(35,23): error BC30456: 'UploadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadFile.vb(38,23): error BC30456: 'UploadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\CreateDirectory.vb(20,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\CreateDirectory.vb(21,23): error BC30456: 'CreateDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DirectoryExists.vb(24,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DirectoryExists.vb(26,26): error BC30456: 'DirectoryExistsAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadManyFiles.vb(29,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadManyFiles.vb(32,23): error BC30456: 'DownloadFilesAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadDirectoryWithRules.vb(40,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadDirectoryWithRules.vb(50,23): error BC30456: 'UploadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadDirectoryWithRules.vb(57,23): error BC30456: 'UploadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadDirectoryWithRules.vb(40,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadDirectoryWithRules.vb(50,23): error BC30456: 'DownloadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadDirectoryWithRules.vb(57,23): error BC30456: 'DownloadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadDirectory.vb(28,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadDirectory.vb(31,23): error BC30456: 'UploadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\UploadDirectory.vb(34,23): error BC30456: 'UploadDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\SetWorkingDirectory.vb(20,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\SetWorkingDirectory.vb(21,23): error BC30456: 'SetWorkingDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\Rename.vb(23,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\Rename.vb(27,23): error BC30456: 'RenameAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadFileWithProgress.vb(33,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\DownloadFileWithProgress.vb(46,23): error BC30456: 'DownloadFileAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetWorkingDirectory.vb(20,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetWorkingDirectory.vb(21,72): error BC30456: 'GetWorkingDirectoryAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetNameListing.vb(25,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetNameListing.vb(27,37): error BC30456: 'GetNameListingAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetNameListing.vb(28,45): error BC30456: 'DirectoryExistsAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetNameListing.vb(29,40): error BC30456: 'GetModifiedTimeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetNameListing.vb(30,57): error BC30456: 'GetFileSizeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetModifiedTime.vb(20,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetModifiedTime.vb(21,68): error BC30456: 'GetModifiedTimeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetListing.vb(38,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetListing.vb(41,40): error BC30456: 'GetListingAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetListing.vb(47,74): error BC30456: 'GetModifiedTimeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetListing.vb(51,70): error BC30456: 'GetFileSizeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetListing.vb(52,74): error BC30456: 'GetModifiedTimeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetListing.vb(53,66): error BC30456: 'GetChmodAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetFileSize.vb(20,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetFileSize.vb(21,64): error BC30456: 'GetFileSizeAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetChecksum.vb(36,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\GetChecksum.vb(39,45): error BC30456: 'GetChecksumAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\FileExists.vb(24,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\FileExists.vb(26,26): error BC30456: 'FileExistsAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\ExecuteFTPCommand.vb(14,25): warning BC40000: 'Public Shared Function __Assign(Of FtpReply)(ByRef target As FtpReply, value As FtpReply) As FtpReply' is obsolete: 'Please refactor calling code to use normal Visual Basic assignment'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\ExecuteFTPCommand.vb(24,23): error BC30456: 'ConnectAsync' is not a member of 'FtpClient'.
3>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.VBExamples\ExecuteFTPCommand.vb(27,58): error BC30456: 'ExecuteAsync' is not a member of 'FtpClient'.
3>Done building project "VBExamples.vbproj" -- FAILED.
2>FluentFTP.Xunit -> C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Xunit\bin\Debug\net6.0\FluentFTP.Xunit.dll
2>Done building project "FluentFTP.Xunit.csproj".
5>------ Rebuild All started: Project: FluentFTP.Tests, Configuration: Debug Any CPU ------
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\System\IntegrationTestSuite.cs(31,29,31,45): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ConnectTests.cs(41,20,41,32): error CS1061: 'FtpClient' does not contain a definition for 'ConnectAsync' and no accessible extension method 'ConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\CustomTests.cs(29,17,29,33): error CS1061: 'FtpClient' does not contain a definition for 'AutoConnectAsync' and no accessible extension method 'AutoConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(22,24,22,28): warning CS8600: Converting null literal or possible null value to non-nullable type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ConnectTests.cs(64,34,64,50): error CS1061: 'FtpClient' does not contain a definition for 'AutoConnectAsync' and no accessible extension method 'AutoConnectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\FileTransferTests.cs(44,39,44,55): error CS1061: 'FtpClient' does not contain a definition for 'UploadBytesAsync' and no accessible extension method 'UploadBytesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\FileTransferTests.cs(47,35,47,53): error CS1061: 'FtpClient' does not contain a definition for 'DownloadBytesAsync' and no accessible extension method 'DownloadBytesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ConnectTests.cs(78,35,78,50): error CS1061: 'FtpClient' does not contain a definition for 'AutoDetectAsync' and no accessible extension method 'AutoDetectAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\TimeoutTests.cs(50,18,50,30): error CS1501: No overload for method 'ConnectAsync' takes 0 arguments
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(50,17,50,33): error CS1061: 'FtpClient' does not contain a definition for 'UploadBytesAsync' and no accessible extension method 'UploadBytesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(51,17,51,33): error CS1061: 'FtpClient' does not contain a definition for 'UploadBytesAsync' and no accessible extension method 'UploadBytesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(53,44,53,69): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsyncEnumerable' and no accessible extension method 'GetListingAsyncEnumerable' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(56,49,56,74): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsyncEnumerable' and no accessible extension method 'GetListingAsyncEnumerable' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(59,49,59,74): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsyncEnumerable' and no accessible extension method 'GetListingAsyncEnumerable' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(63,17,63,41): error CS1061: 'FtpClient' does not contain a definition for 'SetWorkingDirectoryAsync' and no accessible extension method 'SetWorkingDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(64,56,64,81): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsyncEnumerable' and no accessible extension method 'GetListingAsyncEnumerable' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(74,17,74,33): error CS1061: 'FtpClient' does not contain a definition for 'UploadBytesAsync' and no accessible extension method 'UploadBytesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(75,17,75,33): error CS1061: 'FtpClient' does not contain a definition for 'UploadBytesAsync' and no accessible extension method 'UploadBytesAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(77,32,77,47): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsync' and no accessible extension method 'GetListingAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(80,37,80,52): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsync' and no accessible extension method 'GetListingAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(83,37,83,52): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsync' and no accessible extension method 'GetListingAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(87,17,87,41): error CS1061: 'FtpClient' does not contain a definition for 'SetWorkingDirectoryAsync' and no accessible extension method 'SetWorkingDirectoryAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\ListingTests.cs(88,44,88,59): error CS1061: 'FtpClient' does not contain a definition for 'GetListingAsync' and no accessible extension method 'GetListingAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\FileTransferTests.cs(83,39,83,56): error CS1061: 'FtpClient' does not contain a definition for 'UploadStreamAsync' and no accessible extension method 'UploadStreamAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Integration\Tests\FileTransferTests.cs(87,34,87,53): error CS1061: 'FtpClient' does not contain a definition for 'DownloadStreamAsync' and no accessible extension method 'DownloadStreamAsync' accepting a first argument of type 'FtpClient' could be found (are you missing a using directive or an assembly reference?)
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(125,5,125,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(138,5,138,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(139,5,139,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(140,5,140,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(150,5,150,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(248,5,248,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(249,5,249,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(421,5,421,9): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(448,37,448,41): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(464,37,464,41): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(479,37,479,41): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>C:\Users\FanDjango\source\repos\FluentFTP\FluentFTP.Tests\Unit\ParserTests.cs(495,37,495,41): warning CS8625: Cannot convert null literal to non-nullable reference type.
5>Done building project "FluentFTP.Tests.csproj" -- FAILED.
========== Rebuild All: 2 succeeded, 3 failed, 0 skipped ==========

@FanDjango  You're right, all the tests and examples were broken. I fixed it now.
Do you support the idea of removing the Async suffix for methods in the AsyncFtpClient?

If there is with a class both non-async and async method that return the same thing suffix the async one with Async. Otherwise not.
But if these methods are bound to two different classes, I would not suffix with Async, as this is a homomorphism.

Done:

New: Drop Async suffix for all async FTP methods (DownloadFile instead of DownloadFileAsync)

@jnyrup Can you review this please? To set a logger you can use client.Logger = XYZ
https://github.com/robinrodricks/FluentFTP/blob/v40/FluentFTP/Client/BaseClient/Log.cs#L15

Usage: To set a logger you can use client.Logger = XYZ and/or client.LogToConsole = true

Console is a possible output, so the former includes the second. I agree with keeping options such as LogFunctions because these indicates to the internals what should be logged but where to log should be kept in a single option (Logger) IMHO. If the user wants to log to console he should provide a ConsoleLogger. To be (very) strict, even this
#if DEBUG
	Debug.WriteLine(message);
#endif

may not be there but shoud be turned into a logger.
Moreover, be careful of adding MELA dependency (I agree with all @jnyrup considerations here), transitive dependency management is one of the biggest PITA of Nuget and has even changed in .NET standard management.
As a style note, I would not check for if(m_logger != null). It should never be null, this is why also @jnyrup was suggesting a NoOpLogger, so enforce is to not be null.
My 2 cents
A

In LogToDebugOrConsole the debug part is inside #if DEBUG and as we all consume a release mode of FluentFtp, it will
effectively only be LogToConsole, i.e. no debugging part.

Instead of controlling logging via the knobs Config.LogFunctions etc. we could let the configuration of the logger decide what should be written.
protected void LogFunc(string function, object[] args = null) {
    if (m_logger.IsEnabled(LogLevel.Debug)) {
        ...
    }
}

For example a modern C# application will read the loglevel from one of these json files depending on the application is deployed to production or running locally while developing.
appSettings.production.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "FluentFTP": "Warning"
    }
  }
}

appSettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "FluentFTP": "Trace"
    }
  }
}

Similarly there is LogLevel.Trace for Ip/UserName/Password.
LogLevel.Trace

Logs that contain the most detailed messages. These messages may contain sensitive application data. These messages are disabled by default and should never be enabled in a production environment.

Suggestion:
Let m_logger default to a NoOpLogger instance, which does nothing.
Then you don't have to null check m_logger, except in the setter of the property Logger.

Instead of controlling logging via the knobs Config.LogFunctions etc. we could let the configuration of the logger decide what should be written

Yes but how would the logger be able to have these application-specific logging knobs? These are specific to the way FluentFTP handles things.

if (m_logger.IsEnabled(LogLevel.Debug)) {

Is this the industry standard way of doing it? If so I will support it like this.

In LogToDebugOrConsole the debug part is inside #if DEBUG and as we all consume a release mode of FluentFtp, it will
effectively only be LogToConsole, i.e. no debugging part.

Probably #if DEBUG becomes handy maybe when running tests, I was just saying that code can be cleaned up a bit by injecting a dedicated logger when debugging.

Instead of controlling logging via the knobs Config.LogFunctions etc. we could let the configuration of the logger decide what should be written.

This would simplify but requires to bound function call logs to debug level. This is fine, I'm not against it, but @robinrodricks could keep the "business" knobs (function log etc...) as a separate control.
E.g. you may keep a flag to obfuscate passwords, if set you replace password with "****", this kind of setting is more like an interceptor/manipulator than a logger itself and as such cannot be replaced by a Trace or Debug level.  From an architecture point of view
log request --> interceptor --> actual logger with level
If you end up with something like that, I suggest defining an IInternalLoggerInterface, with higher level methods
internal interface IInternalLoggerInterface
{
    void Credentials(string username, string password);
    void Address(IPEndPoint address);
    void Message(string message);
}

This would allow you to keep all the log logic in one place instead of having if(Config.LogPasswords) all around
To me the important thing is not duplicating output knobs, ILogger decides where

My comment was (probably) also meant as question/comment on why having all these logging knobs i FluentFTP.
My line of thought is:
FluentFtp has a piece of information and decides the severity (log level) of it.
It's then up to the logger whether, where and how to log it.

if (m_logger.IsEnabled(LogLevel.Debug)) {

This is not strictly necessary (I also had to look this up again), it just lets you skip some logic if no logging will be performed anyways.
In the former ItemsToString() is always invoked, while in the latter it is only invoked if debug logging is enabled.
m_logger.LogInformation(function, args.ItemsToString());

if (m_logger.IsEnabled(LogLevel.Debug))
    m_logger.LogDebug(function, args.ItemsToString());

@Adhara3 so can you guys decide what log settings you want or don't want (username/password/IP). I'm fine either way.

So my final suggestion is:

Remove LogFunctions (always log it)
Remove LogUser/Pass/IP (Always mask out sensitive info)

I'm fine with functions mapped to debug and user/pass/IP mapped as trace.
I still recommend merging this in a single place.
A

I'm fine with functions mapped to debug and user/pass/IP mapped as trace.

@Adhara3  shall we go this way?

Done:

Remove logging settings as they are always enabled: LogFunctions
Remove logging settings as sensitive data is always masked: LogIP, LogUserName, LogPassword

What should we do about the 100 constructors? Can I only keep 1 or 2 of them because this is honestly excessive.

Ideally

FtpClient(Uri host)
FtpClient(Uri host, NetworkCredential credentials)

should be enough with a couple of notes:

Internally you put account into NetworkCredential domain field, which may not be standard (I'm no expert here), so that could be tricky for the user
You can provide, if you wish, external helper methods (e.g.  to go from host to Uri), basically migrating constructors to helper methods. The advantage is that you better separate responsibilities. Up to you.
To me, properties that are constructor based should be readonly.

My 2 cents
A

To me, properties that are constructor based should be readonly.

Good point. In that case why not remove ALL the parametered constructors and just keep:
`new FtpClient(ILogger logger = null, FtpConfig config = null)

n that case why not remove ALL the parametered constructors and just keep

The difference is that using the constructor you are sure that the object itself is usable, while using properties you are not.
var brokenClient = new FTPClient();
// brokenClient is in a broken state

var okClient = new FTPClient("host", "user", "password");
// ok client has all the required parameter, so it's not in a weird state. Of course, this does not mean that "host" exists but there is nonetheless a huge difference between "I require a host you did not provide" and "the provided host does not exist"

As a user (but also as a tech guy) the object coming from a constructor should be able to work without any further adjustment, or if you wish, required parameter should go into the constructor, optional one can be properties.
My 2 cents
A

Done:

Constructor API

New: 3 new FTP client constructors that accept FTP host, credentials, config and logger
Remove extranous constructors because properties can be used instead

I think I'm ready to release this.
@Adhara3 @jnyrup @FanDjango Can you please test the latest master branch locally and see if it looks good?
I will release after at least 1 person confirms everything works fine.

I won't have time to test this until tomorrow evening at earliest.
Can you publish a v40 beta version to nuget?
That would make it easier for more to test and come with feedback before this large change.

I won't have time to test this until tomorrow evening at earliest. Can you publish a v40 beta version to nuget? That would make it easier for more to test and come with feedback before this large change.

Test anytime you want. I have no idea how to publish a beta version to nuget :)

It is a pity you have removed FtpClient(host, port, user, pass...)

I needed to recode a few places to .Config....,
I have not set up logging yet,
For some reason in my test application I needed to add using FluentFTP.Client.BaseClient; (haven't researched why yet...)
so apart from progress messages, which I myself produce per Console.Writeln, I am running blind 👍 but my test suite for z/OS runs and finishes with no errors or problems.

I will release after at least 1 person confirms everything works fine

everything ?????? No, I can only confirm my special stuff. But yolt, right?
Ähm, release V40? Ok, but what about the Wiki? I needed to do quite a few changes and the logging changes will keep me busy some more time. Real world users who have not been following this thread will be slightly surprised at the build errors they will be getting... One could use a small dictionary of what compiler errors to handle in what way to fix it.

Test anytime you want. I have no idea how to publish a beta version to nuget :)

By setting the Version to e.g. 40.0.0-beta1.
https://github.com/robinrodricks/FluentFTP/blob/f59e510ef75bab29881afd5e8b90ee2e19da0163/FluentFTP/FluentFTP.csproj#L31
https://docs.microsoft.com/en-us/nuget/create-packages/prerelease-packages

I will use a custom logger provider and some glue logic to get the log output to the status windows of my z/OS FTP client. Doing this cleanly exceeds the time I have in the next few days.
My summary of the experiences with the transition from V39.x to V40:
Up to now all my stuff works.
Some code modifications are needed, as you can imagine from your changes. It would be nice for these to be summarised in some migration text, mabye.
Especially those not experienced in implementing Ilogger (I am just a C# novice) could use a sample and if I have time I will provide mine (which will be really minimal, not like the ones to be found on the internet). It will enable a quick get up and go for those who need to migrate to V40.

For those follwing this thread and wanting to work with V40 as soon as possible with minimal disruption:
You need
using Microsoft.Extensions.Logging;
and the coresponding Nuget packages...
and then (my sort of minimum approach, for a quick start):
		public class CustomConsoleLogger : ILogger {
			public CustomConsoleLogger() {
			}

			public IDisposable BeginScope<TState>(TState state) {
				return null;
			}

			public bool IsEnabled(LogLevel logLevel) {
				return true;
			}

			public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) {
				if (!IsEnabled(logLevel)) {
					return;
				}

				// Console.WriteLine($"{logLevel.ToString()} - {eventId.Id} - {formatter(state, exception)}");
				Console.WriteLine($"{formatter(state, exception)}");
			}
		}
		public class CustomLoggerProvider : ILoggerProvider {
			public CustomLoggerProvider() {
			}

			public ILogger CreateLogger(string name) {
				return new CustomConsoleLogger();
			}

			public void Dispose() {
			}
		}

and
			// Either:

	/* */	// FTP_Sess.Config.LogToConsole = true;

			// Or:

	/* */	ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
	/* */	{
	/* */		//builder.AddConsole();
	/* */		//builder.AddDebug();
	/* */	});
        /* */
        /* */	loggerFactory.AddProvider(new CustomLoggerProvider());
        /* */
        /* */	ILogger<BaseFtpClient> logger = loggerFactory.CreateLogger<BaseFtpClient>();
        /* */
	/* */	FTP_Sess.Logger = logger;

will get you going if you need console output or logging, or putting the output where you need it, like a message panel or whatever..
I realize that this is old well known stuff for the C# experts out there but duh! Not for me, it wasn't.

And here is a bug:
File Listings appear correctly if you use FTP_Sess.Config.LogToConsole = true;
File Listings do not appear if you use an Ilogger implementation.

@FanDjango Hmm that sounds wonky. Is the ILogger crashing and aborting the file listing?

@FanDjango
https://github.com/robinrodricks/FluentFTP/wiki/v40-Migration-Guide

@jnyrup @FanDjango  Released a beta version!
https://www.nuget.org/packages/FluentFTP/40.0.0-beta1

@robinrodricks Re. this:

File Listings appear correctly if you use FTP_Sess.Config.LogToConsole = true;

File Listings do not appear if you use an Ilogger implementation.

File Listings and other sundry output are sent by you via:
				case FtpTraceLevel.Verbose:
					m_logger.Debug(message);
					break;

For .Debug (and .Trace) to be handled, you need an appsettings.json or even an appsettings.development.json file to configure ILogger.
This is not really attractive.
I changed it to
				case FtpTraceLevel.Verbose:
					m_logger.LogInformation(message);
					break;

and I am getting my messages.
You can take it onward from there, I suppose.

@robinrodricks
Since I want to discern between the old loglevels "Verbose" and "Info" in my application, I changed the code to;
				case FtpTraceLevel.Verbose:
					m_logger.LogInformation("+++ " + message);
					break;

Individual commands have not been changed. Whatever is the old log level has been preserved. Only thing has changed is the mapping from Ftp log level to MS log level.

I will investigate and then I will do that. Thanks.

Thanks very much in advance for any fixes towards v40.

It is a pity you have removed FtpClient(host, port, user, pass...)

Good point, added port back into the constructor. I added it at the end if that's ok?

host, port, user, pass seems more logical, though

host, port, user, pass seems more logical, though

That's true, however many people don't pass a port and just host/user/pass. Because most times FTP is on known ports.

The two FTP servers I connect to are both public and use default port 21, so with the new set of constructors I'll have change from
new FtpClient(host)

to
new FtpClient
{
    Host = host
}

Not a deal-breaker for me at all, but I would have gone with a single FtpClient(string host, FtpConfig config = null) since host is the minimal needed information to attempt to connect to an FTP server and config because it is passed along to BaseFtpClient.
Credentials and a non-standard port are optional in my view.

new FtpClient
{
    Host = host
...
...
}

I would prefer to recode to this and config use after considering, also. Could then remove all extra constructors...

The downside of making all properties optional is that one can construct an instance of FtpClient which is not ready to use.
var client = new FtpClient();
client.Connect();             // who to connect to?

@jnyrup  Fair enough. I have added a host constructor. Does it work for you?
public FtpClient(string host, int port = 0, FtpConfig config = null, ILogger logger = null)

The downside of making all properties optional is that one can construct an instance of FtpClient which is not ready to use.
var client = new FtpClient();
client.Connect();             // who to connect to?

Sounds quite similar to this
:)

@Adhara3  I want to avoid over complicating the API. I really don't like java-style SOLID principles and the convoluted class structures that it produces. Besides this library is super simple, just one main class with a bunch of methods. So I'm trying to make it as simple to use as possible.

I don't know if there is a "time-out" symbol or meme, but like in a basket-ball game, can I request such a time-out, or subroutine call?
I have a problem with V40. Well, I had some concerns and was saying, not a deal breaker, just like @jnyrup.
But my problem, reported by a (paying) user of my application, is: TLS AUTH not working (Test server is ProFTPD). Now, I used my local tests and they work (Console Application, new Client() etc. etc. AutoConnect and all is ok), the actual application though (WinForms) does not connect. The problem is certainly on my side, I think, but it is provoked by the migration V39->V40.
Now is too early to go into details but please be assured I have bisected and debugged to a certain degree and I am sure I will find the problem. But what I am saying is: Don't release yet. I am re-using the client for multiple connections and I know it is a no-no, but the failure is on the first usage. So, duh! Sadly there are a few other customer requests pending so I don't see myself debugging this further until next week begins, customer has rolled back to one of my apps that uses V39.
The concerns I have, independantly of your discussions about the perfect way to present this library to a harried, hurried and anxious to use it user, is that my application suffers from:
The new logger or is it overkill?

I want an easy way to FTP(S) with the emphasis on the S, the normal stuff I could do myself. No time for the SSL, I thought.
I want the full conversation with the server to report to the user.
 The verbose ones are missing, see above.

Can you imagine? I have users of the app, and I am to a certain degree already considering cloning the latest 39.xx and using that, which means rolling back. I can do without the hassle.

TLS AUTH not working

I'm sorry about that.

I don't want to package lots of .dlls other than FluentFTP (which I need to do if I implement ILogger)

Many users chose this. I agreed with them on the premise that you can implement your own ILogger implementation and do anything you want with it. Plus we have never been able to integrate cleanly with log4net and other big .NET loggers and this was a goal too.

I want the full conversation with the server to report to the user.

Are you not getting it now? You know you could always implement your own ILogger implementation and consume the log events yourself. I agree about the issues with Debug/Trace, if there is no easy way to overcome it, then I could consider adding back the client.OnLogEvent which would be the legacy logger system, it would always fire, and it would pass the FtpTrace level. Does that work for you?

Agreed. Config files can become a deployment hassle.

I have users of the app, and I am to a certain degree already considering cloning the latest 39.xx and using that, which means rolling back.

@FanDjango There was something wonky in the new logger implementation as I was not always adding a prefix to the messages. That could be why you noticed things going wierd. I have now always added a prefix, just like the old system.
I have also added back client.LegacyLogger which is an easy way to get log messages. Please try it out:
https://www.nuget.org/packages/FluentFTP/40.0.0-beta2

I have also added back client.LegacyLogger which is an easy way to get log messages. If you use this you don't need a config file. Please try it out

Ok, in my fork, I had the same idea and did that already, but I am using your newest master now and your LegacyLogger works fine. One note though: If using LegacyLogger in a Consoe Application you must turn off Config.LogToConsole depending on your needs.

LegacyLogger is just going to call that function. It does not conflict with Config.LogToConsole

I am stressed for time right now but have found out why my test programs (Console Application) and the actual production application is not:
In FtpSocketStream.cs we are taking a different path.
				try {
#if NETSTANDARD
					m_sslStream.AuthenticateAsClientAsync(targethost, clientCerts, sslProtocols, Client.Config.ValidateCertificateRevocation).Wait();
#else
					m_sslStream.AuthenticateAsClient(targethost, clientCerts, sslProtocols, Client.Config.ValidateCertificateRevocation);
#endif
				}
				catch (IOException ex) {

The NETSTANDARD path is taken by ConsApp and works.
The else path is taken by my Win Forms app and fails: A call to SSPI failed, see inner exception. Function not available.
Instead of TLS1|TLS2, the sslProtocol is just TLS.
I have not had time to debug further.

Yes that part was changed because I deleted the manual implementation of some FtpSslStream thingy that was part of .NET framework build.

@FanDjango  - https://github.com/robinrodricks/FluentFTP/commit/5e49c22fde76cd1b5337e5b28f799c9432d93aae#diff-a912c4bca93b7162e47b5e68b45c3f9a879b13460c8fdd909e2c4bf8ea67598e

I am targeting .Net Framework 4.7, using FluentFTP bin from 4.6.2. I am unsure how to proceed.

@FanDjango I have restored the previous handling of active stream and stuff. Can you check the latest master?

No change in behaviour, I just tested it

In addition, having removed all Microsoft Logging Extension dlls and all the ones associated with it, once I do the autoconnect, I get an exception: The dlls are still required (probably indirectly in FluentFTP somewhere).
@robinrodricks Now I say to you: Don't stress yourself, I am just a single user. I can go back and use V39.x. This is not about me alone. I am also quite practised in using my own fork. Stay cool. The one client of mine who upgraded has already gone back. Let some other also report their experiences, might be all ok.

You need MELA which is a requirement, no other DLLs are required.
@FanDjango  You were sounding pretty panicky so I was trying to help you out, also you are the only user on the thread actively trying out v40. I'm sad that auth does not work. I'm still working on this. Are you still going to try with v40 or have you given up?

Are you still going to try with v40

Saturday evening. Before then, no chance - out of the office from now. I'd like to resolve this one way or the other.

@FanDjango Please try the latest master. I have restored the FtpSslStream. I also found that the SSLProtocols were set incorrectly.

You need MELA which is a requirement, no other DLLs are required.

Oki doki, did it. I can see why, now, too.

Please try the latest master. I have restored the FtpSslStream

Sadly, same behaviour.

found that the SSLProtocols were set incorrectly

I mentioned that previously, too.

It is likely this release will work for you. If it does let me know so I can try something.

Doesn't, so we need to do more involved debugging maybe. But not from this hotel room...

Hi firstly I want to thank you for being so dedicated and working on this problem from a hotel room!
At this point its frustrating and shocking - I have restored all the older .NET framework related behavior and it should have worked 100%. Anyway I will have to see how to proceed. I will do a diff of the last major version and see if I can spot any changes. If that also fails, in the WORST case, I'll have to begin again with the older code and perform ALL the refactoring again which may not be possible due to the sheer effort, so I'll have to see....

@FanDjango  Any error or something that I can use to debug this?

The downside of making all properties optional is that one can construct an instance of FtpClient which is not ready to use.

The thing is that the Connect method will throw an error if there is no host specified, so I wouldn't say the object is in a "broken" state, just that its not ready to use.
After all I have no idea how my thousands of users are actually using it. Maybe they are using a blank constructor and they have other methods to configure the client at a later point in time, who knows? I want to give people the freedom to use it however they want, like a rubber band. I don't want just ONE right way of using it and every other way is WRONG or restricted by the system.

I would also second that it's good practice that constructors always leave an object in a usable state.
Benefits:

the code clearly communicates with the developer what the minimum requirements are to work with this thing
it's safer. If you use properties you could, for example, accidentally remove client.Host = "ftp://foo.bar"; but only find out when you run the software. If you remove a constructor argument it will (most likely ) fail to compile.  ( I sometimes use named arguments to make sure that arguments don't get mixed up or a different constructor gets used accidentally. With C# 10 we should be able to specify public string Host { get; init; } which will be nice, too).

it's safer. If you use properties you could, for example, accidentally remove client.Host = "ftp://foo.bar"; but only find out when you run the software. If you remove a constructor argument it will (most likely) fail to compile. So it's failing faster than throwing an exception.

These are all good points. If EVERYONE on this thread feels like I should remove the FtpClient() constructor, I will. I have no issues either way.

@robinrodricks Had short window of time to test right now: Current master is ok, connect is ok. Very good!
So now, if you want to incrementally go forward again with some of the changes you nixxed going backwards, I am at you service.

Can you bring back Client.Config.LogIP? Got the first complaints re. the three asterisks.

Had short window of time to test right now: Current master is ok, connect is ok. Very good!

F*cking finally!

So now, if you want to incrementally go forward again with some of the changes you nixxed going backwards, I am at your service.

Thank you so much, I just want to see if we can achieve good FTPS connectivity without the custom SSL stream class.

Can you bring back Client.Config.LogIP? Got the first complaints re. the three asterisks.

Hmm ok..

Hmm ok.. First time I'm seeing your colorized log, its very nice!

Do not bring the option back in, the problem here is that host is being treated as a password and masked. The option is useless, we said earlier always log host and user pass, using privacy accordingly
A

Do not bring the option back in, the problem here is that host is being treated as a password and masked

How does extra configurability hurt? 99% of people who post logs especially here on github need to mask out their servers as it is sensitive. However in this case the user wants to log it, so he should have some configurability to do it right.

@FanDjango  Can you try the latest please.
I have removed the FtpSslStream which was some legacy code.
I have added LogHost and LogCredentials.

I have removed the FtpSslStream which was some legacy code. If it fails to connect for you, I will revert this change.

It still works, no failure.

It still works, no failure.

Fantastic news.

I have removed all the ASYNC constants. Now we have only 95 conditional compilation checks (#if).
Released beta3.
https://www.nuget.org/packages/FluentFTP/40.0.0-beta3
@FanDjango @jnyrup and whoever else has time to check:
Please check if beta3 works for everyone then I will release as a final nuget package!

I tried out 40.0.0-beta3 on the handful of different FTP servers we connect to, and I didn't notice any problems.
We use a mixture of FtpClient/AsyncFtpClient and the methods Connect, SetWorkingDirectory, GetListing, GetNameListing, DownloadFile, DownloadBytes, Disconnect.

@jnyrup  Awesome, thanks very much for testing!
I have released a nuget with your changes, would appreciate if you could test again.
https://www.nuget.org/packages/FluentFTP/40.0.0-beta4
I added a note for all the contributors who made PRs against v40. Thanks guys!
@FanDjango  Once you confirm that beta4 is good, I will release v40!

@jnyrup Thanks for #940

This made it a bit of a pain point for me to contribute, as I had to disable auto-formatter when editing this project.
Same for me and this really helps

@robinrodricks I confirm 24dba70 works for my stuff
One thing disturbs me after this sequence of events: My test-suite uses a CLI calling the base of my app, it is a Console App, and I observe and check/analyze its output. My actual production app is a Win Forms GUI.
The test-suite (obviously) went a different compile path (#if...) on build and worked. This told me, all is well,
and then one of my clients, an important one but also one of the best - I love it when they pick up your updates and you get the feeling that they are "panting" for new stuff, reported this unexpected failure.
Do you have any ideas how to address this discrepancy?
I mean to say that the growing test suite is certainly also subject to this kind of divergence, not just mine.
Also, I cannot put a z/OS FTP server into a docker container - shouldn't we have a "sample" test unit for a "sample" real FTP server, of whatever kind. I would use that one...

This business concerning Config.LogHost (is good, works) and Config.LogCredentials:
Apart from the details of the previous discussion, I see the following:
Look, example:

331 Password required for <user>
230 User <user> logged on
Wow, what privacy. But that's not my desire, to hide the user.
`Config.LogCredentials' (tries) to hide both see 331 and 230.
How can I enable logging of the user without showing the password? I would agree that the 230 is enough perhaps, but a user trying to make sure his logon is right, would like to see the "USER ...." command.
I mean, all I want to hide is the password?
Of course, I can "correct" that in my logger routine (whichever method I choose to use).

@FanDjango Lol. I will add back User/Pass config. I will try to hide mike as well.

Both are the same platform? .NET 4.7?

Oooops.

One thing disturbs me after this sequence of events...

After some checking, I am no longer disturbed.

Oooops.

If some platform fails, I need to know.

You are going to see some stuff anyway.

@FanDjango  Done, please check the latest master - LogUserName & LogPassword added. mike should completely disappear.

If some platform fails, I need to know.

Yes, but you fixed it already, no longer a problem. But I found a problem on my side - testing with two different targets, a simple typo, which now both work, finally.

please check the latest master

Doing that in a moment

Latest master works for me.

Should I release the v40 nuget @FanDjango  and @jnyrup  ?

I have only a few minor comments left, but only the first one might be blocking v40 as it changes the API.

From the C# Design Guidelines

❌ DO NOT provide instance fields that are public or protected.
You should provide properties for accessing fields instead of making them public or protected.

There are 77 violations of that guideline

list of files
FluentFTP\Helpers\FtpListParser.cs:17
FluentFTP\Helpers\FtpListParser.cs:26
FluentFTP\Helpers\FtpListParser.cs:31
FluentFTP\Helpers\FtpListParser.cs:36
FluentFTP\Helpers\FtpListParser.cs:45
FluentFTP\Model\FtpClientState.cs:14
FluentFTP\Model\FtpClientState.cs:21
FluentFTP\Model\FtpClientState.cs:27
FluentFTP\Model\FtpClientState.cs:32
FluentFTP\Model\FtpClientState.cs:37
FluentFTP\Model\FtpClientState.cs:42
FluentFTP\Model\FtpClientState.cs:47
FluentFTP\Model\FtpClientState.cs:52
FluentFTP\Model\FtpClientState.cs:57
FluentFTP\Model\FtpFxpSession.cs:15
FluentFTP\Model\FtpFxpSession.cs:20
FluentFTP\Model\FtpFxpSession.cs:25
FluentFTP\Model\FtpFxpSessionAsync.cs:17
FluentFTP\Model\FtpFxpSessionAsync.cs:22
FluentFTP\Model\FtpFxpSessionAsync.cs:27
FluentFTP\Model\FtpListItem.cs:51
FluentFTP\Model\FtpListItem.cs:56
FluentFTP\Model\FtpListItem.cs:61
FluentFTP\Model\FtpListItem.cs:81
FluentFTP\Model\FtpListItem.cs:86
FluentFTP\Model\FtpListItem.cs:93
FluentFTP\Model\FtpListItem.cs:98
FluentFTP\Model\FtpListItem.cs:103
FluentFTP\Model\FtpListItem.cs:108
FluentFTP\Model\FtpListItem.cs:113
FluentFTP\Model\FtpListItem.cs:118
FluentFTP\Model\FtpListItem.cs:123
FluentFTP\Model\FtpListItem.cs:128
FluentFTP\Model\FtpListItem.cs:133
FluentFTP\Model\FtpListItem.cs:138
FluentFTP\Model\FtpListItem.cs:144
FluentFTP\Model\FtpListItem.cs:149
FluentFTP\Model\FtpListItem.cs:155
FluentFTP\Model\FtpListItem.cs:161
FluentFTP\Model\FtpListItem.cs:168
FluentFTP\Model\FtpProfile.cs:21
FluentFTP\Model\FtpProfile.cs:26
FluentFTP\Model\FtpProfile.cs:31
FluentFTP\Model\FtpProfile.cs:36
FluentFTP\Model\FtpProfile.cs:41
FluentFTP\Model\FtpProfile.cs:46
FluentFTP\Model\FtpProfile.cs:51
FluentFTP\Model\FtpProfile.cs:56
FluentFTP\Model\FtpProfile.cs:61
FluentFTP\Model\FtpProfile.cs:66
FluentFTP\Model\FtpResult.cs:15
FluentFTP\Model\FtpResult.cs:20
FluentFTP\Model\FtpResult.cs:25
FluentFTP\Model\FtpResult.cs:30
FluentFTP\Model\FtpResult.cs:45
FluentFTP\Model\FtpResult.cs:50
FluentFTP\Model\FtpResult.cs:55
FluentFTP\Model\FtpResult.cs:60
FluentFTP\Model\FtpResult.cs:65
FluentFTP\Model\FtpSizeReply.cs:10
FluentFTP\Model\IntRef.cs:7
FluentFTP\Rules\FtpFileExtensionRule.cs:17
FluentFTP\Rules\FtpFileExtensionRule.cs:22
FluentFTP\Rules\FtpFIleNameRegexRule.cs:19
FluentFTP\Rules\FtpFIleNameRegexRule.cs:24
FluentFTP\Rules\FtpFileNameRule.cs:16
FluentFTP\Rules\FtpFileNameRule.cs:21
FluentFTP\Rules\FtpFolderNameRegexRule.cs:19
FluentFTP\Rules\FtpFolderNameRegexRule.cs:24
FluentFTP\Rules\FtpFolderNameRegexRule.cs:29
FluentFTP\Rules\FtpFolderNameRule.cs:13
FluentFTP\Rules\FtpFolderNameRule.cs:24
FluentFTP\Rules\FtpFolderNameRule.cs:29
FluentFTP\Rules\FtpFolderNameRule.cs:34
FluentFTP\Rules\FtpSizeRule.cs:16
FluentFTP\Rules\FtpSizeRule.cs:21
FluentFTP\Rules\FtpSizeRule.cs:26

FtpSocketStream.SslStream is unused

Unused return values
Use the return value of method 'Trim'.	FluentFTP\Helpers\Parsers\NonStopParser.cs:59
Use the return value of method 'Parse'.	FluentFTP\Helpers\Parsers\VMSParser.cs:96

DO NOT provide instance fields that are public or protected

Yes I never understood why C# forced the slower properties to be used instead of public fields. Most of the uses here are just plain POCOs which don't really need properties.

A benefit of properties is that in debug mode, you can set breakpoints on them.
In release mode the call to the property is inlined, so the generated assembly is identical whether you use a field with or without a an explicit backing field.
SharpLab

@jnyrup Ok, fixed all issues except for the last 2 (return values), they are valid, or I cannot figure it out.
Release https://www.nuget.org/packages/FluentFTP/40.0.0-beta5
Guys please check and confirm it works, so I can release the final nuget.

A benefit of properties is that in debug mode, you can set breakpoints on them.

Something one takes for granted until you need to find out from whence came an update, or can you not break-point on a value (i.e. a field) changing its value?

In release mode the call to the property is inlined, so the generated assembly is identical whether you use a field with or without a an explicit backing field.

This is a profoundly valuable statement that reconciles me with the tediousness of making "quasi" unneeded properties.

@robinrodricks Newest master works for me
One point:
If I compare, file by file, the Async client with the Sync client, there are subtle differences in the code. See the following screenshot of ExamDiff on Authenticate.cs just a an example.

There are actually quite a few of these. Worth looking at, file by file (61 files)?

Should I release the v40 nuget

I am just a single one of your users. Who am I to decide. You are raring to go and the changes are sensible, the code is much nicer this way, it becomes much easier (See my "ExamDiff" on previous post) to see any SYNC-ASYNC discrepancies, much less "#if"finess in the code.
I needed a small amount of code modifications. Users might gripe and grouch, but they gain a logging framework or can use legacy logging, they get cleaner code leading to better maintenance. If you follow through with handling their issues, they will see the upside of the V39-V40 switch.
So ok, go for it... IMHO

@jnyrup

In release mode the call to the property is inlined, so the generated assembly is identical whether you use a field with or without a an explicit backing field.

I was not aware of this.

Published v40!
https://www.nuget.org/packages/FluentFTP/40.0.0
Now to wait for the coming storm (of issues!)
Thanks to ALL who participated in this thread. I am closing it, we can take the remaining features or any new issues as part of new discussions.