jubi
jubi

Reputation: 625

Method returns multiple object

I have a MVC 4 project which consumes services from a web api 1 project .

Here i need to have a service method where i pass an id and a string named action based on the action the service should go and fetch data from the table. Here i will have different cases based on the actions.

So if my action is person it should go to person table and based on the id passed it should return LIST

IF action is email it should fetch data from the Email table based on the id passed and should return LIST

Is it possible to achieve from single method as my return type will be different in each cases? If so what will be my return type of the method?

public Email GetEmail(int id)
{
    Email email = db.Emails.Find(id);
    if (email == null)
    {
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
    }

    return email;
}


public List<Email> GetEmailByPerson(int personid)
{
    List<Email> email = db.Emails.Where(n => n.PersonID == personid).ToList();

    if (email == null)
    {
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
    }

    return email;
}

public Person GetPerson(int id)
{
    Person person = db.Persons.Find(id);
    return person;
}

My get service call always call the same method Modified as below based on the comments

config.Routes.MapHttpRoute(
                        name: "DefaultApi",
                        routeTemplate: "api/{controller}/{id}",
                        defaults: new { id = RouteParameter.Optional }
                    );

config.Routes.MapHttpRoute(
                    name: "ActionApi",
                    routeTemplate: "api/{controller}/{action}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                    );

Code for controller action is:

[ActionName=EmailsByPersonID]
public IEnumerable<Email> GetEmailsByPersonID(int personid)
{
    var emails = db.Emails.Where(n => n.Personid == personid).ToList();

    if (emails == null)
    {
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
    }

    return emails.AsEnumerable();
}

I have made these changes in the web api.config file and decorated my method with action name : EmailByPerson and the service call is http://localhost:XXXX/ActionApi/Email/EmailsByPersonID/1

Upvotes: 1

Views: 1085

Answers (3)

Amit Kumar Ghosh
Amit Kumar Ghosh

Reputation: 3726

with this template -

public class MyController : ApiController
{
    public string GetName(string id)
    {
        return id;
    }

    public string GetNameById(string id)
    {
        return id;
    }
}

GlobalConfiguration.Configuration.Routes.MapHttpRoute
        ("default","api/{controller}/{action}/{id}",new { id = RouteParameter.Optional });

then make the calls to api like -

http://localhost:port/api/My/GetName/12
http://localhost:port/api/My/GetNameById/12

works for me atleast. :)

UPDATE

You could also do it like this -

public class CustomActionInvoker : ApiControllerActionSelector
{
    public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    {
        if (controllerContext == null)
            throw new ArgumentNullException("controllerContext");

        var routeData = (string)controllerContext.RouteData.Values["optional"];
        if (!string.IsNullOrWhiteSpace(routeData))
        {
            var actionInfo = controllerContext.ControllerDescriptor.ControllerType
                .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly).ToList();
            var methodInfo = actionInfo.Where(a => a.Name.Contains(routeData)).FirstOrDefault();
            if (methodInfo != null)
            {
                return new ReflectedHttpActionDescriptor(controllerContext.ControllerDescriptor, methodInfo);
            }
        }
        return base.SelectAction(controllerContext);
    }
}

In the config -

 GlobalConfiguration.Configuration.Routes.MapHttpRoute("default", "api/{controller}/{optional}/{id}", new { id = RouteParameter.Optional });

  GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionSelector), new CustomActionInvoker());

And change the api call to -

http://localhost:port/api/My/Email/12

May be the previous example follows exactly this approach out of the box.

Upvotes: 0

Siva Kumar Siddam
Siva Kumar Siddam

Reputation: 154

There are several ways to fix this.

  1. You can use [RoutePrefix("api/Service")] for your controller and [Route("User")] and [Route("Email")] , 

you should be able too call your web api api/service/user (GET,POST,PUT,Delete) , same thing goes with your Email as well

 2. you can create IModel/IResult/SuperClass for your User/Email, and your web api method would be like IEnumerable<IModel> Get(string entityType) or 
 IModel Get(string entityType,int id)

Hope this will work.

Upvotes: 0

tede24
tede24

Reputation: 2354

I don't like this approach, but you don't ask us to make opinions about it but a specific question. And the answer to the question is YES it's possible.

You can use HttpResponseMessage for this purpose:

public HttpResponseMessage GetXx(string type, int id)
{
    switch(type)
    {
        case "xx":
            Type1 obj1 = <your logic>;
            return Request.CreateResponse(HttpStatusCode.OK, obj1);
        case "yy":
             ....
}

Upvotes: 1

Related Questions