Get the file count w/o counting, VS.Net

I need to get the file count of a folder, but I don't want to go about the clumsy method of counting them.
Found some examples of  "GetFirstFile" and loop w/"GetNextFile", but this is still counting.

Is there not a method of just querying the file structure or FAT table for the number or files a folder contains ?

Thanks
sidwelleAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Najam UddinCommented:
Assuming you are not considering GetFiles() as looping

directory.GetFiles(@"folderpath", "*.dll", SearchOption.AllDirectories).Length;

Open in new window

0
AndyAinscowFreelance programmer / ConsultantCommented:
>>Is there not a method of just querying the file structure or FAT table for the number or files a folder contains ?

Still involves counting (and rather more awkward counting at that)
0
sidwelleAuthor Commented:
Najam, I think under the hood the method still counts ??
0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Najam UddinCommented:
Yes it does. I never came across any win api that gives count directly. However if ever come across performance issue while looping for files DirectoryInfo.EnumerateFiles helps a lot as it does not wait up for file select process to complete
0
Jacques Bourgeois (James Burger)PresidentCommented:
The FAT table does not record the count. It needs to be analysed to get the count. So, no matter what you do, there will be a loop somewhere that retrieves the information and accumulates the count.
0
it_saigeDeveloperCommented:
As stated by others, there is really no magic method or property that contains the count of files that does not require looping through the contents of the tree(s).

There are, however, methods that can be used to speed up the process.  If you are using .NET 4 or above, then I would recommend Directory.EnumerateFiles.  

