ToString() method not working in Modle's Getter/Setter - c#

I have a C# Model that contains a birthday. The birthday field takes the birthday from a separate API in the DateTime format. I've been trying to change this to a String instead, and I want to do it with Getters/Setters, if possible.
I get an error for ToString() when trying to do this. Error states "cannot convert from "string" to "System.IFormatProvider". I've tried lots of other variants of this and I just can not get something to work. I want to be able to achieve it via the Getters/Setters.
public class PersonLookUpModel
{
public DateTime? DateOfBirth { get; set; }
public string? _dateOfBirthString;
public string? DateOfBirthString {
get => DateOfBirth;
set => _dateOfBirthString = value.ToString("MMMM dd"); }
}

Here you go.
public class PersonLookupModel
{
public DateTime DateOfBirth{get;set;}
public string DOBString => DateOfBirth.ToString("MMM dd");
}

This is what ended up working:
public DateTime? DateOfBirth { get; set; }
public string _dobString;
public string DOBString {
get => _dobString = DateOfBirth?.ToString("MMMM dd");
set => _dobString = value; }

Related

Is there a way to type check a generic function parameter using the shape of the object instead of inheritance?

I have a few EF model classes that I want to create. Each class has a few common properties that I want to set before inserting a new entity, for example:
public partial class BlogPost {
public DateTime CreatedTime { get; set; }
public string CreatorName { get; set; }
public string PostTitle { get; set; }
public string PostText { get; set; }
}
public partial class Comment {
public DateTime CreatedTime { get; set; }
public string CreatorName { get; set; }
public string CommentText { get; set; }
}
...
When I create these classes, I'm instantiating them like so:
var blogPost = new BlogPost {
CreatedTime = DateTime.UtcNow,
CreatorName = creatorName,
PostTitle = postTitle,
PostText = postText,
};
var comment = new Comment {
CreatedTime = DateTime.UtcNow,
CreatorName = creatorName,
...
};
...
I want to create a method to automatically set some of the common properties so I don't need to manually type them out for each class with the same properties. Since they don't extend the same class or implement the same interface, I'm wondering how this can be achieved. My first thought was to use a generic method; however, I don't know if there's a way to specify what properties the generic type should have without them extending the same class (similar to TypeScript's "duck typing"). My desired method looks something like this:
public void SetInitialProperties<T>(T dbEntity, DateTime createdTime, string creatorName) where T : ??? {
dbEntity.CreatedTime = createdTime;
dbEntity.CreatorName = creatorName;
}
...
var blogPost = new BlogPost { PostTitle = postTitle, PostText = postText };
SetInitialProperties(blogPost, createdTime, creatorName);
Worst case scenario if I can't use a generic, I could always use dynamic; however, I'd like to keep type checking if possible.
You can achieve what you want using reflection. You can pass in an object and resolve it's type, then get all the public properties of that given type and find if you have one called CreatedTime for example. Then you can set the value of the given property on the passed dbEntity object. However, I do not recommend this solution:
public void SetInitialProperties(object dbEntity, DateTime createdTime, string creatorName) {
// get the passed object's properties and find the one called CreatedTime
var createdTimePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatedTime").FirstOrDefault();
// below line is equal to: dbEntity.CreatedTime = createdTime;
createdTimePropertyInfo.SetValue(dbEntity, createdTime);
var creatorNamePropertyInfo = dbEntity.GetType().GetProperties().Where(i => i.Name == "CreatorName").FirstOrDefault();
creatorNamePropertyInfo.SetValue(dbEntity, creatorName);
}
You would be better off on the long run by creating a common interface or even an abstract base class so you don't have to implement CreatedTime and CreatorName and other properties for every EF model. It would look like the following:
public interface IUserEntry
{
DateTime CreatedTime { get; set; }
string CreatorName { get; set; }
}
public abstract class UserEntryBase : IUserEntry
{
public DateTime CreatedTime { get; set; }
public string CreatorName { get; set; }
}
public partial class BlogPost : UserEntryBase
{
public string PostTitle { get; set; }
public string PostText { get; set; }
}
public partial class Comment : UserEntryBase
{
public string CommentText { get; set; }
}
And your SetInitialProperties would be pretty simple:
public void SetInitialProperties(IUserEntry dbEntity, DateTime createdTime, string creatorName)
{
dbEntity.CreatedTime = createdTime;
dbEntity.CreatorName = creatorName;
}
Once you develop onto an interface, you achieve much more flexibility than by using reflection or a dynamic type, since you get the compile-time checking that was mentioned before me and you can see the common properties of your models.
You can't do that in C# because C# uses a nominal type system and not a structural type system.
For your particular case you have to come up with an interface that contains the properties in common and which will be implemented by both entities, then use that new interface as you generic function parameter constraint.
If you're absolutely sure the properties will have the same name, you could pass a dynamic to set property values. However, this prevents any compile-time checking of the typing, so if you accidently pass an incompatible type it won't be caught until runtime.
public void SetInitialProperties(dynamic dbEntity, DateTime createdTime, string creatorName) {
dbEntity.CreatedTime = createdTime;
dbEntity.CreatorName = creatorName;
}

