2

I'm pretty new to this and my issue is very particular. I've googled a lot and haven't been able to find anything on this. I have two properties in a database, StartDate and StartMonth. The StartDate is of type DateTime and the StartMonth is of string type. (I know it's dumb to save both of these into the database but this isn't something I can change because I don't have the authority to do so.) My question is how in the DB Model can I set the StartMonth to equal the Month in the StartDate (since it is the same)? Rather, what is the proper way to do this? Any help is really appreciated!

To be clear I have currently:

[Table("EMPLOYMENT")]
public class Employment : EntityBase
{   
    [Column("STARTDATE")]
    public DateTime? StartDate { get; set; }

    [Column("STARTMONTH")]
    public string StartMonth { get; set; }
}

EDIT: I tried the following (I realize it wasn't any of the suggested, but it was suggested by a co-worker):

[Table("EMPLOYMENT")]
public class Employment : EntityBase
{   
    [Column("STARTDATE")]
    internal DateTime? StartDateInternal { get; set; }

    [Column("STARTMONTH")]
    private string StartMonth { get; set; }

    [NotMapped]
    public DateTime? StartDate
    {
        get
        {
            return StartDateInternal;
        }
        set
        {
            StartDateInternal = value;
            StartMonth = value?.ToString("MMMM");
        }
}

Specifically, the StartMonth is now private, the StartDate is now a NotMapped property that sets the StartDateInternal and StartMonth and returns the StartDateInternal value, and the original StartDate from the previous code is now named StartDateInternal. Though I can see that the value is being properly passed all the way to the model, it will not set the value. It always sets it to NULL in the database. I'm not sure why and was wondering if anyone could see why.

  • you could alter the table schema from your table directly, set STARTMONTH = Datepart(mm,STARTDATE) – LONG Mar 27 '17 at 18:40
  • If I could I would, but I do not have permissions to mess with the table schema and we have our tables being pulled from an Oracle database (Oracle to SQL) so messing with the schema could mess up that process as well. –  Apr 04 '17 at 00:00

4 Answers4

1

You can use a backing field, and get the month from StartDate only if StartMonth value is not set.

private string _startMonth;

[Column("STARTMONTH")]
public string StartMonth
{
    get
    {
        if (string.IsNullOrWhiteSpace(_startMonth) && StartDate.HasValue)
            return StartDate.Value.ToString("MMMM");
        return _startMonth;
    }
    set { _startMonth = value; }
}
Win
  • 61,100
  • 13
  • 102
  • 181
  • It is possible for someone to shoot themselves in the foot here `new Employment() { StartDate = new DateTime(12,1,1), StartMonth = "January" }` – dana Mar 27 '17 at 18:53
  • Yes, it could happen. The problem is if StartMonth does not have setter, Entity Framework will throw exception. If you do not trust setting value at all, you can just remove `string.IsNullOrWhiteSpace(_startMonth)`. – Win Mar 27 '17 at 19:03
  • It looks like StartMonth is coming in from the database and therefore needs to have a setter. – BigTFromAZ Mar 27 '17 at 19:25
0

Mark your setters as private, then create a SetStartDate method that sets both of the properties.

[Table("EMPLOYMENT")]
public class Employment : EntityBase
{   
    [Column("STARTDATE")]
    public DateTime? StartDate { get; private set; }

    [Column("STARTMONTH")]
    public string StartMonth { get; private set; }

    public string SetStartDate(DateTime? startDate)
    {
        this.StartDate = startDate;
        this.StartMonth = startDate.HasValue ? startDate.Value.ToString("MMMM") : null;
    }
}

Another way to model this is to mark the setter for StartMonth as private and have the setter for StartDate set both properties:

[Table("EMPLOYMENT")]
public class Employment : EntityBase
{   
    DateTime? startDate;

    [Column("STARTDATE")]
    public DateTime? StartDate
    {
        get { return this.startDate; }
        set
        {
            this.startDate = value;
            this.StartMonth = value.HasValue ? value.Value.ToString("MMMM") : null;
        }
    }

    [Column("STARTMONTH")]
    public string StartMonth { get; private set; }
}
dana
  • 17,267
  • 6
  • 64
  • 88
  • What does making the set private do? Or, rather, why do this? –  Apr 04 '17 at 00:02
  • The main reason is to prevent the user from shooting themselves in the foot. They can specify a `StartDate` which will automatically calculate `StartMonth`. There is no way from them to specify conflicting values for these fields. – dana Apr 04 '17 at 00:44
  • For clarifying, when I say "user" I mean "developer"... Without jumping through hoops, there will be no way for them (or even you) to mess up these values. Both columns will automatically be set by specifying a single value for `StartDate`. – dana Apr 04 '17 at 00:50
0

You should not map StartMonth property, so it will not be saved in the database, but the model will still have the property. You can do something like:

    [NotMapped]
    public string StartMonth
    {
        get
        {
            return StartDate?.Month.ToString();
        }
    }

where NotMapped attribute tells EF to not map this property, but you are able to use this property in code. Also, you won't need a set property, because StartMonth will just return StartDate month. This way will make you sure that StartMonth and StartDate.Month doesn't have different values.

Marius Orha
  • 670
  • 1
  • 9
  • 26
  • If the setter is used throughout the code and this breaks everything _and_ you're 100% certain that the StartMonth and StartDate are always set together and correctly... Mark the property as [deprecated](http://stackoverflow.com/a/1759357/156755) which will tell other devs not to use it in future, and add a setter that logs a warning but does nothing else ... Even if you're certain, run the full test suite and check all the warnings you receive. – Basic Mar 27 '17 at 19:09
  • Erik indicated that he can't mess with the database design which leads me to believe this database may well be modelled following an old mainframe design, like VSAM, DL/1 or IDMS. It was not uncommon to see dates broken into their parts when stored with designs having old roots. Either way if his app is going to update this database, he has to select a technique that other applications that use the database can accept. – BigTFromAZ Mar 27 '17 at 19:33
  • Who is Erik? Either way, it cannot be altered. –  Apr 04 '17 at 00:41
0

Can you alter the database design?

If so, use a trigger or other facility in your database to render the STARMONTH column as a pseudo-column returning the "month" value of the STARTDATE column.

If not, then you can set the StartMonth when processing a new StartDate:

    [Table("EMPLOYMENT")]
    public class Employment
    {
        private DateTime _StartDate;

        [Column("STARTDATE")]
        public DateTime StartDate
        {
            get
            {
                return _StartDate;
            }
            set
            {
                _StartDate = value;
                StartMonth = value.ToString("MMMM") ?? null;

            }
        }

        [Column("STARTMONTH")]
        public string StartMonth { get; set; }

    }

A database pseudo column would guarantee a consistent result to all DB clients. The above code will be fine for anything that uses this code.

BigTFromAZ
  • 684
  • 7
  • 22
  • I cannot alter the database design, unfortunately. I did try something similar to what you suggested, though. I have edited my question to reflect it. –  Apr 04 '17 at 00:25