Otherwise, using 3.5 and below requires that you use the Win32 API in order to get a fast count of the files in a directory structure.  One project that discusses this is on CodeProject [http://www.codeproject.com/Articles/38959/A-Faster-Directory-Enumerator], from which I have derived the following example -
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;

namespace EE_Q28859179
{
	class Program
	{
		static Stopwatch watch = new Stopwatch();

		static void Main(string[] args)
		{
			watch.Start();
			var count = (from FileData f in FastDirectoryEnumerator.EnumerateFiles("C:\\DHTI", "*.*", SearchOption.AllDirectories) select f).Count();
			watch.Stop();
			Console.WriteLine("Using the FastDirectoryEnumerator to enumerate the DHTI directory tree took {0} ms; Counted {1} files.", watch.ElapsedMilliseconds, count);

			watch.Reset();
			Console.WriteLine();

			watch.Start();
			var count2 = Directory.EnumerateFiles("C:\\DHTI", "*.*", SearchOption.AllDirectories).Count();
			watch.Stop();
			Console.WriteLine("Using Directory.EnumerateFiles to enumerate the DHTI directory tree took {0} ms; Counted {1} files.", watch.ElapsedMilliseconds, count2);

			watch.Reset();
			Console.WriteLine();

			watch.Start();
			var count3 = Directory.GetFiles("C:\\DHTI", "*.*", SearchOption.AllDirectories).Count();
			watch.Stop();
			Console.WriteLine("Using Directory.GetFiles to enumerate the DHTI directory tree took {0} ms; Counted {1} files.", watch.ElapsedMilliseconds, count3);

			Console.ReadLine();
		}
	}

	/// <summary>
	/// Contains information about a file returned by the 
	/// <see cref="FastDirectoryEnumerator"/> class.
	/// </summary>
	[Serializable]
	public class FileData
	{
		/// <summary>
		/// Attributes of the file.
		/// </summary>
		public readonly FileAttributes Attributes;

		public DateTime CreationTime
		{
			get { return this.CreationTimeUtc.ToLocalTime(); }
		}

		/// <summary>
		/// File creation time in UTC
		/// </summary>
		public readonly DateTime CreationTimeUtc;

		/// <summary>
		/// Gets the last access time in local time.
		/// </summary>
		public DateTime LastAccesTime
		{
			get { return this.LastAccessTimeUtc.ToLocalTime(); }
		}

		/// <summary>
		/// File last access time in UTC
		/// </summary>
		public readonly DateTime LastAccessTimeUtc;

		/// <summary>
		/// Gets the last access time in local time.
		/// </summary>
		public DateTime LastWriteTime
		{
			get { return this.LastWriteTimeUtc.ToLocalTime(); }
		}

		/// <summary>
		/// File last write time in UTC
		/// </summary>
		public readonly DateTime LastWriteTimeUtc;

		/// <summary>
		/// Size of the file in bytes
		/// </summary>
		public readonly long Size;

		/// <summary>
		/// Name of the file
		/// </summary>
		public readonly string Name;

		/// <summary>
		/// Full path to the file.
		/// </summary>
		public readonly string Path;

		/// <summary>
		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </returns>
		public override string ToString()
		{
			return this.Name;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="FileData"/> class.
		/// </summary>
		/// <param name="dir">The directory that the file is stored at</param>
		/// <param name="findData">WIN32_FIND_DATA structure that this
		/// object wraps.</param>
		internal FileData(string dir, WIN32_FIND_DATA findData)
		{
			this.Attributes = findData.dwFileAttributes;


			this.CreationTimeUtc = ConvertDateTime(findData.ftCreationTime_dwHighDateTime,
										 findData.ftCreationTime_dwLowDateTime);

			this.LastAccessTimeUtc = ConvertDateTime(findData.ftLastAccessTime_dwHighDateTime,
										 findData.ftLastAccessTime_dwLowDateTime);

			this.LastWriteTimeUtc = ConvertDateTime(findData.ftLastWriteTime_dwHighDateTime,
										 findData.ftLastWriteTime_dwLowDateTime);

			this.Size = CombineHighLowInts(findData.nFileSizeHigh, findData.nFileSizeLow);

			this.Name = findData.cFileName;
			this.Path = System.IO.Path.Combine(dir, findData.cFileName);
		}

		private static long CombineHighLowInts(uint high, uint low)
		{
			return (((long)high) << 0x20) | low;
		}

		private static DateTime ConvertDateTime(uint high, uint low)
		{
			long fileTime = CombineHighLowInts(high, low);
			return DateTime.FromFileTimeUtc(fileTime);
		}
	}

	/// <summary>
	/// Contains information about the file that is found 
	/// by the FindFirstFile or FindNextFile functions.
	/// </summary>
	[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto), BestFitMapping(false)]
	internal class WIN32_FIND_DATA
	{
		public FileAttributes dwFileAttributes;
		public uint ftCreationTime_dwLowDateTime;
		public uint ftCreationTime_dwHighDateTime;
		public uint ftLastAccessTime_dwLowDateTime;
		public uint ftLastAccessTime_dwHighDateTime;
		public uint ftLastWriteTime_dwLowDateTime;
		public uint ftLastWriteTime_dwHighDateTime;
		public uint nFileSizeHigh;
		public uint nFileSizeLow;
		public int dwReserved0;
		public int dwReserved1;
		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
		public string cFileName;
		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
		public string cAlternateFileName;

		/// <summary>
		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </returns>
		public override string ToString()
		{
			return "File name=" + cFileName;
		}
	}

	/// <summary>
	/// A fast enumerator of files in a directory.  Use this if you need to get attributes for 
	/// all files in a directory.
	/// </summary>
	/// <remarks>
	/// This enumerator is substantially faster than using <see cref="Directory.GetFiles(string)"/>
	/// and then creating a new FileInfo object for each path.  Use this version when you 
	/// will need to look at the attibutes of each file returned (for example, you need
	/// to check each file in a directory to see if it was modified after a specific date).
	/// </remarks>
	public static class FastDirectoryEnumerator
	{
		/// <summary>
		/// Gets <see cref="FileData"/> for all the files in a directory.
		/// </summary>
		/// <param name="path">The path to search.</param>
		/// <returns>An object that implements <see cref="IEnumerable{FileData}"/> and 
		/// allows you to enumerate the files in the given directory.</returns>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="path"/> is a null reference (Nothing in VB)
		/// </exception>
		public static IEnumerable<FileData> EnumerateFiles(string path)
		{
			return FastDirectoryEnumerator.EnumerateFiles(path, "*");
		}

		/// <summary>
		/// Gets <see cref="FileData"/> for all the files in a directory that match a 
		/// specific filter.
		/// </summary>
		/// <param name="path">The path to search.</param>
		/// <param name="searchPattern">The search string to match against files in the path.</param>
		/// <returns>An object that implements <see cref="IEnumerable{FileData}"/> and 
		/// allows you to enumerate the files in the given directory.</returns>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="path"/> is a null reference (Nothing in VB)
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="filter"/> is a null reference (Nothing in VB)
		/// </exception>
		public static IEnumerable<FileData> EnumerateFiles(string path, string searchPattern)
		{
			return FastDirectoryEnumerator.EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
		}

		/// <summary>
		/// Gets <see cref="FileData"/> for all the files in a directory that 
		/// match a specific filter, optionally including all sub directories.
		/// </summary>
		/// <param name="path">The path to search.</param>
		/// <param name="searchPattern">The search string to match against files in the path.</param>
		/// <param name="searchOption">
		/// One of the SearchOption values that specifies whether the search 
		/// operation should include all subdirectories or only the current directory.
		/// </param>
		/// <returns>An object that implements <see cref="IEnumerable{FileData}"/> and 
		/// allows you to enumerate the files in the given directory.</returns>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="path"/> is a null reference (Nothing in VB)
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="filter"/> is a null reference (Nothing in VB)
		/// </exception>
		/// <exception cref="ArgumentOutOfRangeException">
		/// <paramref name="searchOption"/> is not one of the valid values of the
		/// <see cref="System.IO.SearchOption"/> enumeration.
		/// </exception>
		public static IEnumerable<FileData> EnumerateFiles(string path, string searchPattern, SearchOption searchOption)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			if (searchPattern == null)
			{
				throw new ArgumentNullException("searchPattern");
			}
			if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories))
			{
				throw new ArgumentOutOfRangeException("searchOption");
			}

			string fullPath = Path.GetFullPath(path);

			return new FileEnumerable(fullPath, searchPattern, searchOption);
		}

		/// <summary>
		/// Gets <see cref="FileData"/> for all the files in a directory that match a 
		/// specific filter.
		/// </summary>
		/// <param name="path">The path to search.</param>
		/// <param name="searchPattern">The search string to match against files in the path.</param>
		/// <returns>An object that implements <see cref="IEnumerable{FileData}"/> and 
		/// allows you to enumerate the files in the given directory.</returns>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="path"/> is a null reference (Nothing in VB)
		/// </exception>
		/// <exception cref="ArgumentNullException">
		/// <paramref name="filter"/> is a null reference (Nothing in VB)
		/// </exception>
		public static FileData[] GetFiles(string path, string searchPattern, SearchOption searchOption)
		{
			IEnumerable<FileData> e = FastDirectoryEnumerator.EnumerateFiles(path, searchPattern, searchOption);
			List<FileData> list = new List<FileData>(e);

			FileData[] retval = new FileData[list.Count];
			list.CopyTo(retval);

			return retval;
		}

		/// <summary>
		/// Provides the implementation of the 
		/// <see cref="T:System.Collections.Generic.IEnumerable`1"/> interface
		/// </summary>
		private class FileEnumerable : IEnumerable<FileData>
		{
			private readonly string m_path;
			private readonly string m_filter;
			private readonly SearchOption m_searchOption;

			/// <summary>
			/// Initializes a new instance of the <see cref="FileEnumerable"/> class.
			/// </summary>
			/// <param name="path">The path to search.</param>
			/// <param name="filter">The search string to match against files in the path.</param>
			/// <param name="searchOption">
			/// One of the SearchOption values that specifies whether the search 
			/// operation should include all subdirectories or only the current directory.
			/// </param>
			public FileEnumerable(string path, string filter, SearchOption searchOption)
			{
				m_path = path;
				m_filter = filter;
				m_searchOption = searchOption;
			}

			#region IEnumerable<FileData> Members

			/// <summary>
			/// Returns an enumerator that iterates through the collection.
			/// </summary>
			/// <returns>
			/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can 
			/// be used to iterate through the collection.
			/// </returns>
			public IEnumerator<FileData> GetEnumerator()
			{
				return new FileEnumerator(m_path, m_filter, m_searchOption);
			}

			#endregion

			#region IEnumerable Members

			/// <summary>
			/// Returns an enumerator that iterates through a collection.
			/// </summary>
			/// <returns>
			/// An <see cref="T:System.Collections.IEnumerator"/> object that can be 
			/// used to iterate through the collection.
			/// </returns>
			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
			{
				return new FileEnumerator(m_path, m_filter, m_searchOption);
			}

			#endregion
		}

		/// <summary>
		/// Wraps a FindFirstFile handle.
		/// </summary>
		private sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid
		{
			[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
			[DllImport("kernel32.dll")]
			private static extern bool FindClose(IntPtr handle);

			/// <summary>
			/// Initializes a new instance of the <see cref="SafeFindHandle"/> class.
			/// </summary>
			[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
			internal SafeFindHandle()
				: base(true)
			{
			}

			/// <summary>
			/// When overridden in a derived class, executes the code required to free the handle.
			/// </summary>
			/// <returns>
			/// true if the handle is released successfully; otherwise, in the 
			/// event of a catastrophic failure, false. In this case, it 
			/// generates a releaseHandleFailed MDA Managed Debugging Assistant.
			/// </returns>
			protected override bool ReleaseHandle()
			{
				return FindClose(base.handle);
			}
		}

		/// <summary>
		/// Provides the implementation of the 
		/// <see cref="T:System.Collections.Generic.IEnumerator`1"/> interface
		/// </summary>
		[System.Security.SuppressUnmanagedCodeSecurity]
		private class FileEnumerator : IEnumerator<FileData>
		{
			[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
			private static extern SafeFindHandle FindFirstFile(string fileName,
			    [In, Out] WIN32_FIND_DATA data);

			[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
			private static extern bool FindNextFile(SafeFindHandle hndFindFile,
				   [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_DATA lpFindFileData);

			/// <summary>
			/// Hold context information about where we current are in the directory search.
			/// </summary>
			private class SearchContext
			{
				public readonly string Path;
				public Stack<string> SubdirectoriesToProcess;

				public SearchContext(string path)
				{
					this.Path = path;
				}
			}

			private string m_path;
			private string m_filter;
			private SearchOption m_searchOption;
			private Stack<SearchContext> m_contextStack;
			private SearchContext m_currentContext;

			private SafeFindHandle m_hndFindFile;
			private WIN32_FIND_DATA m_win_find_data = new WIN32_FIND_DATA();

			/// <summary>
			/// Initializes a new instance of the <see cref="FileEnumerator"/> class.
			/// </summary>
			/// <param name="path">The path to search.</param>
			/// <param name="filter">The search string to match against files in the path.</param>
			/// <param name="searchOption">
			/// One of the SearchOption values that specifies whether the search 
			/// operation should include all subdirectories or only the current directory.
			/// </param>
			public FileEnumerator(string path, string filter, SearchOption searchOption)
			{
				m_path = path;
				m_filter = filter;
				m_searchOption = searchOption;
				m_currentContext = new SearchContext(path);

				if (m_searchOption == SearchOption.AllDirectories)
				{
					m_contextStack = new Stack<SearchContext>();
				}
			}

			#region IEnumerator<FileData> Members

			/// <summary>
			/// Gets the element in the collection at the current position of the enumerator.
			/// </summary>
			/// <value></value>
			/// <returns>
			/// The element in the collection at the current position of the enumerator.
			/// </returns>
			public FileData Current
			{
				get { return new FileData(m_path, m_win_find_data); }
			}

			#endregion

			#region IDisposable Members

			/// <summary>
			/// Performs application-defined tasks associated with freeing, releasing, 
			/// or resetting unmanaged resources.
			/// </summary>
			public void Dispose()
			{
				if (m_hndFindFile != null)
				{
					m_hndFindFile.Dispose();
				}
			}

			#endregion

			#region IEnumerator Members

			/// <summary>
			/// Gets the element in the collection at the current position of the enumerator.
			/// </summary>
			/// <value></value>
			/// <returns>
			/// The element in the collection at the current position of the enumerator.
			/// </returns>
			object System.Collections.IEnumerator.Current
			{
				get { return new FileData(m_path, m_win_find_data); }
			}

			/// <summary>
			/// Advances the enumerator to the next element of the collection.
			/// </summary>
			/// <returns>
			/// true if the enumerator was successfully advanced to the next element; 
			/// false if the enumerator has passed the end of the collection.
			/// </returns>
			/// <exception cref="T:System.InvalidOperationException">
			/// The collection was modified after the enumerator was created.
			/// </exception>
			public bool MoveNext()
			{
				bool retval = false;

				//If the handle is null, this is first call to MoveNext in the current 
				// directory.  In that case, start a new search.
				if (m_currentContext.SubdirectoriesToProcess == null)
				{
					if (m_hndFindFile == null)
					{
						new FileIOPermission(FileIOPermissionAccess.PathDiscovery, m_path).Demand();

						string searchPath = Path.Combine(m_path, m_filter);
						m_hndFindFile = FindFirstFile(searchPath, m_win_find_data);
						retval = !m_hndFindFile.IsInvalid;
					}
					else
					{
						//Otherwise, find the next item.
						retval = FindNextFile(m_hndFindFile, m_win_find_data);
					}
				}

				//If the call to FindNextFile or FindFirstFile succeeded...
				if (retval)
				{
					if (((FileAttributes)m_win_find_data.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory)
					{
						//Ignore folders for now.   We call MoveNext recursively here to 
						// move to the next item that FindNextFile will return.
						return MoveNext();
					}
				}
				else if (m_searchOption == SearchOption.AllDirectories)
				{
					//SearchContext context = new SearchContext(m_hndFindFile, m_path);
					//m_contextStack.Push(context);
					//m_path = Path.Combine(m_path, m_win_find_data.cFileName);
					//m_hndFindFile = null;

					if (m_currentContext.SubdirectoriesToProcess == null)
					{
						string[] subDirectories = Directory.GetDirectories(m_path);
						m_currentContext.SubdirectoriesToProcess = new Stack<string>(subDirectories);
					}

					if (m_currentContext.SubdirectoriesToProcess.Count > 0)
					{
						string subDir = m_currentContext.SubdirectoriesToProcess.Pop();

						m_contextStack.Push(m_currentContext);
						m_path = subDir;
						m_hndFindFile = null;
						m_currentContext = new SearchContext(m_path);
						return MoveNext();
					}

					//If there are no more files in this directory and we are 
					// in a sub directory, pop back up to the parent directory and
					// continue the search from there.
					if (m_contextStack.Count > 0)
					{
						m_currentContext = m_contextStack.Pop();
						m_path = m_currentContext.Path;
						if (m_hndFindFile != null)
						{
							m_hndFindFile.Close();
							m_hndFindFile = null;
						}

						return MoveNext();
					}
				}

				return retval;
			}

			/// <summary>
			/// Sets the enumerator to its initial position, which is before the first element in the collection.
			/// </summary>
			/// <exception cref="T:System.InvalidOperationException">
			/// The collection was modified after the enumerator was created.
			/// </exception>
			public void Reset()
			{
				m_hndFindFile = null;
			}

			#endregion
		}
	}
}

Open in new window

For frame of reference, DHTI is a massive folder -Capture.JPGThe output I receive when running the above over this folder structure is -Capture.JPGWith each run the times do change but one this is clear, the fastest is using Directory.EnumerateFiles.

-saige-
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sidwelleAuthor Commented:
Ok, I changed my approach. Stayed with the loop, but I set up condition to exit the Do loop if the count exceeds  a set limit.

I don't really need to know how many files are in the folder, just don't want the file count to exceed a set limit.  I set the limit to 1k and the function returns less the 1/2 second as apposed to waiting 6 secs to count every file just to get a count.

Let me know if anyone has a better solution.

Thanks
0
Jacques Bourgeois (James Burger)PresidentCommented:
Usually, what is important on a disk drive is not the number of files but the space they use.

More recent versions of Windows (it started with Vista is my memory is good) enable you to limit the amount of space allocated to each user on a specific disk. You might want to go that route.

Simply right click on the drive in the File Explorer and go in the quota tab to set the limit and optionally have a log created when the quota is exceeded.
0
käµfm³d 👽Commented:
@it_saige

I don't think one test is enough to say (definitively) that one method is faster than another. Any one test could have had interference from the system itself (e.g. context switching, blocked I/O, etc.). It would be better to show executions over a period of time. I'd expect EnumerateFiles to be slower based on my understanding of iterators.

That said, at the lengths of time you show, 10ths of milliseconds seems a tad too small to be concerned about. Then I again, I don't know the OP's environment.
0
AndyAinscowFreelance programmer / ConsultantCommented:
>>just don't want the file count to exceed a set limit.

Just do what you have changed to by exiting when your limit is reached.  You can return true/false to indicate if the max value is reached should that info be required.
0
sidwelleAuthor Commented:
The folder that I am querying is on our HIS (Hospital Information System), the folder is where files are placed to be imported by the system (images, insurance dosc, etc ...). So the folder is effectively a queue.
If so some reason the service that processes those files quits (and it does), its not uncommon to see 50K+ files in that folder. I don't care to count them all, but it the count is over a limit, I am not going to post anymore. I will stop and kick an email to the staff that the Q has exceeded a limit. I also don't have the ability to set a limit or do I want to. Some of the systems posting files are not sophisticated enough to realize there files aren't being posted and may loose those files, or hard to figure out what was posted or not.

I don't care  (it would be nice if it was fast) to wait for a loop to tell me how many,  just if the count has exceeded a limit.
I think the solution with the loop exiting after a limit is reached is probably the best given what I have to work with.
0
AndyAinscowFreelance programmer / ConsultantCommented:
>>If so some reason the service that processes those files quits (and it does)

So wouldn't it be better in that case to make another app (service) which will restart this service should it stop ?
0
sidwelleAuthor Commented:
I don't have access to install services on that server, and if I did I am sure the vendor would remind us that we can't install anything on the server via there contract w/Health system.

No.
0
sidwelleAuthor Commented:
Thanks for answering.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.