I have a url, for example: http://mywebsite.com/another/andanother/test.php I want to be able to split it by / so I can access it in an array like so:
identify[0];
identify[1];
I have a getter and setter like so:
p
How can I achieve what I am looking for?
I get the following error when I try running
Cannot convert string to char
Here's a way to do it, with lazy loading:
private string[] _identify;
public string[] identify
{
get
{
if (_identify == null)
{
_identify = url.Text.Split('/');
}
return _identify;
}
}
Perhaps the existing [Uri class]https://msdn.microsoft.com/en-us/library/system.uri(v=vs.113).aspx) provides what you need.
This:
var uri = new Uri(
"http://scotthannen.org/blog/2017/04/26/dependency-inversion-for-beginners.html");
Console.WriteLine(uri.AbsolutePath);
returns
/blog/2017/04/26/dependency-inversion-for-beginners.html
which you can split by "/".
This:
var uri = new Uri(
"http://scotthannen.org/blog/2017/04/26/dependency-inversion-for-beginners.html");
foreach(var segment in uri.Segments)
{
Console.WriteLine(segment);
}
returns
/
blog/
2017/
04/
26/
dependency-inversion-for-beginners.html
And there are all sorts of other helpful properties and methods for working with urls so you don't have to use all sorts of string methods to parse them.
Related
I have StoreProcedureConstant.cs file where I've defined constants. I am able to call the constants but can't do it with dynamic strings.
StoreProcedureConstant.cs
public const string testSP = "sp_testingtest";
public const string restSP = "sp_test1";
public const string SP1 = "sp_secondtest";
Service.cs
string[] paramlist = {"testSP","restSP","SP1"};
string[] except = {};
var newparams = paramlist.ToDictionary(key => key, value => value);
foreach (var param in newparams.Values)
{
Data = CommonDBService.ExecuteNonQuery(StoreProcedureConstant.param);
if (Data == false)
{
except.Append(param);
}
}
I have this other file Service.cs from where I am calling the constants from StoreProcedureConstant.cs file.
I am not able to call constants like StoreProcedureConstant.param. So I've converted string array to dictionary but it is still not working.
I am new to c# and I think this is not possible. Please help!!
You need to do something like this:
string[] paramlist = {testSP,restSP,SP1};
But it seems like those names are not available (not in scope). You can possibly address this by adding a using for the StoreProcedureConstant namespace at the top of the Service.cs file, but you may also need to clarify the class name as part of the array.
I'm creating a practice application to practice using text files as a database.
Take a pipe delineated text file with 14 People Data Entries
Split the text file on the pipes and add each entry to a add each entry (about 150) to a list of the objects.
I've casted list of objects to a string.
Now I want a button click to display that list in a textbox.
Here is the code.
namespace StaffRosterLewis.ViewModel
{
class LoadData
{
public static void LoadRosterData()
{
string mypath = #"J:\zUtilities - Program Files\";
mypath += "StaffRoster - RosterOld.txt";
List<Model.Person> people = new List<Model.Person>();
List<string> lines = File.ReadAllLines(mypath, Encoding.ASCII).ToList();
foreach (var line in lines)
{
string[] entries = line.Split('|');
Model.Person newPerson = new Model.Person
{
LastName = entries[1],
FirstName = entries[2],
Extension = entries[3],
Department = entries[4],
Team = entries[5],
Group = entries[6],
Title = entries[7],
Shift = entries[8],
EmergencyResponder = entries[9],
AEDCPRCert = entries[10],
Languages = entries[11],
Notary = entries[12],
Note = entries[13],
DutyLocation = entries[14]
};
//newPerson.Unknown15 = entries[15];
people.Add(newPerson);
}
people.ToString();
}
}
}
I think the problem is here where the button click happens. I am simply trying to show the content of people above, but I lose the variable "people" no matter how I try to reference it. The above code is public and I made it static so the people variable should be accessible anywhere within the project. (I thought)
private void Button_Show_Click(object sender, RoutedEventArgs e)
{
Button_Show.Content = $"{LoadData.LoadRosterData.people.ToString()}";
}
There are many problems with your code.
You are trying to access a method like a static property here.
Second, you have return type a void, which should be of type string.
Third you should override the ToString method to return the list items as sting in the required format.
You should use Path.Combine to get the path.
If you are planning to make the people as static variable then you have to accept that it’s not thread safe and make sure you reset it whenever it’s necessary else you may face unexpected items in the list.
And your code will throw exception if you have less than 15 |in a line
You don't need to make LoadRosterData static. However, as mentioned by other commenters the method must return a result (in context of your current usage).
Because reading from a file can be resource consuming, you could increase performance when you store the result in a e.g. public property Peoples.
This allows to access the collection without forcing to read the probably unchanged file. Since this would mean to introduce an instance variable, it's recommended to make LoadRosterData an instance member too.
To create a string from the collection you can use the StringBuilder. The below example uses the StringWriter, which allows asynchronous string creation. StringWriter also uses a StringBuilder internally.
You can further improve the code by overriding ToString of the Person type.
To improve the file reading performance you should use the asynchronous API of the StreamReader instead of the synchronous File class. Using StreamReader.ReadLineAsync will also save you an extra loop.
To make file handling and data model creation in particular more convenient, you should consider to make use of serialization.
The recommended text format would be JSON. See Microsoft Docs: How to serialize and deserialize (marshal and unmarshal) JSON in .NET to learn how to do it. Deserializing the JSON file (preferably asynchronously) will automatically produce a collection of Person items without any hassle (this will eliminate the ugly and fragile index based access (to initialize instance properties) as well as making any delimiters like your pipe '|' redundant).
You are accessing the array starting from index '1'. But in computer science, index always starts from '0'. Not sure if you start from index '1' on purpose.
The following code fixes some issues and implements some performance improvements. It also shows how to convert the collection to a string, where each Person item is displayed in it's own line:
PersonDataReader.cs
class PersonDataReader
{
// Consider to use an ImmutableList to prevent modification.
// In the current context, this property could (and probably should) defined private.
public List<Person> Persons { get; }
// If Person is private, 'HasData' has to be defined private too
public bool HasData => this.Persons.Any();
// Constructor
public PersonDataReader() => this.Persons = new List<Person>();
public async Task<string> CreateRosterSummaryAsync()
{
// Use StringWriter to enable asynchronous string creation.
// StringWriter also uses a StringBuilder internally to improve performance.
using (var textWriter = new StringWriter())
{
if (!this.HasData)
{
await LoadRosterDataAsync();
}
// Alternatively use LINQ,
// for example Enuemrable.Select together with Enumerable.Aggregate
// to concatenate the Person.ToString values
foreach (Person person in this.Persons)
{
string personString = person.ToString();
// Write a Person per line
await textWriter.WriteLineAsync(personString);
}
return textWriter.ToString();
}
}
private async Task LoadRosterDataAsync()
{
this.Persons.Clear();
// Use Path.Combine to ensure a valid formatted path (improve robustness)
string sourcePath = Path.Combine(#"J:\zUtilities - Program Files", "StaffRoster - RosterOld.txt");
// Test if the file actually exists to avoid the expensive exception.
// If the file not found exception is desired, remove the File.Exists condition.
if (File.Exists(sourcePath))
{
return;
}
using (var fileReader = new StreamReaeder(sourcePath, Encoding.ASCII))
{
while (!fileReader.EndOfFile)
{
var line = await reader.ReadLineAsync();
string[] personValues = line.Split('|', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
// If you would use serialization, constructing the type explicitly
// can be avoided. A recommended text format to allow easy de-/serialization is the JSON format.
var newPerson = new Model.Person
{
// Check if starting from index '1' is really correct
LastName = personValues[1],
FirstName = personValues[2],
Extension = personValues[3],
Department = personValues[4],
Team = personValues[5],
Group = personValues[6],
Title = personValues[7],
Shift = personValues[8],
EmergencyResponder = personValues[9],
AEDCPRCert = personValues[10],
Languages = personValues[11],
Notary = personValues[12],
Note = personValues[13],
DutyLocation = personValues[14]
};
this.Persons.Add(newPerson);
}
}
}
}
Person.cs
class Person
{
/* Properties of Person */
// Example text representation of a 'People' object
// using string interpolation
public override string ToString()
=> $"Lastname: {this.LastName}; Firstname: {this.FirstName}; Duty location: {this.DutyLocation}";
}
MainWindow.xaml.cs
partial class MainWindow : Window
{
private PersonDataReader PersonDataReader { get; }
public MainWindow()
{
InitializeComponent();
this.PersonDataReader = new PersonDataReader();
}
private async void Button_Show_Click(object sender, RoutedEventArgs e)
{
string personPerLineText = await this.PersonDataReader.CreateRosterSummaryAsync();
// Why did you chose a Button here? It should be
// this.DisplayTextBox.Text = personPerLineText;
this.Button_Show.Content = personPerLineText;
// Consider to display the complete List<Person> in a ListBox.
}
}
Remarks
A ListBox is probably the better choice to display the list of Person. It's more intuitive to implement and would eliminate the need to worry about string representations and creation.
Your method LoadRosterData has a return value void, means nothing. If you would like to return a string value and use it you may do it like this:
//returns a string
public static string MyStringMethod()
{
string str = "My string";
//Specify the return value with the return keyword
return str;
}
Or if you want to return a list:
public static List<string> MyListMethod()
{
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
return list;
}
You can't do ToString() with list, but you can refer to the index of a person.
And the useage:
button.Content = $"{MyStringMethod()}"
Or if you want to get person by name you can use IndexOf() method:
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
string number = "one";
button.Content = $"list[list.IndexOf(number)]";
I am trying to create a utility class where I could pass a list of Anonymous Type (AT) and it would produce a CSV file with the AT's properties as its columns and property values as its respective data.
I have a working code but I feel it could be improved (a lot!). I inherited a class from FileResult and decorate it with my custom implementations. Here's what I have so far:
public class ExportCSVAnonymous : FileResult {
public dynamic List {
set;
get;
}
public char Separator {
set;
get;
}
public ExportCSVAnonymous(dynamic list, string fileDownloadName, char separator = ',') : base("text/csv") {
List = list;
Separator = separator;
FileDownloadName = fileDownloadName;
}
public ExportCSVAnonymous(dynamic list, string fileDownloadName, char separator = ',') : base("text/csv") {
List = list;
Separator = separator;
FileDownloadName = fileDownloadName;
}
protected override void WriteFile(HttpResponseBase response) {
var outputStream = response.OutputStream;
using (var memoryStream = new MemoryStream()) {
WriteList(memoryStream);
outputStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
}
private void WriteList(Stream stream) {
var streamWriter = new StreamWriter(stream, Encoding.Default);
WriteHeaderLine(streamWriter);
streamWriter.WriteLine();
WriteDataLines(streamWriter);
streamWriter.Flush();
}
//I wish this part could be improved
private void WriteHeaderLine(StreamWriter streamWriter) {
foreach (var line in List) {
foreach (MemberInfo member in line.GetType().GetProperties()) {
WriteValue(streamWriter, member.Name);
}
break;
}
}
private void WriteValue(StreamWriter writer, String value) {
writer.Write("\"");
writer.Write(value.Replace("\"", "\"\""));
writer.Write("\"" + Separator);
}
private void WriteDataLines(StreamWriter streamWriter) {
foreach (var line in List) {
foreach (MemberInfo member in line.GetType().GetProperties()) {
WriteValue(streamWriter, GetPropertyValue(line, member.Name));
}
streamWriter.WriteLine();
}
}
private static string GetPropertyValue(object src, string propName) {
object obj = src.GetType().GetProperty(propName).GetValue(src, null);
return (obj != null) ? obj.ToString() : "";
}
}
I used dynamic as a way to pass my AT inside the class. Is there better way to do this? Lastly, I want to improve the WriteHeaderLine method. Since I am using dynamic type, I cannot cast it successfully to inspect the properties of the AT. What's the best way to do this?
To some extent I feel its a bit overkill, for purposes of writing a CSV, to use an anonymous type (or any type. really) to pass the info purely so you can pass headers too as named properties. I get it, but ask yourself what is the code really doing/what problem are you solving?
You want to write N strings to a file.
That's pretty much it. So you write some simple method:
void WriteCsvLine(path, string[] cells){
File.AppendAllText(path, string.Join(",", cells) + Environment.NewLine);
}
And you call it like:
someContext.Employees.Select(e =>
new [] { e.Name, e.Dept, e.Salary.ToString() }
).ToList().ForEach(x => WriteCsvLine("c:\\...", x) ;
Ah, but we don't want to pass the path every time.. So you upgrade it to be a class, take the path as a constructor arg..
Ah, but we need to escape commas.. So you upgrade it to quote the fields
Ah, but we need to provide some variable delimiter.. So you upgrade it to have another constructor arg
Ah, but we could optimize to write multiple lines at a time.. So you upgrade it to take a List<string[]> or whatever
Ah, but we need to write a header line.. So you just make the first string array you pass to be the headers instead (you can LINQ Concat your data onto a new[]{"Name","Dept","Salary"} or make it a constructor argument..)
So we've need up with something that we maybe use like this:
var x = new CsvWriter("c:\\...", ',', new[]{"EmployeeName","DepartmentName","Salary"});
x.WriteEnumerable(someContext.Employees.Select(e => new [] {
e.Name,
e.Dept,
e.Salary.ToString()
}));
But that's not very cool - surely we can do it cooler.. So you decide you'll pass a KeyValuePair<string, string>[] (or a record or a ValueTuple) where the key is the header and the value is the data.. your calling code bulks up because you're specifying the header name with the data every time..
Ah, but that's still not very cool with all those strings.. So you decide you'll pass an anonymous type where the property name is the header, and the property value is the data..
Your code has some fewer " chars but now the receiving end has become a torturous nightmare of unpacking the property names into being strings so they can be written as a header line.. (and I don't even know if you can easily control the order of columns any more)
Ends up, the problem was simple: "find a way to pass what the header of the column should be", or in other words "pass a string to a method"
..and somehow we went from:
void Print(string what){
Console.WriteLine(what);
}
...
Print("Hello World");
to something like:
using System.Reflection;
static void Print<T>(T what)
{
PropertyInfo[] propertyInfos = what.GetType().GetProperties();
Console.WriteLine(propertyInfos[0].Name.Replace("_", " "));
}
...
Print(new { Hello_World = 0 });
It'll work, but it's a fairly fairly bonkers way of "passing a string to a method" when you think about it..
..and now the boss wants the headers to have percent symbols so I'm off to work out how to get those into property names and also add another bool flag so we can skip writing the header sometimes .. 😀
I am trying to invoke a function for every member of a list, but pass additional parameters to the delegate.
if I have a list called documents
List<string> documents = GetAllDocuments();
Now I need to iterate over the documents and call a method for every entry. I can do it using something like
documents.ForEach(CallAnotherFunction);
This would require the CallAnotherFunction to have a definition like
public void CallAnotherFunction(string value)
{
//do something
}
However, I need another parameter in CallAnotherFunction called, say content, that is dependent on the calling list.
So, my ideal definition would be
public void CallAnotherFunction(string value, string content)
{
//do something
}
And I would like to pass content as part of the ForEach call
List<string> documents = GetAllDocuments();
documents.ForEach(CallAnotherFunction <<pass content>>);
List<string> templates = GetAllTemplates();
templates.ForEach(CallAnotherFunction <<pass another content>>);
Is there a way I can achieve this without having to define different functions, or use iterators?
Use lambda expressions instead of method groups:
List<string> documents = GetAllDocuments();
documents.ForEach( d => CallAnotherFunction(d, "some content") );
List<string> templates = GetAllTemplates();
templates.ForEach( t => CallAnotherFunction(t, "other content") );
Use lambda expression:
string content = "Other parameter value";
documents.ForEach(x => CallAnotherFunction(x, content));
I have a Lazy<List<T>> where T is a class which has a huge string and metadata about files.
Let's call them Property HugeString and Property Metadata
I have this class U, which has the same property HugeString, among other things. I have to convert Lazy<List<T>> to Lazy<List<U>> without loading all stuff.
Is it possible ?
This is where I create my list, and inside that method I get info about the file and the file itself:
entity.VersionedItems =
new Lazy<List<VersionedItemEntity>>(
() => VersionedItemEntity.GetFromTFSChanges(entity,chng.Changes));
This is what I want to do (commented)
ChangesetList.Add(
new HistoryLogEntryModel()
{
Revision = changeset.Changeset.ToString(),
Author = changeset.User,
Date = changeset.Date.ToString("dd/MM/yyyy"),
Message = changeset.Comment,
//VersionedItems = changeset.VersionedItems
}
But HistoryLogEntryModel has a different Version of VersionedItems. And I need to convert some variables.If I would convert one thing to another, it would load up everything and that would be unnecessary and slow.
Is this the right approach? How else could I achieve this?
thanks in adv.
~
You should be able to wrap the Lazy<List<T>> in a Lazy<List<U>>.
var uLazy = new Lazy<List<U>>(() => tLazy.Value.Select(t => (U)t).ToList());
Or use some kind of extension method:
public static class ExtensionMethods
{
public static Lazy<U> Convert<T,U>(this Lazy<T> source, Func<Lazy<T>, Lazy<U>> convert)
{
return convert(source);
}
}
Lazy<List<int>> source = new Lazy<List<int>>();
Lazy<List<string>> converted = source.Convert(x =>
{
return new Lazy<List<string>>()
{
Items = x.Items.ConvertAll<string>(i => i.ToString())
};
});
I'm sure there's a tidier way to do this though.