Clean approach to finding a folder's parent - c#

I have this piece of code that searches for a folder given a starting directory. Once the folder is found I need the name of it's parent. The following piece of code works but it is terribly ugly. I have a flag, "sessionFound" to assist with breaking from nested foreach loops. The following works. I was hoping I could get some eyes on this and see if I could get some suggestions on how to make this less verbose and a bit more concise.
Thanks.
private void SetProjectFolder(string sessionid)
{
//IoC container gives the root directory to begin search.
string[] supportDirs = Directory.GetDirectories(ApplicationContainer.SupportDirectory);
bool sessionFound = false;
foreach (string directory in supportDirs)
{
if (!sessionFound)
{
foreach (string folder in Directory.GetDirectories(directory))
{
if (!sessionFound)
{
foreach (string productSubFolder in Directory.GetDirectories(folder))
{
if (productSubFolder.Contains(sessionid))
{
_productName = Directory.GetParent(productSubFolder).Parent.Name;
sessionFound = true;
break;
}
}
}
else
{
break;
}
}
}
else
{
break;
}
}
}

Try this:
private void SetProjectFolder(string sessionid)
{
//This will simulate the contains statement
string searchPattern = string.Format("*{0}*", sessionid);
string[] supportDirs = Directory.GetDirectories(ApplicationContainer.SupportDirectory, searchPattern);
foreach (string filteredFolder in supportDirs)
{
_productName = Directory.GetParent(filteredFolder).Name;
break;
}
}

By removing sessionFound you end up with a pretty clear and straightforward code:
private void SetProjectFolder(string sessionid)
{
//IoC container gives the root directory to begin search.
string[] supportDirs = Directory.GetDirectories(ApplicationContainer.SupportDirectory);
// search for product subfolder
foreach (string directory in supportDirs)
{
foreach (string folder in Directory.GetDirectories(directory))
{
foreach (string productSubFolder in Directory.GetDirectories(folder))
{
if (productSubFolder.Contains(sessionid))
{
// product sub-folder found, set it and exit
_productName = Directory.GetParent(productSubFolder).Parent.Name;
return;
}
}
}
}
// product sub-folder not found
!!! handle error path
}
In case your directory structure is static in the sense of the abovementioned search algorithm, I don't think there's something wrong with the nested foreach statements. Maybe you could extract the code starting with the middle foreach into a separate method like string FindProductSubfolderInFolder(Directory).

Related

Handle network fail while copying files in C#

I am writing a C# app that copy files over a network, the problem is that the size of the files and folders to copy is more than 1 TB.
My method is as follows
public static void SubmitDocsToRepository(string p_FilePaths)
{
IEnumerable<(string,string)> directoryLevels = GetAllFolders(p_FilePaths);
IEnumerable<(string,string)> filesLevels = GetAllFiles(p_FilePaths);
foreach (var tuple in directoryLevels)
Folder copy logic
foreach (var tuple in filesLevels)
File copy logic
}
Which would work fine, but assuming something would happen to the network or remote server or the electric power gets lost for whatever reason what should I add to this code to allow me to continue where I left off, especially how could I retrace my steps to where I was.
It could be something like this:
public static void SubmitDocsToRepository(string p_FilePaths)
{
IEnumerable<(string, string)> directoryLevels = GetAllFolders(p_FilePaths);
IEnumerable<(string, string)> filesLevels = GetAllFiles(p_FilePaths);
foreach (var tuple in directoryLevels)
while (!CopyDirectory(tuple)) ;
foreach (var tuple in filesLevels)
while (!CopyFile(tuple)) ;
}
static bool CopyDirectory((string, string)tuple)
{
try
{
// Copy logic
}
catch
{
// Some logging here
return false;
}
return true;
}
static bool CopyFile((string, string) tuple)
{
try
{
// Copy logic
}
catch
{
// Some logging here
return false;
}
return true;
}

How to validate if a folder already exists

