C#: is there any clever way to eliminate this redundant code?

Viewed 104

I inherited a C# ASP.Net Core (now .Net 5.0) project with a bunch of entities models like this:

   public class Foo
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid ID { get; set; }
        [Column(TypeName = "varchar(36)")]
        public string CreatedBy { get; set; }
        public DateTime CreatedAt { get; set; }
        [Column(TypeName = "varchar(36)")]
        public string RDoxFindingsID { get; set; }
        [Column(TypeName = "varchar(36)")]
        public string LastUpdatedBy { get; set; }
        public DateTime LastUpdatedAt { get; set; }
        public string F001 { get; set; }
        ...

   public class Bar
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid ID { get; set; }
        [Column(TypeName = "varchar(36)")]
        public string CreatedBy { get; set; }
        public DateTime CreatedAt { get; set; }
        [Column(TypeName = "varchar(36)")]
        public string RDoxFindingsID { get; set; }
        [Column(TypeName = "varchar(36)")]
        public string LastUpdatedBy { get; set; }
        public DateTime LastUpdatedAt { get; set; }
        public string Q100 { get; set; }
        ... <= The same "header" fields (CreatedBy, CreatedAt, etc. etc) are copied/pasted at the top of each different model

So my Create and Edit razor pages have a lot of duplicate code like this:

public class CreateModel : PageModel
{
    ...
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        Foo.CreatedAt = DateTime.Now;
        Foo.CreatedBy = Findings.CreatedBy;
        Foo.LastUpdatedAt = DateTime.Now;
        Foo.LastUpdatedBy = Findings.CreatedBy;
        _context.Foo.Add(Foo);
        await _context.SaveChangesAsync();

        Bar.CreatedAt = DateTime.Now;
        Bar.CreatedBy = Findings.CreatedBy;
        Bar.LastUpdatedAt = DateTime.Now;
        Bar.LastUpdatedBy = Findings.CreatedBy;
        _context.Bar.Add(Bar);
        await _context.SaveChangesAsync();
       ...

Q: Is there any clever way I can refactor my C# code such that I can write ONE method I can call for ANY of these models? Without copying/pasting each of these 4-6 lines over and over for each individual model?

NOTE: I CANNOT change the model. But I can do whatever I want with the "Pages" code.

1 Answers

Here comes interfaces very handy, it's actually main use of them. Define common interface and use it. This should e something like this

class Foo : IEntity
{
    public Guid ID { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedAt { get; set; }
    public string RDoxFindingsID { get; set; }
    public string LastUpdatedBy { get; set; }
    public DateTime LastUpdatedAt { get; set; }
}

class Bar : IEntity
{
    public Guid ID { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedAt { get; set; }
    public string RDoxFindingsID { get; set; }
    public string LastUpdatedBy { get; set; }
    public DateTime LastUpdatedAt { get; set; }
}

//You can name whatever you want
interface IEntity
{
    Guid ID { get; set; }
    string CreatedBy { get; set; }
    DateTime CreatedAt { get; set; }
    string RDoxFindingsID { get; set; }
    string LastUpdatedBy { get; set; }
    DateTime LastUpdatedAt { get; set; }
}

class Program
{
    public static async Task<IActionResult> OnPostAsync(IEntity entity)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        entity.CreatedAt = DateTime.Now;
        entity.CreatedBy = Findings.CreatedBy;
        entity.LastUpdatedAt = DateTime.Now;
        entity.LastUpdatedBy = Findings.CreatedBy;
        _context.Foo.Add(entity);
        await _context.SaveChangesAsync();
    }

    static async void Main(string[] args)
    {
        await OnPostAsync(new Foo());
    }
}
Related