I'm really getting stuck on how to design my program, in simple term, it needs to create a list of file in a given path and then sorts them for now by date creating the respective subdirectory. The problem arises since the files are uploaded by the phone in a NAS and their creation date gets modified when uploaded to this drive. Since we are talking about photos-video or audio I tried using metadata and the best way I found to retrieve some common date stored in the metadata based on this answer is this:
internal static class FileInSorting
{
private static List<string> arrHeaders;
private static List<int> date = new List<int>() { 197, 3, 4, 5, 12, 198, 287, 208 };
private static List<FileToSort> fileToSort = new List<FileToSort>();
public static List<FileToSort> GetFilesToSort(string path)
{
Folder objFolder;
LoadHeader(path, out arrHeaders, out objFolder);
//I search for each file inside his extended property for the true creation date
//If none is found I default to what FileInfo thinks is right
foreach (Shell32.FolderItem2 item in objFolder.Items())
{
List<DateTime> tmp = new List<DateTime>();
DateTime SelectedDate;
foreach (int h in date)
{
string l = objFolder.GetDetailsOf(item, h);
if (!string.IsNullOrEmpty(l))
{
string asAscii = Encoding.ASCII.GetString(
Encoding.Convert(
Encoding.UTF8,
Encoding.GetEncoding(
Encoding.ASCII.EncodingName,
new EncoderReplacementFallback(string.Empty),
new DecoderExceptionFallback()),
Encoding.UTF8.GetBytes(l)
)
);
tmp.Add(DateTime.Parse(asAscii.Substring(0, 11)));
}
}
if (tmp.Count == 0)
SelectedDate = File.GetCreationTime(item.Path);
else
SelectedDate = tmp.Min();
fileToSort.Add(new FileToSort(item.Name, item.Path, SelectedDate));
}
return fileToSort;
}
public static void LoadHeader(string path, out List<string> arrHeaders, out Folder objFolder)
{
arrHeaders = new List<string>();
Shell32.Shell shell = new Shell32.Shell();
objFolder = shell.NameSpace(path);
for (int i = 0; i < short.MaxValue; i++)
{
string header = objFolder.GetDetailsOf(null, i);
if (!String.IsNullOrEmpty(header))
arrHeaders.Add(header);
}
}
}
I made this class just for easy use during sort but it could be completely redundant
public class FileToSort
{
public string nome { get; set; }
public string path { get; set; }
public DateTime sortDate { get; set; }
public FileToSort(string nome,string path,DateTime data)
{
this.nome = nome;
this.path = path;
this.sortDate = data;
}
}
The problem using this COM object is that is slow and not so easy to handle(maybe I'm just not able to) and as turned out on another question of mine it's not thread-safe, blocking out the option for parallel operation on multiple folders after the first sort.
For example, i'm first sorting all files in a tree structure "[YEAR]/[Month]/[Full date]" but then I would have to recreate the COM object for each "Full date" folder and sort those by type. I'm aware that after the first date sort I could start using Directory.EnumerateFile() for each of the newly created folders but I would like to see if there is a better way to "design" the code so it can be reused without writing 2 separate methods for the date sort and for the type sort, so is there a way to avoid using the Com object entirely?
Quick edit I forgot another why I'm searching for another solution:
this is a WPF application and I would really like to use a ListView binded with a single collection perhaps a FileInfo collection
The problem arises since the files are in a network and their creation date gets modified when uploaded
That's your choice, and thus your problem. If you don't want file dates to change on upload, don't change them. Windows Explorer, for example, doesn't change them by default, you get the same creation date as the source. Your own code has full access over what dates to use.
I made this class just for easy use during sort but it could be completely redundant
You should look up record. And proper .Net naming conventions (public properties should be capitalized).
it's not thread-safe, blocking out the option for parallel operation on multiple folders after the first sort
You're jumping to assumptions here. It may not be thread-safe, but nothing stops you from creating multiple objects to query through, one for each thread. Look up thread-local variables and/or statics.
but then I would have to recreate the COM object for each "Full date" folder and sort those by type
That line is a little harder to understand, but if you're saying you "need" to requery the entire filesystem again just to sort items then you're dead wrong. Sorting is a view operation, the model doesn't care about it, and what you're writing here is the model. Sorting for the view can be handled any way you want, you have the data in memory already, sort as you wish.
And I don't wish to go through your code too deep, but holy wow what is this:
string asAscii = Encoding.ASCII.GetString(
Encoding.Convert(
Encoding.UTF8,
Encoding.GetEncoding(
Encoding.ASCII.EncodingName,
new EncoderReplacementFallback(string.Empty),
new DecoderExceptionFallback()),
Encoding.UTF8.GetBytes(l)
)
);
If I had to rate it you'd be fired by the time I counted to 0... Just use the original string, what are you doing, man?
Related
Basically I'm trying to put a result of a query into a file path, into the image source.
Updated code. Now I actually get the string I want but I still have trouble with interpolating it into the Uri constructor.
Random rand = new Random();
var nrand = rand.Next(1, 4);
using (var db = new DungeonContext())
{
var query = db.Rooms.Where(b => b.RoomId == nrand).Select(b => b.RoomName);
foreach(string name in query)
{
nameOfRoom = name;
}
}
Somehow I have trouble setting images dir as Resources since I'm missing advancent properties for this folder.
roomScreen.Source = new BitmapImage(new Uri($"/images/{nameOfRoom}.png", UriKind.Relative));
Making Uri relative seemed to stop throwing exceptions, but the intended effect does not happen.
The way you're creating the URI is correct. The trouble is that you're trying to use what is likely meant to be readable text (the name of a room) in a URI and as a flle name. You didn't specify what nameOfRoom is, so I can't tell exactly why it's invalid.
There are ways around it. You could look at this answer which explains how to remove invalid characters from a file name.
But the best solution is not to have the problem. Don't use the name of the room as a file name. Your question indicates that a room has an ID, which is apparently an integer. Using that instead would be much easier:
using (var db = new DungeonContext())
{
room = db.Rooms.Where(b => b.RoomId == nrand);
}
and then
roomScreen.Source = new BitmapImage( new Uri($"/images/Room{room.RoomId}.png"));
If you want to use your code as-is, that's possible. When you do this:
nameOfRoom = db.Rooms.Where(b => b.RoomId == nrand).ToString();
you're calling ToString() on what is presumably a Room class. Unless you override that method, the result will always be the name of the class. (So it would be the same for every room.)
If you overrode the ToString method:
public override string ToString()
{
return $"Room{RoomId}";
}
then calling room.ToString() would return "Room1" or "Room2", etc.
String interpolation calls ToString(), so in that case you could just do
roomScreen.Source = new BitmapImage( new Uri($"/images/{room}.png"));
That works. One downside is that it's not explicit. Someone reading that line of code might not know that Room.ToString() is also the value used to create a file name. You might also want to use the ToString() method differently.
If you wanted to be really explicit and clear, you could create an extension method like this:
public static class RoomFileNameExtensions
{
public static string GetImageFileName(this Room room)
{
return $"Room{room.RoomId}.png";
}
}
Now the Room class isn't responsible for knowing how file names are created. You could do this:
roomScreen.Source = new BitmapImage( new Uri($"/images/{room.GetImageFileName()}"));
You can use that both when creating the file and when reading the file. It ensures that both operations will determine the file name the same way.
Apparently all I needed to do was add UriKInd.RelativeOrAbsolute to the constructor.
If it's in the project directory, utilizing the System.IO.Path namespace comes to mind - something like
Path.Combine(Environment.CurrentDirectory, $"/images/{nameOfRoom}.png")
I'm using protobuf-net in a c# application to load and save my program's 'project files'. At save time, the program creates a ProjectData object and adds many different objects to it - see general principle below.
static ProjectData packProjectData()
{
ProjectData projectData = new ProjectData();
projectData.projectName = ProjectHandler.projectName;
foreach (KeyValuePair<int, Module> kvp in DataHandler.modulesDict)
{
projectData.modules.Add(serializeModule(kvp.Value));
}
return projectData;
}
[ProtoContract]
public class ProjectData
{
[ProtoMember(1)]
public List<SEModule> modules = new List<SEModule>();
[ProtoMember(2)]
public string projectName = "";
}
Once this is created, it's zipped and save to the disk. The problem I am having is that when the number of modules gets very big (40,000+) System.OutOfMemoryException is being reported during the packProjectData function.
I've seen questions like this asked already, but these do not contain a clear answer to address the problem. If anyone can give me either a specific solution, or a general principle to follow that would be greatly appreciated.
What sort of size are we talking about here? Most likely this is due to buffering required for the length prefix - something that v3 will address, but for now - if the file is huge, a pragmatic workaround might be:
[ProtoContract]
public class ProjectData
{
[ProtoMember(1, DataFormat = DataFormat.Grouped)]
public List<SEModule> modules = new List<SEModule>();
[ProtoMember(2)]
public string projectName = "";
}
This changes the internal encoding format of the SEModule items so that no length-prefix is required. This same approach may also be useful for some elements inside SEModule, but I can't see that to comment.
Note that this changes the data layout, so should be considered a breaking change.
This may be a noob question, but I need some help. I have written two simple methods in C#: ReadCsv_IT and GetTranslation. The ReadCsv_IT method reads from a csv file. The GetTransaltion method calls the ReadCsv_IT method and returns the translated input (string key).
My problem is that in the future I will have to request a lot of times GetTranslation, but I obviously don't want to read the .csv files every time. So I was thinking about ways to use cache Memory to optimize my program, so that I don't have to read the .csv file on every request. But I am not sure how to do it and what I could do to optimize my program. Can anyone please help ?
public string ReadCsv_IT(string key)
{
string newKey = "";
using (var streamReader = new StreamReader(#"MyResource.csv"))
{
CsvReader csv = new CsvReader(streamReader);
csv.Configuration.Delimiter = ";";
List<DataRecord> rec = csv.GetRecords<DataRecord>().ToList();
DataRecord record = rec.FirstOrDefault(a => a.ORIGTITLE == key);
if (record != null)
{
//DOES THE LOCALIZATION with the help of the .csv file.
}
}
return newKey;
}
Here is the GetTranslation Method:
public string GetTranslation(string key, string culture = null)
{
string result = "";
if (culture == null)
{
culture = Thread.CurrentThread.CurrentCulture.Name;
}
if (culture == "it-IT")
{
result = ReadCsv_IT(key);
}
return result;
}
Here is also the class DataRecord.
class DataRecord
{
public string ORIGTITLE { get; set; }
public string REPLACETITLE { get; set; }
public string ORIGTOOLTIP { get; set; }
}
}
Two options IMO:
Turn your stream into an object?
In other words:
Make a class stream so you can refer to that object of the class stream.
Second:
Initialize your stream in the scope that calls for GetTranslation, and pass it on as an attribute to GetTranslation and ReadCSV_IT.
Brecht C and Thom Hubers have already given you good advice. I would like to add one more point, though: using csv files for localization in .NET is not really a good idea. Microsoft recommends using a resource-based approach (this article is a good starting point). It seems to me that you are trying to write code for something that is already built into .NET.
From a translation point of view csv files are not the best possible format either. First of all, they are not really standardized: many tools have slightly different ways to handle commas, quotes, and line breaks that are part of the translated text. Besides, translators will be tempted to open them in Excel, and -unless handled with caution- Excel will write out translations in whatever encoding it deems best.
If the project you are working on is for learning please feel free to go ahead with it, but if you are developing software that will be used by customers, updated, translated into several target languages, and redeployed, I would recommend to reconsider your internationalization approach.
#Brecht C is right, use that answer to start. When a variable has to be cached to be used by multiple threads or instances: take a look at InMemoryCache or Redis when perfomance and distribution over several clients gets an issue.
Good evening; I have an application that has a drop down list; This drop down list is meant to be a list of commonly visited websites which can be altered by the user.
My question is how can I store these values in such a manor that would allow the users to change it.
Example; I as the user, decide i want google to be my first website, and youtube to be my second.
I have considered making a "settings" file however is it practical to put 20+ websites into a settings file and then load them at startup? Or a local database, but this may be overkill for the simple need.
Please point me in the right direction.
Given you have already excluded database (probably for right reasons.. as it may be over kill for a small app), I'd recommend writing the data to a local file.. but not plain text..
But preferably serialized either as XML or JSON.
This approach has at least two benefits -
More complex data can be stored in future.. example - while order can be implicit, it can be made explicit.. or additional data like last time the url was used etc..
Structured data is easier to validate against random corruption.. If it was a plain text file.. It will be much harder to ensure its integrity.
The best would be to use the power of Serializer and Deserializer in c#, which will let you work with the file in an Object Oriented. At the same time you don't need to worry about storing into files etc... etc...
Here is the sample code I quickly wrote for you.
using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
public class UrlSerializer
{
private static void Write(string filename)
{
URLCollection urls = new URLCollection();
urls.Add(new Url { Address = "http://www.google.com", Order = 1 });
urls.Add(new Url { Address = "http://www.yahoo.com", Order = 2 });
XmlSerializer x = new XmlSerializer(typeof(URLCollection));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, urls);
}
private static URLCollection Read(string filename)
{
var x = new XmlSerializer(typeof(URLCollection));
TextReader reader = new StreamReader(filename);
var urls = (URLCollection)x.Deserialize(reader);
return urls;
}
}
public class URLCollection : ICollection
{
public string CollectionName;
private ArrayList _urls = new ArrayList();
public Url this[int index]
{
get { return (Url)_urls[index]; }
}
public void CopyTo(Array a, int index)
{
_urls.CopyTo(a, index);
}
public int Count
{
get { return _urls.Count; }
}
public object SyncRoot
{
get { return this; }
}
public bool IsSynchronized
{
get { return false; }
}
public IEnumerator GetEnumerator()
{
return _urls.GetEnumerator();
}
public void Add(Url url)
{
if (url == null) throw new ArgumentNullException("url");
_urls.Add(url);
}
}
}
You clearly need some sort of persistence, for which there are a few options:
Local database
- As you have noted, total overkill. You are just storing a list, not relational data
Simple text file
- Pretty easy, but maybe not the most "professional" way. Using XML serialization to this file would allow for complex data types.
Settings file
- Are these preferences really settings? If they are, then this makes sense.
The Registry - This is great for settings you don't want your users to ever manually mess with. Probably not the best option for a significant amount of data though
I would go with number 2. It doesn't sound like you need any fancy encoding or security, so just store everything in a text file. *.ini files tend to meet this description, but you can use any extension you want. A settings file doesn't seem like the right place for this scenario.
I'll post my entire class and maybe someone with MUCH more experience can help me design something better. I'm really new to doing things Asynchronously, so I'm really lost here. Hopefully my design isn't TOO bad. :P
IMDB Class:
public class IMDB
{
WebClient WebClientX = new WebClient();
byte[] Buffer = null;
public string[] SearchForMovie(string SearchParameter)
{
//Format the search parameter so it forms a valid IMDB *SEARCH* url.
//From within the search website we're going to pull the actual movie
//link.
string sitesearchURL = FindURL(SearchParameter);
//Have a method download asynchronously the ENTIRE source code of the
//IMDB *search* website, and save it to the byte[] "Buffer".
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadDataCompleted);
WebClientX.DownloadDataAsync(new Uri(sitesearchURL));
//Convert the byte[] to a string so we can easily find the *ACTUAL*
//movie URL.
string sitesearchSource = Encoding.ASCII.GetString(Buffer);
//Pass the IMDB source code to method FindInformation() to FIND the movie
//URL.
string MovieURL = FindMovieURL(sitesearchSource);
//Download the source code from the recently found movie URL.
WebClientX.DownloadDataAsync(new Uri(MovieURL));
//Convert the source code to readable string for scraping of information.
string sitemovieSource = Encoding.ASCII.GetString(Buffer);
string[] MovieInformation = ScrapeInformation(sitemovieSource);
return MovieInformation;
}
void WebClientX_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
throw new NotImplementedException();
}
/// <summary>
/// Formats a valid IMDB url for ease of use according to a search parameter.
/// </summary>
/// <param name="sitesearchSource"></param>
/// <returns></returns>
private string FindMovieURL(string sitesearchSource)
{
int Start = sitesearchSource.IndexOf("<link rel=\"canonical\" href=\"");
string IMDBCode = sitesearchSource.Substring((Start + 28), 35);
return IMDBCode;
}
private string[] ScrapeInformation(string Source)
{
string[] Information = new string[5];
Information[0] = FindTitle(Source);
Information[1] = FindDirector(Source);
Information[2] = FindYear(Source);
Information[3] = FindPlot(Source);
Information[4] = FindPoster(Source);
return Information;
}
/*************************************************************************/
private string FindURL(string Search)
{
string[] SearchArray = Search.Split(' ');
string FormattedQuery = "";
foreach (string X in SearchArray)
{
FormattedQuery += X + "+";
}
FormattedQuery.Remove((FormattedQuery.Length - 1), 1);
string TheFormattedQuery = "http://www.imdb.com/find?s=all&q=" + FormattedQuery + "&x=0&y=0";
return TheFormattedQuery;
}
private string FindTitle(string Source)
{
//<title>Couples Retreat (2009)</title>
int Start = Source.IndexOf("<title>");
string Bookmark = Source.Substring((Start + 7), 400);
int End = Bookmark.IndexOf("</title>");
string Title = Bookmark.Substring(0, End - 7);
return Title;
}
private string FindDirector(string Source)
{
int Start = Source.IndexOf("<h5>Director:</h5>");
string Bookmark = Source.Substring((Start + 18), 250);
Start = Bookmark.IndexOf(">");
Bookmark = Bookmark.Substring(Start + 1, 100);
int End = Bookmark.IndexOf("</a>");
string Director = Bookmark.Substring(0, End - 1);
return Director;
}
private string FindYear(string Source)
{
int Start = Source.IndexOf("<h5>Release Date:</h5>");
string Bookmark = Source.Substring((Start + 22), 40);
int End = Bookmark.IndexOf("<a class=");
string ReleaseYear = Bookmark.Substring(0, End - 1);
return ReleaseYear;
}
private string FindPlot(string Source)
{
int Start = Source.IndexOf("<h5>Plot:</h5>");
string Bookmark = Source.Substring((Start + 14), 700);
int End = Bookmark.IndexOf("<a class");
string Plot = Bookmark.Substring(0, End - 1);
return Plot;
}
private string FindPoster(string Source)
{
int Start = Source.IndexOf("<a name=\"poster\" href=");
string Bookmark = Source.Substring((Start + 22), 700);
Start = Bookmark.IndexOf("src=\"");
string PosterURL = Bookmark.Substring((Start + 5), 103);
return PosterURL;
}
}
Form1.cs Class (My windows Form):
public partial class MainSearchForm : Form
{
public MainSearchForm()
{
InitializeComponent();
}
SearchFunctions.IMDB IMDBClass = new QuickFlick.SearchFunctions.IMDB();
int YPosition = 5;
private void btnSearch_Click(object sender, EventArgs e)
{
string[] MovieInformation = IMDBClass.SearchForMovie(txtSearch.Text);
LoadMovieInformation(MovieInformation);
}
public void LoadMovieInformation(string[] FoundInfo)
{
MovieItem TheMovieItem = new MovieItem();
TheMovieItem.SetTitle(FoundInfo[0]);
TheMovieItem.SetDirector(FoundInfo[1]);
TheMovieItem.SetRelease(FoundInfo[2]);
TheMovieItem.SetPlot(FoundInfo[3]);
TheMovieItem.SetPoster(FoundInfo[4]);
TheMovieItem.Location = new Point(5, YPosition);
YPosition += 196;
panel1.Controls.Add(TheMovieItem);
}
}
Now, the gist of what my program is trying to accomplish.
A user will write the name down of a movie, and I'll pull up the information about it. Nothing else! :P It's mostly intended for me to learn Async functions etc. but I'm scared I might be approaching this the completely wrong way.
Once again, I'm not looking for much code, just design of the program. Methods, order of methods, unnecesary methods, etc. :D
Thanks a bunch SO, as always, you rock!
I'm not going to comment on the entire design because, like #Vinko, I think you ought to focus your question a little more in that respect. I will say though that you are fundamentally misunderstanding how the asynchronous methods work. They will return immediately to the calling thread without completing first. That's the whole point with asynchronous threads - they don't wait until they have finished before they return.
For this to work, you must either use synchronous calls or wait on some event after the call and signal that event in the asynchronous callback handler so that your code doesn't attempt to access the returned data until after the call has completed. The benefit in this approach is that your thread is free to perform other tasks while waiting on the call to complete, but you must absorb the complexity of managing the waiting process (it's not much).
See the sample code for the DownloadDataCompleted event at MSDN. You'll need to do a wait for each of the web client downloads that you do. Note that you need to invoke the Set() method on the AutoResetEvent object passed to your handler in the example in your handler.
You need to have models.
For example the FindTitle() and FindDirector() methods should be in a separate class.. perhaps called 'Movie' or 'IMDBMovie'. This class should have attributes such as 'title' and 'director' with getters / setters.
This article could prove helpful to you: MVC
I would agree with clownbaby on the need for models, but would disagree on the need for FindTitle, FindDirectory etc. needing to be in separate classes.
I think that you should structure your code as follows:
Have a class that takes in the search parameters in a generic way and then calls a search through some kind of factory class.
Have the factory class internally create a specialized version of the search class based on either a specification by the user, or a specification in the configuration settings of the application.
Have the search class format the search query and then call another class which simply makes the HTTP request and gets back the HTTP response contents to the search class.
Have the search class then parse the HTTP response to generate the results to be fed back to the class that called the search in Item 1.
The advantages of this sort of approach are that you can then isolate the code that is specialized for processing website specific parsing routines into it's own class and isolate it from the UI as well as utilize a single class to make the HTTP request, regardless of how many websites you want to scrape.
Put another way, the View layer should not care how exactly the data is retrieved and parsed and should have the flexibility to call on various sources of information.
Likewise, the Data layer should not care how the data should be processed and should only be concerned with where to get the data from.
The Domain layer is the one that will sit in the middle, take raw data from the Data layer, parse it and make sense of it and then format it into a shape that the View can understand.
This way, if say you want to also target Yahoo! Movies, Wikipedia or any of the other sources of movie data, you can do that. All you would have to do is add another class in the Domain layer and inform the View that a new source is available in the Domain layer.
Good design is about limiting the impact of changes so that additional features can be built into the application.
Good design is also about being able to test individual components. A design such as the one I have described will allow you to test the various classes that comprise individual layers and ensure that they work as advertised.
Something I find very useful is that if a class cannot be tested very easily using NUnit and Rhino.Mocks, it's probably not very well-designed.
Better to split the model object from the data retrieval object.
User sees View, uses btnSearch_Click
Search starts on other thread, btnSearch_Click action finishes
Search finishes, data may be available, new object creation and addition to view.
View has new data event and redisplays content.
Some adjusted code to do the async calls.
public string[] SearchForMovie(string SearchParameter)
{
//Format the search parameter so it forms a valid IMDB *SEARCH* url.
//From within the search website we're going to pull the actual movie
//link.
string sitesearchURL = FindURL(SearchParameter);
//Have a method download asynchronously the ENTIRE source code of the
//IMDB *search* website, and save it to the byte[] "Buffer".
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadSearchCompleted);
WebClientX.DownloadDataAsync(new Uri(sitesearchURL));
}
void WebClientX_DownloadSearchCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
//Convert the byte[] to a string so we can easily find the *ACTUAL*
//movie URL.
string sitesearchSource = Encoding.ASCII.GetString(Buffer);
//Pass the IMDB source code to method FindInformation() to FIND the movie
//URL.
string MovieURL = FindMovieURL(sitesearchSource);
//Download the source code from the recently found movie URL.
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadMovieCompleted);
WebClientX.DownloadDataAsync(new Uri(MovieURL));
}
void WebClientX_DownloadMovieCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
//Convert the source code to readable string for scraping of information.
string sitemovieSource = Encoding.ASCII.GetString(Buffer);
// would create a movie object here rather than have the scrape function on this class
string[] MovieInformation = ScrapeInformation(sitemovieSource);
Model.LoadMovieInformation(MovieInformation);
}