简体   繁体   中英

Implementing abstract class with abstract types

I have a base abstract class that accepts types that come from abstract classes, and I am banging my head against the wall trying to figure out how to properly implement this.

The base class:

public abstract class ApiService<TRequest, TResponse>
    where TRequest : ApiRequest
    where TResponse : ApiResponse
{
    public abstract TResponse Execute(TRequest Request);
}

The ApiRequest class:

public abstract class ApiRequest
{

}

The ApiResponse class:

public abstract class ApiResponse
{
    public bool Succeeded { get; set; }

}

I've created a TestService class to try to sort this out, but the concepts are not coming together for me:

public class TestService : ApiService<ApiRequest, ApiResponse>
{
    public override ApiResponse Execute(ApiRequest Request)
    {
        ApiResponse response;

        return (response);
    }

Any help you could offer would be greatly appreciated and help me to further understand abstract classes! Thanks!

So my question is: I don't know how to implement ApiResponse in the Execute method, since you can't instantiate an abstract class.

Generics and Polymorphizem is good but it has to stop at some point. In your case you have a good API interface where it is clear that you pass a TRequest and receive a TResponse for it.

What you should add is how to treat specific cases. Add a layer of IRequestHander<TRequest,TResult> that will know how to create a specific Result from a specific Request .

Then using the Factory design pattern your API function will call the factory to get the specific handler suitable for the request it got. It will execute the RequestHander and will return the Response that it got from it.

public class SpecificRequestA : ApiRequest {}
public class SpecificResponseA : ApiResponse{}

public interface IRequestHander<TRequest,TResponse>
    where TRequest : ApiRequest
    where TResponse : ApiResponse
{
    TResponse Exeute(TRequest request);
}

public class SpecificRequestHandlerA : IRequestHander<SpecificRequestA,SpecificResponseA>
{
    SpecificResponseA Execute(SpecificRequestA request)
    {
        //create the new response out of the given request. Here you know exactly on what you are working :)
    }
}

Then add the factory.

Also think maybe to implement the request as Request<TResponse> - see if it better fits your case

I'd recommend looking into Dependency Injection and DI containers (like Castle, Ninject, Unity, Simple Injector) to be responsible for the initialization.

I use the following ApiClient Class in ac# MVC UI Layer:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Web.Helpers;
using System.Web.Mvc;

namespace DocumentManager.UI
{
    public class ApiClient<T>
    {
        public ApiClientErrorTypes Error{get;private set;}

        private string baseUri =  @"http://localhost/DocumentManager.WebAPI/";

        public T ApiGet(string requestUrl)
        {
            using (var client = new HttpClient())
            {
                var requestUri = new Uri(baseUri + requestUrl);
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                if (ApiAuthToken.token != null)
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiAuthToken.token.ToString());
                }
                var response = client.GetAsync(requestUri).Result;
                string contentString = response.Content.ReadAsStringAsync().Result;
                if (response.IsSuccessStatusCode)
                {
                    T result = JsonConvert.DeserializeObject<T>(contentString);
                    return result;
                }
                if (int.Parse(response.StatusCode.ToString()) > 400 && int.Parse(response.StatusCode.ToString()) <= 499)
                    Error = ApiClientErrorTypes.UnAuthorised;
                else
                    Error = ApiClientErrorTypes.Technical;
                return default(T);
            }
        }

        public T ApiPost(string requestUrl, HttpContent encodedContent)
        {
            using(var client = new HttpClient())
            {
                var requestUri = new Uri(baseUri + requestUrl);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                if (ApiAuthToken.token != null)
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiAuthToken.token.ToString());
                }
                var response = client.PostAsync(requestUri, encodedContent).Result;
                string contentString = response.Content.ReadAsStringAsync().Result;
                if (response.IsSuccessStatusCode)
                {
                    T result = JsonConvert.DeserializeObject<T>(contentString);
                    return result;
                }
                if (int.Parse(response.StatusCode.ToString()) > 400 && int.Parse(response.StatusCode.ToString()) <= 499)
                    Error = ApiClientErrorTypes.UnAuthorised;
                else
                    Error = ApiClientErrorTypes.Technical;
                return default(T);
            }
        }

        public bool ApiPostBool(string requestUrl, HttpContent encodedContent)
        {
            using (var client = new HttpClient())
            {
                var requestUri = new Uri(baseUri + requestUrl);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                if (ApiAuthToken.token != null)
                {
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiAuthToken.token.ToString());
                }
                var response = client.PostAsync(requestUri, encodedContent).Result;
                string contentString = response.Content.ReadAsStringAsync().Result;
                if (response.IsSuccessStatusCode)
                {
                    return true;
                }
                return false;
            }
        }
    }
}

And I call this in the following way from the MVC controller:

var apiClient = new ApiClient<Document>();
var doc = apiClient.ApiGet("api/document/get/" + id);
if (doc != null)
{
    //do stuff here
}

I use the following Web API layer method to return this item

namespace DocumentManager.WebApi.Controllers
{
    [RoutePrefix("api/document")]
    public class DocumentController : BaseController
    {
        [Route("get/{id}")]
        public IHttpActionResult Get(int id)
        {
            return Ok(DACDocument.Read(new DataContext(),id));
        }
    }
}

there is an entityframework data layer behind this (DAC...)

The reason I use this architectural structure is that I want multiple MVC UI front end apps to tie into the API back end.

projects in the solution are Data (class Library) API (Web API) UI (Web MVC)

If this helps please mark as answer!

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