Set public properties in ServiceStack web services

2019-07-16 02:52发布

问题:

I am trying to write unit tests for ServiceStack services using Sqlite. Since Sqlite doesn't support stored procedures, I've created public property of type 'ServiceCommand' that takes command text and command type as inputs. By default it is configured to run stored procedure and while writing unit test cases, I am re-assigning the SelectCommand property to sql query against sqlite before calling Any() method as below. All test cases work fine.

var request = new CustomerRequest() { name = "alfki"  };
var service = new CustomerService(InMemoryTestDatabase.OpenDbConnection());
service.SelectCommand = new ServiceCommand() { SQL = "SELECT * FROM customers where customerid = {0}" };
var result = service.Any(request);

But per this thread, public properties of CustomerService are set to null by IOC while resolving the references thus SelectCommand is null in Any() method resulting in object reference error. By setting the property as protected, private, internal or static, I will not be able to run unit tests.

public class CustomerService : Service
{
  private readonly IDbConnection _dbConnection;
  public ServiceCommand SelectCommand {get;set;}

  public CustomerService(IDBConnection dbConnection)
  {        
         _dbConnection = dbConnection;  //injected successfully
         SelectCommand =  new ServiceCommand(){ SQL = "sp_getcustomers",
            CommandType = CommandType.StoredProcedure};          
  }

  public Customer Any(CustomerRequest request)
  {
        //Select command is not accessible here.

  }
}


[Route("/customers")]
public class CustomerRequest
{
    public string name { get; set; }
}

ServiceCommand

public class ServiceCommand
{
    public string SQL { get; set; }        
    public CommandType CommandType { get; set; }

    public ServiceCommand()
    {
        CommandType = CommandType.Text;
    }
}

To be able to run test cases and service as well, I've modified Any() method to instantiate ServiceCommand if it is null. I would like to know if this is the way to go or any better alternatives.

public class CustomerService : Service
{
  private readonly IDbConnection _dbConnection; // injected successfully
  public ServiceCommand SelectCommand {get;set;}

  public CustomerService(IDBConnection dbConnection)
  {        
         _dbConnection = dbConnection;  //injected successfully         
  }

  public Customer Any(CustomerRequest request)
  {
        SelectCommand = SelectCommand ?? new ServiceCommand() { SQL = "sp_getCustomers",CommandType = CommandType.StoredProcedure };

  }
}

回答1:

Since ServiceStack services will inject registered IOC properties for all public properties it will override the value you set in the constructor so you can't make it a public property without registering it in the IOC since it will be overridden to null.

Given this, some possible options are:

Make it a public field

public class CustomerService : Service
{
    public ServiceCommand SelectCommand = new ServiceCommand { 
       SQL = "sp_getcustomers", 
       CommandType = CommandType.StoredProcedure };
    ...
}

Inject a read-only property with a setter

service.SetSelectCommand(
  new ServiceCommand { SQL = "SELECT * FROM customers where customerid = {0}" });

Register it in your IOC and specify it in your constructor

container.Register(new ServiceCommand { 
   SQL = "sp_getcustomers", 
   CommandType = CommandType.StoredProcedure });

and change constructor to:

public CustomerService(IDBConnection dbConnection, ServiceCommand serviceCommand)
{        
    _dbConnection = dbConnection; 
    _serviceCommand = serviceCommand;    
}