Repository navigation
Closed
Description
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 transferredQuickTransferLimit- Implemented but disabledFtpClient.Connect(Uri)- Static constructorsFtpClient.GetPublicIP()- Static methodsDereferenceLink- 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
FtpClientclass 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()becomesclient.Connect(FtpProfile)client.FXPProgressIntervalbecomesclient.FXP.ProgressIntervalclient.DownloadZeroByteFilesbecomesclient.Download.ZeroByteFilesclient.TransferChunkSizebecomesclient.Download.ChunkSizeandclient.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
GetWorkingDirectoryandSetWorkingDirectoryand 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
FtpTraceand its outdated API and instead support aclient.Loggerproperty 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:
NoTimeConversionTimeZoneConversion- 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
IAsyncResultstyle 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.
Activity
robinrodricks commentedon Aug 5, 2022
@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
Adhara3 commentedon Aug 5, 2022
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
IFtpClientLoggerinterface 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
robinrodricks commentedon Aug 6, 2022
Topic 3/4/5: Agreed (more or less)
Forward;
It sounds pretty cool and could fix issues by offering simple and complex strategies. Assuming we have just one FtpClient for simplicity and all features are implemented in modular strategies, 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
SimpleFileDownloadandChunkedFileDownloadetc. 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.Adhara3 commentedon Aug 6, 2022
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):
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
BasicDownloadStrategymay 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
FTPClientconstructor and he should have something working. Basically, you would end up having some core classes + a bunch of add-ons (each client).Moreover, async is in the syntax, so it doesn't work, but a Download strategy could be one file at a time and another implementation may be
EnableThreadSafeDataConnections, basically instead of an option, it's a behaviour.Topic 5 would also fall into this. You provide the API (i.e. the interface) and, if you like, a couple of very basic implementations, but then a user can always customize if required by injecting a different implementation.
The hard part is:
IDownloadStrategymay not be enoght, you may need to split it (or to provide parts to it) likeIPathBuildingStrategyorIServerResponseStrategy, just to give an ideaFanDjango commentedon Aug 6, 2022
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
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#?
robinrodricks commentedon Aug 7, 2022
Topic 3
Maybe we can have a strategy for this?
ManualDirectoryStrategy- the current get/set PWD/CWDAutomaticDirectoryStrategy- a new one which can auto set the dir based on the file commandsNames 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> <Subject> "Strategy"seems like a very Java-esque too-many-words naming scheme and I would like a better one!For backwards compatibility, we could support all the older constructors, which would be implemented like:
Topic 6
Same for me, however lots of contributors were excited to contribute async versions for some reason... And it makes the library look more "modern". I don't know why users can't just create a background worker/thread and use the sync API, maybe its because of the whole node.js "everything is an async/non-blocking operation" pattern that took the world by storm.
Adhara3 commentedon Aug 7, 2022
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
robinrodricks commentedon Aug 7, 2022
No, that would not solve the main problem of duplicating code.
hez2010 commentedon Aug 7, 2022
I would like to see
FooAsync().ConfigureAwait(false).GetAwaiter().GetResult().IAsyncEnumerablesupport again to support "list streaming", without the dependency of Rx/Lx.Net.132 remaining items
FanDjango commentedon Sep 4, 2022
Latest master works for me. LogUserName and LogPassword: works also
robinrodricks commentedon Sep 4, 2022
Should I release the v40 nuget @FanDjango and @jnyrup ?
jnyrup commentedon Sep 4, 2022
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
There are 77 violations of that guideline
list of files
FtpSocketStream.SslStreamis unusedUnused return values
robinrodricks commentedon Sep 4, 2022
Yes I never understood why C# forced the slower properties to be used instead of public fields. It actually has a negative effect on run time performance (may be negligible). Most of the uses here are just plain POCOs which don't really need properties.
jnyrup commentedon Sep 4, 2022
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
robinrodricks commentedon Sep 4, 2022
@jnyrup Ok, agreed that we should follow guidelines as it makes the lib more professional.
I 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.
FanDjango commentedon Sep 4, 2022
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?
This is a profoundly valuable statement that reconciles me with the tediousness of making "quasi" unneeded properties.
FanDjango commentedon Sep 4, 2022
@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.csjust a an example.There are actually a few of these, some only the comments, but some more than just that. Worth looking at, file by file (61 files)?
FanDjango commentedon Sep 4, 2022
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
So, I am finished with this thread and will move on to other topics on my Todo-List. I thank all for the fruitful discussions and I learned a lot also.
robinrodricks commentedon Sep 5, 2022
Thank you!
Seems like a bug.
You can go ahead if you have time, sorry I won't have time this week. Beyond Compare would make quick work of it.
I'll wait some days and then release.
robinrodricks commentedon Sep 7, 2022
@jnyrup
I was not aware of this.
robinrodricks commentedon Sep 7, 2022
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.