2017-09-08 7 views
0

要約:私のビューモデルがpocoクラスと異なるプロジェクトにあるため、IFormFile IFormFileCollectionをAsp .Netに添付されていない自分のクラスに置き換えたい。私のカスタムクラスはICommonFile、ICommonFileCollection、IFormFile(Asp.netのコアクラスではない)、IFormFileCollectionです。Asp Net Core、ネストされたモデルのカスタムモデルバインドが機能しない

私はここでそれを共有します:

ICommonFile.cs

/// <summary> 
/// File with common Parameters including bytes 
/// </summary> 
public interface ICommonFile 
{ 
    /// <summary> 
    /// Stream File 
    /// </summary> 
    Stream File { get; } 

    /// <summary> 
    /// Name of the file 
    /// </summary> 
    string Name { get; } 

    /// <summary> 
    /// Gets the file name with extension. 
    /// </summary> 
    string FileName { get; } 

    /// <summary> 
    /// Gets the file length in bytes. 
    /// </summary> 
    long Length { get; } 

    /// <summary> 
    /// Copies the contents of the uploaded file to the <paramref name="target"/> stream. 
    /// </summary> 
    /// <param name="target">The stream to copy the file contents to.</param> 
    void CopyTo(Stream target); 

    /// <summary> 
    /// Asynchronously copies the contents of the uploaded file to the <paramref name="target"/> stream. 
    /// </summary> 
    /// <param name="target">The stream to copy the file contents to.</param> 
    /// <param name="cancellationToken">Enables cooperative cancellation between threads</param> 
    Task CopyToAsync(Stream target, CancellationToken cancellationToken = default(CancellationToken)); 
} 

ICommonFileCollection.cs

/// <inheritdoc /> 
/// <summary> 
/// Represents the collection of files. 
/// </summary> 
public interface ICommonFileCollection : IReadOnlyList<ICommonFile> 
{ 
    /// <summary> 
    /// File Indexer by name 
    /// </summary> 
    /// <param name="name">File name index</param> 
    /// <returns>File with related file name index</returns> 
    ICommonFile this[string name] { get; } 

    /// <summary> 
    /// Gets file by name 
    /// </summary> 
    /// <param name="name">file name</param> 
    /// <returns>File with related file name index</returns> 
    ICommonFile GetFile(string name); 

    /// <summary> 
    /// Gets Files by name 
    /// </summary> 
    /// <param name="name"></param>> 
    /// <returns>Files with related file name index</returns> 
    IReadOnlyList<ICommonFile> GetFiles(string name); 
} 

IFormFile.cs

/// <inheritdoc /> 
/// <summary> 
/// File transferred by HttpProtocol, this is an independent 
/// Asp.net core interface 
/// </summary> 
public interface IFormFile : ICommonFile 
{ 
    /// <summary> 
    /// Gets the raw Content-Type header of the uploaded file. 
    /// </summary> 
    string ContentType { get; } 

    /// <summary> 
    /// Gets the raw Content-Disposition header of the uploaded file. 
    /// </summary> 
    string ContentDisposition { get; } 
} 

私は最終的に成功した私のモデルバインダーを作成しIFormFileCollection.cs

/// <summary> 
/// File Collection transferred by HttpProtocol, this is an independent 
/// Asp.net core implementation 
/// </summary> 
public interface IFormFileCollection 
{ 
    //Use it when you need to implement new features to Form File collection over HttpProtocol 
} 

、私もそれを共有します:

FormFileModelBinderProvider.cs

/// <inheritdoc /> 
/// <summary> 
/// Model Binder Provider, it inspects 
/// any model when the request is triggered 
/// </summary> 
public class FormFileModelBinderProvider : IModelBinderProvider 
{ 
    /// <inheritdoc /> 
    /// <summary> 
    /// Inspects a Model for any CommonFile class or Collection with 
    /// same class if exist the FormFileModelBinder initiates 
    /// </summary> 
    /// <param name="context">Model provider context</param> 
    /// <returns>a new Instance o FormFileModelBinder if type is found otherwise null</returns> 
    public IModelBinder GetBinder(ModelBinderProviderContext context) 
    { 
     if (context == null) throw new ArgumentNullException(nameof(context)); 
     if (!context.Metadata.IsComplexType) return null; 

     var isSingleCommonFile = IsSingleCommonFile(context.Metadata.ModelType); 

     var isCommonFileCollection = IsCommonFileCollection(context.Metadata.ModelType); 

     if (!isSingleCommonFile && !isCommonFileCollection) return null; 

     return new FormFileModelBinder(); 
    } 

    /// <summary> 
    /// Checks if object type is a CommonFile Collection 
    /// </summary> 
    /// <param name="modelType">Context Meta data ModelType</param> 
    /// <returns>If modelType is a collection of CommonFile returns true otherwise false</returns> 
    private static bool IsCommonFileCollection(Type modelType) 
    { 
     if (typeof(ICommonFileCollection).IsAssignableFrom(modelType)) 
     { 
      return true; 
     } 

     var hasCommonFileArguments = modelType.GetGenericArguments() 
      .AsParallel().Any(t => typeof(ICommonFile).IsAssignableFrom(t)); 

     if (typeof(IEnumerable).IsAssignableFrom(modelType) && hasCommonFileArguments) 
     { 
      return true; 
     } 

     if (typeof(IAsyncEnumerable<object>).IsAssignableFrom(modelType) && hasCommonFileArguments) 
     { 
      return true; 
     } 

     return false; 
    } 

    /// <summary> 
    /// Checks if object type is CommonFile or an implementation of ICommonFile 
    /// </summary> 
    /// <param name="modelType"></param> 
    /// <returns></returns> 
    private static bool IsSingleCommonFile(Type modelType) 
    { 
     if (modelType == typeof(ICommonFile) || modelType.GetInterfaces().Contains(typeof(ICommonFile))) 
     { 
      return true; 
     } 

     return false; 
    } 
} 

FormFileModelBinder。 CS

/// <inheritdoc /> 
/// <summary> 
/// Form File Model binder 
/// Parses the Form file object type to a commonFile 
/// </summary> 
public class FormFileModelBinder : IModelBinder 
{ 
    /// <summary> 
    /// Expression to map IFormFile object type to CommonFile 
    /// </summary> 
    private readonly Func<Microsoft.AspNetCore.Http.IFormFile, ICommonFile> _expression; 

    /// <summary> 
    /// FormFile Model binder constructor 
    /// </summary> 
    public FormFileModelBinder() 
    { 
     _expression = x => new CommonFile(x.OpenReadStream(), x.Length, x.Name, x.FileName); 
    } 

    /// <inheritdoc /> 
    /// <summary> 
    /// It Binds IFormFile to Common file, getting the file 
    /// from the binding context 
    /// </summary> 
    /// <param name="bindingContext">Http Context</param> 
    /// <returns>Completed Task</returns> 
    // TODO: Bind this context to ICommonFile or ICommonFileCollection object 
    public Task BindModelAsync(ModelBindingContext bindingContext) 
    { 
     dynamic model; 
     if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); 

     var formFiles = bindingContext.ActionContext.HttpContext.Request.Form.Files; 

     if (!formFiles.Any()) return Task.CompletedTask; 

     if (formFiles.Count > 1) 
     { 
      model = formFiles.AsParallel().Select(_expression); 
     } 
     else 
     { 
      model = new FormFileCollection(); 
      model.AddRange(filteredFiles.AsParallel().Select(_expression)); 
     } 

     bindingContext.Result = ModelBindingResult.Success(model); 
     return Task.CompletedTask; 
    } 
} 

実際にネストされたモデルを使用している場合を除き、すべてが正常に機能しています。私は私が使用している私のモデルの一例を共有し、私が働いてシナリオやんではない Test.cs

public class Test 
{ 
    //It's Working 
    public ICommonFileCollection Files { get; set; } 

    //It's Working 
    public ICommonFileCollection Files2 { get; set; } 

    //This is a nested model 
    public TestExtra TestExtra { get; set; } 
} 

TestExtra.cs

public class TestExtra 
{ 
    //It's not working 
    public ICommonFileCollection Files { get; set; } 
} 
でいくつかのコメントをします

実際にAPIにリクエストすると、次のようなスクリーンショットが表示されます(スクリーンショット)。 Visual Studio Debugging

私はスクリーンを共有しています私の郵便配達要求のショットも私の要求を明確にするために良いです。 Postman request

ネストされたモデルでこの作業を行うことが奨励されている場合、それは素晴らしいことです。

+0

これは、デフォルトの 'IFormFile'での動作とまったく同じですか?また、ファイル以外のプロパティを追加して、それらが実際に正しくバインドされていることを確認できますか? – poke

+0

これはASP net coreのIFormFileとまったく同じように動作しますが、実際には私はfrom githubというコードを持っています。私はすでにファイル以外のプロパティで試してみました。 – Jonathan

答えて

2

Asp Net Coreモデルバインダーは1つのプロパティーでモデルをバインドしません。クラスに1つのプロパティーがある場合、そのプロパティーはnullになりますが、2つ以上を追加するとバインドされます。私の間違い私は入れ子にされたクラスの1つのプロパティを持っていた。コード全体が正しい。

+0

これは本当に知って良いことです。 – poke

関連する問題