How to pass ObjectId from MongoDB in MVC.net

2019-01-15 08:13发布

问题:

I'm starting a new project with Mongo, NoRM and MVC .Net.

Before I was using FluentNHibernate so my IDs were integer, now my IDs are ObjectId. So when I have an Edit link my URL looks like this :

WebSite/Admin/Edit/23,111,160,3,240,200,191,56,25,0,0,0

And it does not bind automaticly to my controller as an ObjectId

Do you have any suggestions/best practices to work with this? Do I need to encode/decode the ID everytime?

Thanks!

回答1:

I Use following

public class ObjectIdModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        string value = controllerContext.RouteData.Values[bindingContext.ModelName] as string;
        if (String.IsNullOrEmpty(value)) {
            return ObjectId.Empty;
        }
        return new ObjectId(value);
    }
}

and

protected void Application_Start()
    {
        ......

        ModelBinders.Binders.Add(typeof(ObjectId), new ObjectIdModelBinder()); 
    }

almost forgot, make URLs from ObjectId.ToString()



回答2:

Use a custom model binder like this ... (working against the offical C# MongoDB driver)

protected void Application_Start()
{
    ...
    ModelBinders.Binders.Add(typeof(ObjectId), new ObjectIdModelBinder()); 
}

public class ObjectIdModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (result == null)
        {
            return ObjectId.Empty;
        }
        return ObjectId.Parse((string)result.ConvertTo(typeof(string)));
    }
}


回答3:

I am not familiar with the ObjectId type but you could write a custom model binder that will take care of converting the id route constraint to an instance of ObjectId.



回答4:

Did you know you can use the [MongoIdentifier] attribute to make any property act as the unique key?

I've been solving this issue by borrowing a technique from WordPress by having every entity also be represented by a "url slug" property and decorating that property with [MongoIdentifier].

So if I had a person named Johnny Walker I'd create a slug of "johnny-walker". You just have to make sure these url slugs stay unique and you get to keep clean urls without ugly object ids.



回答5:

For Web API you can add Custom parameter binding ule in WebApiConfig:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        //...
        config.ParameterBindingRules.Insert(0, GetCustomParameterBinding);
        //...
    }

    public static HttpParameterBinding GetCustomParameterBinding(HttpParameterDescriptor descriptor)
    {
        if (descriptor.ParameterType == typeof(ObjectId))
        {
            return new ObjectIdParameterBinding(descriptor);
        }
        // any other types, let the default parameter binding handle
        return null;
    }

    public class ObjectIdParameterBinding : HttpParameterBinding
    {
        public ObjectIdParameterBinding(HttpParameterDescriptor desc)
            : base(desc)
        {
        }

        public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            try
            {
                SetValue(actionContext, new ObjectId(actionContext.ControllerContext.RouteData.Values[Descriptor.ParameterName] as string));
                return Task.CompletedTask;
            }
            catch (FormatException)
            {
                throw new BadRequestException("Invalid ObjectId format");
            }
        }
    }
}

And use it Without any additional attributes in controller:

 [Route("{id}")]
 public IHttpActionResult Get(ObjectId id)