Using setter in ViewModel using another property

I have the following ViewModel in my app and there is two BirthDate property (with / without time). When using set { } without body I encounter "Not using the value means that the accessor ignores the caller's intent which could cause unexpected results at runtime.". Then I added a private property and update set as set { birthDateWithTime = value; }. However, this time birthDateWithTime private property not sees to be used. Is there any mistake regarding to implementation below? I want to use both property separately and do not want to convert in JavaScript or code.
public class DemoViewModel
{
public int Id { get; set; }
public DateTime BirthDate { get; set; }
private string birthDateWithTime;
public string BirthDateWithTime {
get { return BirthDate.ToString("dd/MM/yyyy - HH:mm"); }
set { birthDateWithTime = value; }
}
}
#Gabriel Llorico answer is correct. or maybe you can try another one.
private DateTime _birthDate;
public DateTime BirthDate{
get{
return _birthDate;
}
set{
this._birthDate = value;
this.BirthDateWithTime = this._birthDate.ToString("dd/MM/yyyy - HH:mm");
}
}
public string BirthDateWithTime{get;set;}
try this if it is solely dependent on BirthDate
public string BirthDateWithTime
{
get
{
return BirthDate.ToString("dd/MM/yyyy - HH:mm");
}
}
Or just use a body-property style.
public string BirthDateWithTime => BirthDate.ToString("dd/MM/yyyy - HH:mm");
If you need to set BirthDateWithTime, just set in the BirthDate property, so both are updated.

Sorting a ListBox in c# with multiple fields

Is there a way to sort a list box on one field then another?
(I saw the other posts but I think this is a bit more involved). I could do this the long way but thought there was a faster shorthand version I just wasn't aware of.
Basically this reads a directory for all the folders in it with the format of:
DATENAME
I parse out the name from the date. I need to organize these by name THEN by date (the second filter is what is tripping me up).
So a folder of:
12012016TULLY
1202019LAVA
2202018LAVA
5162019CLOUD
5202020LAVA
would look like
5162019CLOUD
2202018LAVA
1202019LAVA
5202020LAVA
12012016TULLY
So this is what I have:
class MyListBoxItem
{
public string StudyBaseFolder { get; set; }
public string StudyName { get; set; }
public string UserLastName { get; set; }
public string StudyDate { get; set; }
}
List<MyListBoxItem> studiesAndFolders = new List<MyListBoxItem>();
//later int he code i build a list of studyNames (which is a path and I pasre the path here too)
foreach (string sn in studyName)
{
//get user name
String lastName = getLastName(sn);
String theDate = getDate(sn);
//can I organize this based on the LAST NAME THEN THE DATE
studiesAndFolders.Add(new MyListBoxItem { StudyBaseFolder = path, StudyName = sn, UserLastName = lastName, StudyDate = theDate });
}
Then I finally add this to the listbox.
listDirectories.Items.Clear();
//I do it this way so a double click on an item gets the object back and I can do things with it.
foreach(MyListBoxItem direc in studiesAndFolders)
{
listDirectories.Items.Add(direc);
}
listbox.sorted=true didn't help, and I am sure there might be an expression (LINQ to the rescue?). I was just going to do it the long way with a ton of cases when I take the studiesAndFolders and put it to the list.
This code sorts by name, then by date. And should be easy to read.
foreach(MyListBoxItem direc in studiesAndFolders.OrderBy(x => x.StudyName).ThenBy(x => x.StudyDate))
{
listDirectories.Items.Add(direc);
}
As others have noted, you should be storing the StudyDate as a DateTime unless you want it sorted alphabetically.
Firstly, change type of StudyDate to DateTime
class MyListBoxItem
{
...
public DateTime StudyDate { get; set; }
}
After, create new comparer
public class MyListBoxItemComparer : IComparer<MyListBoxItem>
{
public int Compare(MyListBoxItem x, MyListBoxItem y)
{
if (x.StudyName == y.StudyName)
{
return x.StudyDate.CompareTo(y.StudyDate);
}
return String.Compare(x.StudyName, y.StudyName, StringComparison.Ordinal);
}
}
Finally, use SortedSet instead of List
SortedSet<MyListBoxItem> studiesAndFolders = new SortedSet<MyListBoxItem>(new MyListBoxItemComparer());
For ordering based on the date correctly you need StudyDate to be of type DateTime
class MyListBoxItem
{
public string StudyBaseFolder { get; set; }
public string StudyName { get; set; }
public string UserLastName { get; set; }
public DateTime StudyDate { get; set; }
}
Then you can order by using LINQ extension methods.
var orderedDirectories =
directories.OrderBy(dir => dir.StudyName)
.ThenBy(dir => dir.StudyDate);
foreach (var directory in orderedDirectories)
{
listBox.Items.Add(directory);
}
You can override .ToString() method in MyListBoxItem class that listbox will display items as you want.
class MyListBoxItem
{
public string StudyBaseFolder { get; set; }
public string StudyName { get; set; }
public string UserLastName { get; set; }
public DateTime StudyDate { get; set; }
public override string ToString()
{
return $"{StudyDate:MMddyyyy}{StudyName}";
}
}
If you can use LINQ in your project, first you should make StudyDate type of DateTime. Then you could do this:
MyList.OrderBy(x => x.StudyName).ThenByDescending(x=>x.StudyDate).ToList()