I want to create folders and add classes within the folders. I can create the folder once, but once I have created it, I just want to add classes. My code gives error because I'm trying to create a folder several times, which is not right. So before adding a class to the folder, I want to check if the folder already exists and if it exists I just want to add the class.
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
According to documentation, there is no kind of Exists function which would tell us if a folder already existed.
So you have at least two options:
1. Try and ignore
Simply:
try
{
var rootFolder = project.ProjectItems.AddFolder(folder);
}
catch
{
/* folder already exists, nothing to do */
}
2. Solution folders can only occur in the first level below the solution root node, so we can get away with a non-recursive solution.
public static bool CheckIfSolutionFolderExists(ProjectItems projectItems, string foldername)
{
foreach(var projectItem in projectItems)
{
if(projectItem.Kind == EnvDTE.vsProjectItemKindVirtualFolder)
{
if(projectItem.Name == foldername)
{
return true;
}
}
}
return false;
}
For a recursive solution, I found this, which boils down to:
public static bool CheckIfFileExistsInProject(ProjectItems projectItems, string fullpath)
{
foreach(ProjectItem projectItem in projectItems)
{
if(projectItem.Name == fullpath)
{
return true;
}
else if ((projectItem.ProjectItems != null) && (projectItem.ProjectItems.Count > 0))
{
/* recursive search */
return CheckIfFileExistsInProject(projectItem.ProjectItems, fullpath);
}
}
return false;
}
A robust pattern for solution and project folder management would use AddFromDirectory whenever you want to mirror the file system hierarchy in the project tree, and AddFolder only for virtual folders that have no representation in the file system.
ProjectItem item;
if(Directory.Exists(itemname))
{
item = project.AddFromDirectory(itemname);
}
else
{
item = project.AddFolder(itemname);
}
(source: inspired from this)
You can use method Directory.Exists
and in your code it'll be look like there
if(!Directory.Exists(folder)) {
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
}
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
You need to do the following:
if(Directory.Exists(path))
{
// The folder already exists
}
else
{
//Create a new folder here ...
}
Please check this out for more details on Directory.Exists

Delete file from folder where contains

