简体   繁体   中英

workaround a static method in a generic interface

I have a business model TodoItem . I have an API that gives me todoitems . So, I have a corresponding TodoItemModel for each controller (let's name it TodoItemDTO ).

The TodoItemDTO class should be able to load itself from a TodoItem business class. So, usually, we would write a static method on the TodoItemDTO class TodoItemDTO FromTodoItem(TodoItem todoItem) .

I would like to define an interface for such classes. But unfortunately I can't define a static method in an interface... ( note : I use the latest C# version, so, finally, I do, but I should define it... however each class should define its static method)

Here is what I tried:

public interface IDtoModel<TBussinesModel, TDtoModel>
{
    TDtoModel FromBusinessModel(TBussinesModel businessObject);
}


public class ToDoItemDTO : IDtoModel<ToDoItem, ToDoItemDTO>
{
    public int Id { get; set; }
    [Required]
    public string Title { get; set; }
    public string Description { get; set; }
    public bool IsDone { get; private set; }

    public ToDoItemDTO FromBusinessModel(ToDoItem businessObject)
    {
        return new ToDoItemDTO()
        {
            Id = businessObject.Id,
            Title = businessObject.Title,
            Description = businessObject.Description,
            IsDone = businessObject.IsDone
        };
    }
}

The only 'problem' here is I have a class method that should be, normally, a static method.

So, by eg, when I define a generic controller, I need to create new objects without any need:

[Route("api/[controller]")]
[ApiController]
public abstract class BaseApiController<TBussinesModel, TDtoModel> : Controller
    where TBussinesModel : BaseEntity
    where TDtoModel : IDtoModel<TBussinesModel, TDtoModel>, new()        
{
    protected readonly IRepository _repository;
    public BaseApiController(IRepository repository) { _repository = repository; }

    [HttpGet("{id:int}")]
    public async Task<IActionResult> GetById(int id)
    {
        var businessObject = await _repository.GetByIdAsync<TBussinesModel>(id);
        // 
        // here bellow, I need to create a new object
        // 
        var dtoObject = (new TDtoModel()).FromBusinessModel(businessObject);
        return Ok(dtoObject);
    }

    [HttpPost]
    public abstract Task<IActionResult> Post([FromBody] TDtoModel item);
}

There are lots of options, none of them perfect.

Personally, I would start by turning your factory method into a constructor of the DTO;

    public ToDoItemDTO(ToDoItem businessObject)
    {
        Id = businessObject.Id;
        Title = businessObject.Title;
        Description = businessObject.Description;
        IsDone = businessObject.IsDone;
    }

You could define a factory class for each DTO. You could use Activator.CreateInstance to dynamically call the constructor at runtime.

Or you can locate the constructor with reflection, build an Expression tree and compile it.

    static Func<TBussinesModel, TDtoModel> GetFactory<TBussinesModel, TDtoModel>()
    {
        var p = Expression.Parameter(typeof(TBussinesModel), "p");
        return Expression.Lambda<Func<TBussinesModel, TDtoModel>>(
            Expression.New(
                typeof(TDtoModel).GetConstructor(new Type[] { typeof(TBussinesModel) }),
                p
            ),
            p)
            .Compile();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM