0

In my project (Asp.net MVC), I want to use DevExtreme GridView to display my data. I've used code first to create databases and tables. In the project, I have a model with the name of Member. I did right click on the Controller folder and select Add->Controller->DevExtreme Web API Controller with actions, using Entity Framework. In the wizard, I selected my database context and model and determine my controller name (MembersController) and then clicked Add. So in the Views folder, I created a folder with name Members and inside it, I added a view with name Index. (I don't know what exactly name must be for view, you suppose Index). In the index view, I used the wizard to add a DevExtreme GridView (Right-click on the view context and click on Insert A DevExtreme Control Here. In the wizard, I selected GridView as control and DatabaseContext, Member model and Members controller. You can see all of my codes in the below: Member Mode: Model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace WebApplication2.Models
{
    public class Member
    {
        #region Ctor
        public Member()
        {

        }
        #endregion

        #region Properties

        [Key]
        public int MemberID { get; set; }

        [Required(ErrorMessage ="*")]
        public string FirstName { get; set; }

        [Required(ErrorMessage = "*")]
        public string LastName { get; set; }

        public string  Phone { get; set; }

        public string Mobile { get; set; }

        [Required(ErrorMessage = "*")]
        public string NID { get; set; }

        [Required(ErrorMessage = "*")]
        public string MID { get; set; }

        [Required(ErrorMessage = "*")]
        public string SalaryID { get; set; }
        #endregion
    }
}

Controller:

[Route("api/Members/{action}", Name = "MembersApi")]
public class MembersController : ApiController
{
    private ApplicationDbContext _context = new ApplicationDbContext();

    [HttpGet]
    public HttpResponseMessage Get(DataSourceLoadOptions loadOptions) {
        var members = _context.Members.Select(i => new {
            i.MemberID,
            i.FirstName,
            i.LastName,
            i.Phone,
            i.Mobile,
            i.NID,
            i.MID,
            i.SalaryID
        });
        return Request.CreateResponse(DataSourceLoader.Load(members, loadOptions));
    }

    [HttpPost]
    public HttpResponseMessage Post(FormDataCollection form) {
        var model = new Member();
        var values = JsonConvert.DeserializeObject<IDictionary>(form.Get("values"));
        PopulateModel(model, values);

        Validate(model);
        if (!ModelState.IsValid)
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, GetFullErrorMessage(ModelState));

        var result = _context.Members.Add(model);
        _context.SaveChanges();

        return Request.CreateResponse(HttpStatusCode.Created, result.MemberID);
    }

    [HttpPut]
    public HttpResponseMessage Put(FormDataCollection form) {
        var key = Convert.ToInt32(form.Get("key"));
        var model = _context.Members.FirstOrDefault(item => item.MemberID == key);
        if(model == null)
            return Request.CreateResponse(HttpStatusCode.Conflict, "Member not found");

        var values = JsonConvert.DeserializeObject<IDictionary>(form.Get("values"));
        PopulateModel(model, values);

        Validate(model);
        if (!ModelState.IsValid)
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, GetFullErrorMessage(ModelState));

        _context.SaveChanges();

        return Request.CreateResponse(HttpStatusCode.OK);
    }

    [HttpDelete]
    public void Delete(FormDataCollection form) {
        var key = Convert.ToInt32(form.Get("key"));
        var model = _context.Members.FirstOrDefault(item => item.MemberID == key);

        _context.Members.Remove(model);
        _context.SaveChanges();
    }


    private void PopulateModel(Member model, IDictionary values) {
        string MEMBER_ID = nameof(Member.MemberID);
        string FIRST_NAME = nameof(Member.FirstName);
        string LAST_NAME = nameof(Member.LastName);
        string PHONE = nameof(Member.Phone);
        string MOBILE = nameof(Member.Mobile);
        string NID = nameof(Member.NID);
        string MID = nameof(Member.MID);
        string SALARY_ID = nameof(Member.SalaryID);

        if(values.Contains(MEMBER_ID)) {
            model.MemberID = Convert.ToInt32(values[MEMBER_ID]);
        }

        if(values.Contains(FIRST_NAME)) {
            model.FirstName = Convert.ToString(values[FIRST_NAME]);
        }

        if(values.Contains(LAST_NAME)) {
            model.LastName = Convert.ToString(values[LAST_NAME]);
        }

        if(values.Contains(PHONE)) {
            model.Phone = Convert.ToString(values[PHONE]);
        }

        if(values.Contains(MOBILE)) {
            model.Mobile = Convert.ToString(values[MOBILE]);
        }

        if(values.Contains(NID)) {
            model.NID = Convert.ToString(values[NID]);
        }

        if(values.Contains(MID)) {
            model.MID = Convert.ToString(values[MID]);
        }

        if(values.Contains(SALARY_ID)) {
            model.SalaryID = Convert.ToString(values[SALARY_ID]);
        }
    }

    private string GetFullErrorMessage(ModelStateDictionary modelState) {
        var messages = new List<string>();

        foreach(var entry in modelState) {
            foreach(var error in entry.Value.Errors)
                messages.Add(error.ErrorMessage);
        }

        return String.Join(" ", messages);
    }

    protected override void Dispose(bool disposing) {
        if (disposing) {
            _context.Dispose();
        }
        base.Dispose(disposing);
    }
}