Can I simplify this C# in my ViewModel?

In my code I have the following:
viewModel.Note.Modified = DateTime.Now;
viewModel.Note.ModifiedBy = User.Identity.Name;
and in my view:
[DisplayName("Modified")]
public DateTime Modified { get; set; }
[DisplayName("Modified By")]
public string ModifiedBy { get; set; }
Could I just have my code change ModifiedBy and then have some code that runs in the ViewModel that changes the date of Modified when the Modified by is changed?
The way to do this will depend on how much logic you want in your view. If you don't mind some logic there, you can do something like this:
[DisplayName("Modified")]
public DateTime Modified { get; set; }
private string m_ModifiedBy
[DisplayName("Modified By")]
public string ModifiedBy {
get { return m_ModifiedBy; }
set{ m_ModifiedBy = value; Modified = DateTime.Now; }
}
If you want the logic in your ViewModel, you can do something like this: ( assuming that viewModel is a class variable )
public void SetModifiedBy(string modifiedBy) {
viewModel.Note.Modified = DateTime.Now;
viewModel.Note.ModifiedBy = modifiedBy;
}
Then you would just call SetModifiedBy(User.Identity.Name) and both values would be updated.
If viewModel.Note is a reference to a NoteViewModel instance, then you can use the following:
public class NoteViewModel{
private DateTime? m_Modified;
private string m_ModifiedBy;
// note that you do not need the DisplayNameAttribute, because the default
// display name is the property name
public DateTime Modified {
get { return m_Modified ?? DateTime.Now; }
}
[DisplayName("Modified By")]
public string ModifiedBy {
get { return m_ModifiedBy ?? string.Empty; }
set {
if(value!=null) {
m_ModifiedBy = value;
m_Modified = DateTime.Now;
}
}
}
}
Then in your "code" (I'm guessing you meant the controller?), you can just do:
viewModel.Note.ModifiedBy = User.Identity.Name;
and you will have the intended result.
Side Note: Depending on the audience of your application, you may want to consider using DateTime.UtcNow for localization purposes. DateTime.Now will return the current DateTime on the server, which is dependent on the server's location. If you are displaying this data to the user, it is likely that you will want to either (a) specify the time zone, or (b) localize the time to the time zone of the client machine

DateTime How To

When creating a class what is the syntax to define a public property for that class as a DateTime type rather than string?
If you want a date object, with class-controlled formatting, you need two properties:
public DateTime DateField { get; set; }
// a read only string
public String DateFieldString {
get { return DateField.ToString(/* your format */); }
}
EDIT
ASP.NET: BoundField's DataFormatString property looks to be what you want.
<asp:BoundField
DataField="EventDate"
HeaderText="Event Date"
DataFormatString="{0:MM/dd/yyyy}"/>
Easy as (pumpkin) pie. Happy Thanksgiving!
public class MyClass {
public DateTime MyDate { get; set; }
public string MyFormattedDate { get { return MyDate.ToString(myFormat); } }
}
Something like
public class TestClas
{
DateTime dtDate;
public DateTime DtDate
{
get
{
return dtDate;
}
set
{
dtDate = value;
}
}
}
and to get and set the field you can use
TestClas objDate = new TestClas();
// set date
objDate.DtDate = DateTime.Now;
// get date
DateTime dtCurDate = objDate.DtDate;
Edit
It would be better not to implement the formatting inside the property. Make the formatting inside the gridview. Otherwise if you need another formatting then you would have to create another property.
using System;
public class Customer {
private DateTime createDate;
public DateTime CreateDate {
get { return createDate; }
set { createDate = value; }
}
}

Categories

Resources