How to use the DTO efficiently based on Scenario i

2019-08-01 06:00发布

问题:

I'm working on Employee Model, it contains all the information about the Employee

For Example:

public class Employee
{
    public int EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public string HomePhone { get; set; }
    public string MobilePhone { get; set; }
}

I'm having two methods for fetching records of Employee

public Employee GetEmployeeName(int id)
{
    // The return should contain only FirstName, MiddleName and LastName. 
    // The rest of the properties should be non-accessable (i.e., Private)
}

public Employee GetEmployeeContacts(int id)
{
    // The return should contain only EmailAddress, HomePhone and MobilePhone. 
    // The rest of the properties should be non-accessable (i.e., Private)
}

public Employee GetEmployeeNameEmail(int id)
{
    // The return should contain only FirstName, MiddleName, LastName and EmailAddress. 
    // The rest of the properties should be non-accessable (i.e., Private)
}

public Employee GetEmployee(int id)
{
    // It should return the entire Employee object
}

How could I achieve this? could you please any one help in this regards.

回答1:

The simple answer is that you cannot change the visibility of an object's properties from one instance to another -- they are whatever the class defines. You can do one of the following:

  1. Use a dynamic object, which has a host of pros and cons. See here and here.

  2. Use inheritance to define a base class (e.g. EmployeeCommon) with the common denominator properties and subclasses for each additional subset (EmployeeCommonAndPhoneNumbers) you want to return from your various methods.

  3. Separate out the various sets of properties into separate "property group" classes (e.g. ContactInfo), and put them as properties on your Employee class. The individual properties (like FirstName or EmailAddress) that you want to "show and hide" can be present or absent on each instance of Employee as you need, but all of the various "property group" objects would still be on every instance of Employee as a property (though they would be null on the instances where they're not needed), so this may or may not fit your requirements.



回答2:

Your question summary mentioned that your intention is to create DTOs, i.e. Data Transfer Objects, presumably in an API messaging context. Your Employee class can certainly serve this purpose, and might also double as a business object class, with its own business logic, aside from just the auto-properties you've listed.

IIUC, the specialized GetEmployee... methods will live in a separate factory class, and will create instances of Employee, with only the required properties populated. I'm guessing you want to do this because your API includes different message contracts with different subsets of Employee properties. GetEmployeeNameEmail method implementation would look up the Employee with the specified ID, create a new Employee instance, populate only the name and email properties, and return this object.

You can't make the methods private for these different DTO contexts; but you can "hide" them by defining separate interfaces for each property subset. The Employee subclass would implement each of these interfaces:

public interface IEmployeeWithName
{
     int EmployeeId { get; set; }
     string FirstName { get; set; }
     string MiddleName { get; set; }
     string LastName { get; set; }
}

 interface IEmployeeWithContacts
{
     int EmployeeId { get; set; }
     string EmailAddress { get; set; }
     string HomePhone { get; set; }
     string MobilePhone { get; set; }
}

 interface IEmployeeWithNameEmail
{
     int EmployeeId { get; set; }
     string FirstName { get; set; }
     string MiddleName { get; set; }
     string LastName { get; set; }
     string EmailAddress { get; set; }
}

 interface IEmployee
{
     int EmployeeId { get; set; }
     string FirstName { get; set; }
     string MiddleName { get; set; }
     string LastName { get; set; }
     string EmailAddress { get; set; }
     string HomePhone { get; set; }
     string MobilePhone { get; set; }
}

public class Employee : IEmployee, IEmployeeWithContacts, IEmployeeWithName, IEmployeeWithNameEmail
{
    public int EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }
    public string HomePhone { get; set; }
    public string MobilePhone { get; set; }
}

public class EmployeeFactory
{

    private Employee GetEmployeeByID(int id)
    {
        // this would perform the lookup and populate the Employee object...
        return new Employee();
    }

    public IEmployeeWithName GetEmployeeName(int id)
    {
        // The return should contain only FirstName, MiddleName and LastName. 
        return GetEmployeeByID(id);
    }

    public IEmployeeWithContacts GetEmployeeContacts(int id)
    {
        // The return should contain only EmailAddress, HomePhone and MobilePhone. 
        return GetEmployeeByID(id);
    }

    public IEmployeeWithNameEmail GetEmployeeNameEmail(int id)
    {
        // The return should contain only FirstName, MiddleName, LastName and EmailAddress. 
        return GetEmployeeByID(id);
    }

    public IEmployee GetEmployee(int id)
    {
        // It should return the entire Employee object
        return GetEmployeeByID(id);
    }
}

Note that if you pass these to a reflective serializer, your interface facade might not be sufficient to hide properties you want to suppress. In that case, you'd have to go back to creating a new Employee object, but you might be able to leverage something like AutoMapper, in combination with the specialized interfaces, to reduce code redundancy.



标签: c# oop model