I am looking to create a Method which I can use to delete files from a directory when the filename contains the wildcard parameter.
So far I have the below but I cannot seem to work out how I can delete any files from my fileEntries collection that contain the wildcard.
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
// Process the list of ALL files found in the directory.
string[] fileEntries = Directory.GetFiles(targetDirectory);
foreach (var item in fileEntries)
{
var itemToDelete = fileEntries.Contains(wildcard);
// delete where contains
}
}
Can anyone finish this or suggest a more efficient way of doing this in one neat method call which takes the directory and wildcard?
You can use the search-pattern of GetFiles/EnumerateFiles(which can be more efficient):
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
string searchPattern = string.Format("*{0}*", wildcard);
var filesToDelete = Directory.EnumerateFiles(targetDirectory, searchPattern);
foreach (var fileToDelete in filesToDelete)
{
try{
File.Delete(fileToDelete);
}catch(Exception ex){
// log this...
}
}
}
Look at the remarks section for further information.
First off, this line is wrong:
var itemToDelete = fileEntries.Contains(wildcard);
This returns a boolean indicating whether at least one of the filenames is an exact match. What you want is:
var items = fileEntries.Where(name => name.Contains(wildcard));
Second, you don't even need to filter the filenames like this, you can simply use this other GetFiles overload that takes a search pattern.
Finally, you can then use File.Delete to actually delete those files.
This is my first stab at something like this with LINQ, but this worked for me:
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
Directory.GetFiles(targetDirectory).Where(j => j.Contains(wildcard)).ToList().ForEach(i => File.Delete(i));
}
Or this uses a multiline lambda expression to include error handling:
public static void DeleteFileContaining(string targetDirectory, string wildcard)
{
Directory.GetFiles(targetDirectory).Where(j => j.Contains(wildcard)).ToList().ForEach(i =>
{
try
{
File.Delete(i);
}
catch (Exception ex)
{
//do something here on exception.
}
});
}
I would use the wildcard to get only the files that I am interested in.
public static void DeleteFileContaining(string targetDirectory, string wildcard){
string[] fileEntries = Directory.GetFiles(targetDirectory, wildcard);//something like *.jpg
// delete all matching files.
foreach (string f in fileEntries)
{
File.Delete(f);
}
}
I am not sure you want to delete from folder but as you have mentioned you want to remove from fileEntries collection you can achieve by this
fileEntries = fileEntries.Where(a => !a.Contains(wildcard)).ToArray();
You might want to look at the System.IO-Class to delete files (http://msdn.microsoft.com/en-us/library/System.IO(v=vs.110).aspx)

FTP Directory/SubDirectory listing using edtFTPnet,C#

On my FTP Server I have the following folder structure
- Parent Directory
-a.txt
-b.txt.old
-SubDirectory1
-c.txt
-NestedSubDirectory1
-d.txt
-SubDirectory2
-e.txt
-f.txt.old
The number of SDs are not fixed. I need a way to get all the files(can be any format) without the .old extension from the Parent Directory.
I'm currently using the 3rd party dll edtFTPnet.
ftpConnection.GetFileInfos()Where(f => !(f.Name.EndsWith(".old"))).ToList();
This helps me get the details of the files and folders at the current working directory level.
Can someone tell me a way to get all the files with the parentdirectory, subdirectories and nested subdirectories.
The solution may or may not use edtFTPnet.
FTPConnection.GetFileInfos() returns an array of FTPFile. The class FTPFile has a boolean property Dir which indicates whether its filename accesses a file (false) or directory (true).
Something like this should work:
void ReadSubDirectories(FTPConncetion connection, FTPFile[] files)
{
foreach (var file in files)
{
if (file.Dir)
{
// Save parent directory
var curDir = connection.ServerDirectory;
// Move into directory
connection.ChangeWorkingDirectory(file.Name)
// Read all files
ReadSubDirectories(connection, connection.GetFileInfos());
// Move back into parent directory
connection.ChangeWorkingDirectory(curDir)
}
else
{
// Do magic with your files
}
}
}
However you might be better off using just .NET's built-in FtpWebRequest class since its methods and naming conventions are clearer, it's better documented and it's easier to find references online.
Try to use extensions like this:
class Program
{
static void Main(string[] args)
{
using (var connection = new FTPConnection
{
ServerAddress = "127.0.0.1",
UserName = "Admin",
Password = "1",
})
{
connection.Connect();
connection.ServerDirectory = "/recursive_folder";
var resultRecursive =
connection.GetFileInfosRecursive().Where(f => !(f.Name.EndsWith(".old"))).ToList();
var resultDefault = connection.GetFileInfos().Where(f => !(f.Name.EndsWith(".old"))).ToList();
}
}
}
public static class FtpClientExtensions
{
public static FTPFile[] GetFileInfosRecursive(this FTPConnection connection)
{
var resultList = new List<FTPFile>();
var fileInfos = connection.GetFileInfos();
resultList.AddRange(fileInfos);
foreach (var fileInfo in fileInfos)
{
if (fileInfo.Dir)
{
connection.ServerDirectory = fileInfo.Path;
resultList.AddRange(connection.GetFileInfosRecursive());
}
}
return resultList.ToArray();
}
}

conditional foreach loop c#

How can i do this? (c#) Basically, i want to be able to sort of, comment out a bracket. but not really. I guess I want to close brackets out of order. That's probably not possible. I can obviously implement this in full separate if clauses, but this would considerably lighten my code.
P.S.: I am trying to place the "//do something" code, either in a foreach loop, or for a single instance with an unrelated (to that of the foreach loop) argument, depending on a condition. I hope this helps clear it up.
pseudocode (what i'm trying to do, if you could close brackets out of order):
im aware this is no where near valid code, it is pseudocode as i stated.
i havent looked at the second post yet, but the first post (Sean Bright), thank you, but the condition is unrelated to the number of entries (there will always be files in the directory, regardless of if i want the loop to execute)
extracting the //dosomething code into a function will work. I'm not sure how i overlooked that. I suppose I was wondering if there was a simpler way, but you are correct. thank you.
if (isTrue)
{
//START LOOP
string [] fileEntries = Directory.GetFiles(LogsDirectory, SystemLogFormat);
foreach(string fileName in fileEntries)
{
using (StreamReader r = new StreamReader(fileName))
{
/* The previous two blocks are not closed */
}
else
{
using (StreamReader r = new StreamReader(SingleFileName))
{
/* The previous block is not closed */
}
// do all sorts of things
if (isTrue)
{
/* Close the two unclosed blocks above */
}
}
}
else
{
/* Close the unclosed block above */
}
}
thanks!
(indents are weird, sorry, its the forums doing)
Refactor the file processing part into a separate function:
public void OriginalFunction() {
if ( isTrue ) {
string [] fileEntries = Directory.GetFiles(LogsDirectory, SystemLogFormat);
foreach(string fileName in fileEntries) {
ProcessFile(fileName);
}
} else {
ProcessFile(SingleFileName);
}
}
public void ProcessFile( string name ) {
using (StreamReader r = new StreamReader(name))
{
// Do a bunch of stuff
}
}
Why not just do this:
string [] fileEntries = null;
if (isTrue) {
fileEntries = Directory.GetFiles(LogsDirectory, SystemLogFormat);
} else {
fileEntries = new string [] { SingleFileName };
}
foreach(string fileName in fileEntries) {
using (StreamReader r = new StreamReader(fileName)) {
/* Do whatever */
}
}
Use your conditional to determine how you're going to create your StreamReader, then pass that in the using() like normal.

Categories

Resources