Link to home
Start Free TrialLog in
Avatar of Charles Sugden
Charles SugdenFlag for United States of America

asked on

Create a WebAPI using a generic return parameter

Excuse me in advance for any failings in the knowledge of WEBAPI and Generics.
My jobs do not always allow me to concentrate on 1 particular topic.

Premise:
Using 3 MVC controller calls, each calling a different WEBAPI, passing same parameters

My question is:
I would like to compress multiple controller calls calling multiple webapi methods into a single webapi/

I have attached similar code to what I am using.
ExpertsExchange.txt
Avatar of Jeff Tennessen
Jeff Tennessen
Flag of United States of America image

Actually, I'm not sure generics are necessary here. Couldn't you just do something like this?

[Route("api/alerts/GetA/{subject}")]
[Route("api/alerts/GetB/{subject}")]
[Route("api/alerts/GetC/{subject}")]
[HttpGet]
public IHttpActionResult Get(string subject)
{
    string cnnstr = System.Configuration.ConfigurationManager.ConnectionStrings["DB"].ToString();
    DataTable dt = new DataTable();

    SqlParameter[] prms = new SqlParameter[1];
    prms[0] = new SqlParameter("@FilterArg", SqlDbType.VarChar) { Value = subject };

    DAL.DataAccess da = new DAL.DataAccess();
    dt = da.GetData(cnnstr, "dbo.[getAlerts]", prms);
    List<a> rows = DAL.Helpers.DataTableToList<a>(dt);

    var msg = ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, rows));
    return msg;
}

Open in new window

Avatar of Charles Sugden

ASKER

The stored procedure call will be different in each case and the resultset may or may not conform to a specific class hence my thought of using the generic but let me give it a try.
OH yeah, I need a different class to map the dataset to the rows variable. Nice try though.
What about something like this?

[Route("api/alerts/GetA/{subject}")]
[Route("api/alerts/GetB/{subject}")]
[Route("api/alerts/GetC/{subject}")]
[HttpGet]
public IHttpActionResult Get(string subject)
{
    switch (Request.RequestUri.Segments[3])
    {
        case "GetA/":
            return GetAlerts<a>("dbo.[A]", subject);
        case "GetB/":
            return GetAlerts<b>("dbo.[B]", subject);
        case "GetC/":
            return GetAlerts<c>("dbo.[C]", subject);
        default:
            return default(IHttpActionResult);
    }
}

private IHttpActionResult GetAlerts<T>(string spName, string subject)
{
    string cnnstr = System.Configuration.ConfigurationManager.ConnectionStrings["DB"].ToString();
    DataTable dt = new DataTable();

    SqlParameter[] prms = new SqlParameter[1];
    prms[0] = new SqlParameter("@FilterArg", SqlDbType.VarChar) { Value = subject };

    DAL.DataAccess da = new DAL.DataAccess();
    dt = da.GetData(cnnstr, spName, prms);
    return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, DAL.Helpers.DataTableToList<T>(dt)));
}

Open in new window

was hoping we wouldn't get further into this but the culprit is DataTableToList when used within a generic method.
 I get the following error:
 Error 5 'T' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'DAL.Helpers.DataTableToList<T>(System.Data.DataTable)' c:\users\h188429\documents\visual studio 2013\projects\alertsmonitoringapi\alertsmonitoringapi\controllers\alertscontroller.cs 564 82 AlertsMonitoringAPI


 
[/ public static List<T> DataTableToList<T>(this DataTable table) where T : class, new()
{
            try
            {
                T tempT = new T();
                var tType = tempT.GetType();
                List<T> list = new List<T>();
                foreach (var row in table.Rows.Cast<DataRow>()) 
                {
                    T obj = new T();
                    foreach (var prop in obj.GetType().GetProperties())
                    {
                        var propertyInfo = tType.GetProperty(prop.Name);
                        var rowValue = row[prop.Name];
                        var t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;

                        try
                        {
                            object safeValue = (rowValue == null || DBNull.Value.Equals(rowValue)) ? null : Convert.ChangeType(rowValue, t);
                            propertyInfo.SetValue(obj, safeValue, null);

                        }
                        catch (Exception ex)
                        {
                            throw ex;   //for lack of a better way
                        }
                    }
                    list.Add(obj);
                }
                return list;
            }
            catch
            {
                return null;
            }

Open in new window

That should still work if you decorate the GetAlerts<T>() method I included in my previous reply with a corresponding where. That is:

private IHttpActionResult GetAlerts<T>(string spName, string subject) where T : class, new()

Open in new window

The code looked good until I ran into another error:

Cannot call action method 'System.Web.Http.IHttpActionResult GetData[T](System.String, System.String, System.String, System.String, System.String)' on controller

This link seems to say that I cannot do what I am looking to do

http://stackoverflow.com/questions/23367295/can-you-use-generic-methods-in-a-controller/23367384#23367384
ASKER CERTIFIED SOLUTION
Avatar of Jeff Tennessen
Jeff Tennessen
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Yes that is what I had to do.
The code want too bad.

Thanks for the guidance