asp.net-mvc-4 - 使用 Web API 和 RavenDB 进行继承的自定义模型绑定(bind)器

标签 asp.net-mvc-4 asp.net-web-api ravendb model-binding

我正在开发一个简单的网络应用程序,我需要绑定(bind)所有类型的实现和特定类型的接口(interface)。我的界面有一个像这样的属性

public interface IContent {
    string Id { get;set; }
}

使用此接口(interface)的通用类如下所示

public class Article : IContent {
    public string Id { get;set; }
    public string Heading { get;set; }
}

为了干净起见,文章类只是实现 IContent 的许多不同类之一,因此我需要一种存储和更新这些类型的通用方法。

所以在我的 Controller 中我有这样的 put 方法

public void Put(string id, [System.Web.Http.ModelBinding.ModelBinder(typeof(ContentModelBinder))] IContent value)
{
    // Store the updated object in ravendb
}

和ContentBinder

public class ContentModelBinder : System.Web.Http.ModelBinding.IModelBinder {
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) {

        actionContext.ControllerContext.Request.Content.ReadAsAsync<Article>().ContinueWith(task =>
        {
            Article model = task.Result;
            bindingContext.Model = model;
        });

        return true; 
    }

}

上面的代码不起作用,因为它似乎没有获取 Heading 属性,即使我使用默认模型绑定(bind)器它也可以正确绑定(bind) Heading。

那么,在 BindModel 方法中,我想我需要根据 Id 从 ravendb 加载正确的对象,然后使用某种默认模型绑定(bind)器等更新复杂对象?这就是我需要帮助的地方。

最佳答案

Marcus,下面是一个适用于 Json 和 Xml 格式化程序的示例。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Web.Http;
using System.Web.Http.SelfHost;

namespace Service
{
    class Service
    {
        private static HttpSelfHostServer server = null;
        private static string baseAddress = string.Format("http://{0}:9095/", Environment.MachineName);

        static void Main(string[] args)
        {
            HttpSelfHostConfiguration config = new HttpSelfHostConfiguration(baseAddress);
            config.Routes.MapHttpRoute("Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
            config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;

            try
            {
                server = new HttpSelfHostServer(config);
                server.OpenAsync().Wait();

                Console.WriteLine("Service listenting at: {0} ...", baseAddress);

                TestWithHttpClient("application/xml");

                TestWithHttpClient("application/json");

                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception Details:\n{0}", ex.ToString());
            }
            finally
            {
                if (server != null)
                {
                    server.CloseAsync().Wait();
                }
            }
        }

        private static void TestWithHttpClient(string mediaType)
        {
            HttpClient client = new HttpClient();

            MediaTypeFormatter formatter = null;

            // NOTE: following any settings on the following formatters should match
            // to the settings that the service's formatters have.
            if (mediaType == "application/xml")
            {
                formatter = new XmlMediaTypeFormatter();
            }
            else if (mediaType == "application/json")
            {
                JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
                jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;

                formatter = jsonFormatter;
            }

            HttpRequestMessage request = new HttpRequestMessage();
            request.RequestUri = new Uri(baseAddress + "api/students");
            request.Method = HttpMethod.Get;
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
            HttpResponseMessage response = client.SendAsync(request).Result;
            Student std = response.Content.ReadAsAsync<Student>().Result;

            Console.WriteLine("GET data in '{0}' format", mediaType);
            if (StudentsController.CONSTANT_STUDENT.Equals(std))
            {
                Console.WriteLine("both are equal");
            }

            client = new HttpClient();
            request = new HttpRequestMessage();
            request.RequestUri = new Uri(baseAddress + "api/students");
            request.Method = HttpMethod.Post;
            request.Content = new ObjectContent<Person>(StudentsController.CONSTANT_STUDENT, formatter);
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
            Student std1 = client.SendAsync(request).Result.Content.ReadAsAsync<Student>().Result;

            Console.WriteLine("POST and receive data in '{0}' format", mediaType);
            if (StudentsController.CONSTANT_STUDENT.Equals(std1))
            {
                Console.WriteLine("both are equal");
            }
        }
    }

    public class StudentsController : ApiController
    {
        public static readonly Student CONSTANT_STUDENT = new Student() { Id = 1, Name = "John", EnrolledCourses = new List<string>() { "maths", "physics" } };

        public Person Get()
        {
            return CONSTANT_STUDENT;
        }

        // NOTE: specifying FromBody here is not required. By default complextypes are bound
        // by formatters which read the body
        public Person Post([FromBody] Person person)
        {
            if (!ModelState.IsValid)
            {
                throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
            }

            return person;
        }
    }

    [DataContract]
    [KnownType(typeof(Student))]
    public abstract class Person : IEquatable<Person>
    {
        [DataMember]
        public int Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        // this is ignored
        public DateTime DateOfBirth { get; set; }

        public bool Equals(Person other)
        {
            if (other == null)
                return false;

            if (ReferenceEquals(this, other))
                return true;

            if (this.Id != other.Id)
                return false;

            if (this.Name != other.Name)
                return false;

            return true;
        }
    }

    [DataContract]
    public class Student : Person, IEquatable<Student>
    {
        [DataMember]
        public List<string> EnrolledCourses { get; set; }

        public bool Equals(Student other)
        {
            if (!base.Equals(other))
            {
                return false;
            }

            if (this.EnrolledCourses == null && other.EnrolledCourses == null)
            {
                return true;
            }

            if ((this.EnrolledCourses == null && other.EnrolledCourses != null) ||
                (this.EnrolledCourses != null && other.EnrolledCourses == null))
                return false;

            if (this.EnrolledCourses.Count != other.EnrolledCourses.Count)
                return false;

            for (int i = 0; i < this.EnrolledCourses.Count; i++)
            {
                if (this.EnrolledCourses[i] != other.EnrolledCourses[i])
                    return false;
            }

            return true;
        }
    }
}

关于asp.net-mvc-4 - 使用 Web API 和 RavenDB 进行继承的自定义模型绑定(bind)器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15451270/

相关文章:

asp.net-mvc - MVC 枚举单选按钮必填字段

c# - 在主页 Nopcommerce 上添加 block 标题以分隔产品

asp.net-mvc-4 - MVC 路由不起作用

c# - 在 ASP.NET CORE 应用程序 EF Core 库中不从数据库返回新插入的记录 ID?

angularjs - Angular POST 到 Web API 不传递数据

ravendb - RavenDB Studio 发生身份验证错误

azure - 在 Azure 虚拟机中托管 RavenDb 的风险

azure - 如何在 MVC 应用程序中嵌入视频?

c# - 无法访问 Web API 2 中的 Unity DependencyResolver

ravendb - URL 作为 RavenDB 中的键