View:

   @{
       Layout = "~/Views/Shared/_Layout.cshtml";
    }


    @(Html.DevExtreme().DataGrid<WebApplication2.Models.Member>()
        .DataSource(ds => ds.WebApi()
            .RouteName("MembersApi")
            .LoadAction("Get")
            .InsertAction("Post")
            .UpdateAction("Put")
            .DeleteAction("Delete")
            .Key("MemberID")
        )
        .RemoteOperations(true)
        .Columns(columns => {

            columns.AddFor(m => m.MemberID);

            columns.AddFor(m => m.FirstName);

            columns.AddFor(m => m.LastName);

            columns.AddFor(m => m.Phone);

            columns.AddFor(m => m.Mobile);

            columns.AddFor(m => m.NID);

            columns.AddFor(m => m.MID);

            columns.AddFor(m => m.SalaryID);
        })
        .Editing(e => e
            .AllowAdding(true)
            .AllowUpdating(true)
            .AllowDeleting(true)
        )
    )

WebApiConfig.cs file:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Http;

    namespace WebApplication2
    {
        public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config)
            {
                config.MapHttpAttributeRoutes();

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

                // WebAPI when dealing with JSON & JavaScript!
                // Setup json serialization to serialize classes to camel (std. Json format)
                var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
                formatter.SerializerSettings.ContractResolver =
                    new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
            }
        }
    }

Global.asax.cs file:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Http;
    using System.Web.Mvc;
    using System.Web.Optimization;
    using System.Web.Routing;

    namespace WebApplication2
    {
        public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {

                AreaRegistration.RegisterAllAreas();
                GlobalConfiguration.Configure(WebApiConfig.Register);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
        }
    }

In addition I've installed all requirements for this project according this link. But when I try to show View with https://localhost:44328/Members/index RUL, I get this error:

The resource cannot be found. Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.

Requested URL: /Members/index

I'v tried a lot way to correct my wrong but I couldn't find solution. I almost read all of documents about routing (mvc and web api), but after about 5 days I still couldn't to solve it. Thanks a lot for answer me.

Piroozman
  • 29
  • 9

2 Answers2

0

The thing is as far as I can tell, one of the reasons you are receiving a 404 is because you don't seem to be adding your parameter anywhere. Aside from that your 'DataSourceLoadOptions loadOptions' shouldn't be used as a parameter because it is probably too complex. Shouldn't you create a service which retrieves your loadOptions instead of you giving it along?

If you want all members without giving information then you should do exactly that. Not give the request some metadata it doesn't know about along for the ride.

I suggest you do the following:

  1. Create an API which does not need metadata like how to get a datasource. Things such as Members.LastName are acceptable
  2. Make sure you create a service which is responsible for getting your data in the first place. This means also removing all that extra code in your controller and placing it in a more suitable location.
  3. Keep your classes clean and simple. Your controller now has too many responsibilities.

Hopefully this'll help. If you try your API GET Method as is without the 'DataSourceLoadOptions loadOptions' parameter, then your API will not return 404.

  • Hi, Unfortunately, I didn't get it right. I changed the Get action so that it did not have the LoadOption parameter, but I still did not receive an answer. I really appreciate you giving the answer more accurately based on code. Sorry for my late reply. – Piroozman Dec 21 '19 at 07:18
  • Meanwhile, You can download my project as a RAR file from here: https://drive.google.com/file/d/1o7cTLaYlh4WVRTzcepcpVR8p4cGjc34B/view?usp=sharing – Piroozman Dec 21 '19 at 08:07
0

Since you didn't put in your ajax call url, I'm going to have to work with this

Requested URL: /Members/index

This is a problem, your webApi default route requires your URL to be prepended with /api/

So something like this should work /api/Members, so you can remove the Index part of that URL as the request type will handle which Action is executed ie HTTPGet/HTTPPost

EDIT: Use this as your route

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { controller = "Members" id = RouteParameter.Optional }
);
AbdulG
  • 720
  • 5
  • 16
  • Hi @AbdulG, Thanks for your replay. Unfortunately, after using `api/Members/` I got this error: This XML file does not appear to have any style information associated with it. The document tree is shown below. ` No HTTP resource was found that matches the request URI 'https://localhost:44328/api/Members'. No action was found on the controller 'Members' that matches the request. ` – Piroozman Dec 28 '19 at 05:42