Liên kết muộn Mô hình tự động giải quyết sau khi vào bộ điều khiển


9

Tôi đang tìm cách giải quyết một mô hình sau khi tham gia vào một hành động trong bộ điều khiển, cách đơn giản nhất để mô tả vấn đề sẽ là:

public DTO[] Get(string filterName)
{
    //How can I do this
    this.Resolve<MyCustomType>("MyParamName");
}

Nếu bạn đang tìm kiếm thêm thông tin về lý do tại sao tôi đang cố gắng làm điều đó, bạn có thể tiếp tục đọc để có được hình ảnh đầy đủ

TL; DR

Tôi đang tìm cách giải quyết một yêu cầu mô hình, được đặt tên tham số sẽ luôn được giải quyết từ chuỗi truy vấn Làm cách nào tôi có thể tự động đăng ký các bộ lọc từ khi khởi động. Tôi có một lớp học sẽ xử lý việc đăng ký các bộ lọc của tôi.

Trong lớp khởi động của tôi, tôi muốn có thể tự động đăng ký các bộ lọc với dịch vụ restService của mình. Tôi có một tùy chọn mà tôi đang sử dụng để chuyển đến Trình điều khiển tùy chỉnh của mình, trông giống như vậy:

public class DynamicControllerOptions<TEntity, TDTO>
{
    Dictionary<string, Func<HttpContext, Expression<Func<TEntity, bool>>>> _funcNameToEndpointResolverMap
        = new Dictionary<string, Func<HttpContext, Expression<Func<TEntity, bool>>>>();
    Dictionary<string, List<ParameterOptions>> _filterParamsMap = new Dictionary<string, List<ParameterOptions>>();

    public void AddFilter(string filterName, Expression<Func<TEntity, bool>> filter)
    {
        this._funcNameToEndpointResolverMap.Add(filterName, (httpContext) =>  filter);
    }
    public void AddFilter<T1>(string filterName, Func<T1, Expression<Func<TEntity, bool>>> filterResolver,
        string param1Name = "param1")
    {
        var parameters = new List<ParameterOptions> { new ParameterOptions { Name = param1Name, Type = typeof(T1) } };
        this._filterParamsMap.Add(filterName, parameters);
        this._funcNameToEndpointResolverMap.Add(filterName, (httpContext) => {
            T1 parameter = this.ResolveParameterFromContext<T1>(httpContext, param1Name);
            var filter = filterResolver(parameter);
            return filter;
        });
    }
}

Bộ điều khiển của tôi sẽ theo dõi các tùy chọn và sử dụng chúng để cung cấp các bộ lọc cho các điểm cuối phân trang và OData.

public class DynamicControllerBase<TEntity, TDTO> : ControllerBase
{
    protected DynamicControllerOptions<TEntity, TDTO> _options;
    //...

    public TDTO[] GetList(string filterName = "")
    {
        Expression<Func<TEntity, bool>> filter = 
            this.Options.ResolveFilter(filterName, this.HttpContext);
        var entities = this._context.DbSet<TEntity>().Where(filter).ToList();
        return entities.ToDTO<TDTO>();
    }
}

Tôi gặp khó khăn khi tìm cách giải quyết một cách linh hoạt một mô hình được cung cấp cho HTTPContext, tôi sẽ nghĩ phải làm một cái gì đó như thế này để có được mô hình nhưng đây là mã giả không hoạt động

private Task<T> ResolveParameterFromContext<T>(HttpContext httpContext, string parameterName)
{
    //var modelBindingContext = httpContext.ToModelBindingContext();
    //var modelBinder = httpContext.Features.OfType<IModelBinder>().Single();
    //return modelBinder.BindModelAsync<T>(parameterName);
}

Sau khi đào sâu vào Nguồn, tôi đã thấy một số điều đầy hứa hẹn ModelBinderFactoryControllerActionInvoker Các lớp này được sử dụng trong đường ống để liên kết mô hình,

Tôi hy vọng sẽ hiển thị một giao diện đơn giản để phân giải tên tham số từ QueryString, đại loại như thế này:

ModelBindingContext context = new ModelBindingContext();
return context.GetValueFor<T>("MyParamName");

Tuy nhiên, cách duy nhất tôi thấy để giải quyết một mô hình từ chất kết dính mô hình là tạo các mô tả bộ điều khiển giả và giả định hàng tấn thứ.

Làm thế nào tôi có thể chấp nhận các tham số ràng buộc muộn vào contug của tôi?


2
Tôi không thấy việc sử dụng này. Hơn nữa, ngay cả khi bạn có thể liên kết mô hình dựa trên tham số chuỗi .... bạn sẽ không thể sử dụng phương thức chung như GetValueFor <T> vì T phải giải quyết thời gian biên dịch .... điều này có nghĩa là người gọi phải biết loại T tại thời điểm biên dịch sẽ đánh bại mục đích ràng buộc kiểu động. Điều này có nghĩa là người thừa kế của DynamicControllBase phải biết loại TDTO .... Một điều bạn có thể thử là nhận JSON trong tham số và mỗi lần triển khai DynamicControllBase sẽ khử tuần tự chuỗi thành mô hình và ngược lại.
Jonathan Alfaro

@Darkonekt nếu bạn xem phương thức 'AddFilter', bạn có các tham số chung được nhập sẽ được lưu trữ trong một bao đóng khi bạn đăng ký funcs. Hơi khó hiểu một chút nhưng tôi đảm bảo với bạn là khả thi và có thể hoạt động
johnny

Tôi không muốn cắm vào json, vì tôi không muốn thay đổi cách để webapi giải quyết các tham số một cách tự nhiên
johnny 5

Nếu bạn giải thích thêm một chút về trường hợp sử dụng và kịch bản thực tế trong đó loại chức năng này là cần thiết, điều đó sẽ giúp ích rất nhiều. Có lẽ thậm chí còn có một giải pháp đơn giản hơn .. ai biết được. Đối với bản thân tôi, đôi khi tôi thích làm phức tạp mọi thứ .. Chỉ cần nói ..
Ông Blond

@ Mr.Blond Tôi có một dịch vụ nghỉ ngơi chung cung cấp chức năng crud và nhận danh sách. Đôi khi các dịch vụ của tôi cần lọc dữ liệu từ danh sách get, nhưng tôi không muốn phải viết toàn bộ dịch vụ mà tôi cần để cung cấp bộ lọc
johnny

Câu trả lời:


2

Tôi đồng ý với quan điểm của bạn

dịch vụ cần lọc dữ liệu từ danh sách get, nhưng tôi không muốn phải viết toàn bộ dịch vụ tôi cần để cung cấp bộ lọc

Tại sao viết một widget / bộ lọc / điểm cuối cho mọi kết hợp có thể?

Đơn giản chỉ cần cung cấp các hoạt động cơ bản để có được tất cả dữ liệu / thuộc tính. Sau đó sử dụng GraphQL để cho phép người dùng cuối lọc ( mô hình ) nó theo nhu cầu của họ .

Từ GraphQL

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.


Tôi đã xem xét sử dụng GraphQL nhưng tôi quá gắn bó với việc triển khai hiện tại của mình
ngày 5

2

Chúng tôi đã thực hiện điều này, mã của chúng tôi tham khảo trang web này: https://prideparrot.com/blog/archive/2012/6/gotchas_in_explicit_model_binding

Cụ thể, nhìn vào mã của chúng tôi, thủ thuật nào chấp nhận FormCollection trong phương thức điều khiển của bạn và sau đó sử dụng chất kết dính mô hình, mô hình và dữ liệu biểu mẫu:

Ví dụ lấy từ liên kết:

public ActionResult Save(FormCollection form)
{
var empType = Type.GetType("Example.Models.Employee");
var emp = Activator.CreateInstance(empType);

var binder = Binders.GetBinder(empType);

  var bindingContext = new ModelBindingContext()
  {
    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => emp, empType),
    ModelState = ModelState,
    ValueProvider = form
  };      

  binder.BindModel(ControllerContext, bindingContext);

  if (ModelState.IsValid)
  {
   _empRepo.Save(emp);

    return RedirectToAction("Index");
  }

return View();
}

(Lưu ý: trang web dường như ngừng hoạt động, liên kết là đến archive.org)


Cảm ơn sự giúp đỡ của bạn, Hiện tại, tôi không sử dụng MVC, ví dụ (Có thể có nhiều hơn 1 tham số đầu vào) Tôi cần giải quyết các tham số theo tên. Ngoài ra, tôi đang sử dụng .Net-Core Tôi nghĩ rằng điều này được viết cho phiên bản cũ .net. Vui lòng hoàn thành sơ khai phương thức: để câu trả lời được chấp nhận:this.Resolve<MyCustomType>("MyParamName");
johnny 5/11

Vì tôi không có môi trường để tái tạo điều này một cách tối thiểu, tôi sẽ không thể làm điều đó - tôi xin lỗi vì đã bỏ lỡ yêu cầu dotnetcore.
Brian nói Phục hồi Monica

Tôi có thể dịch nó sang .Net-Core, điều đó tốt, Nếu bạn chỉ cho tôi cách giải quyết theo tên tham số tôi sẽ chấp nhận
johnny 5/11

Đây là điều gần nhất với câu trả lời tôi muốn vì vậy tôi sẽ đưa cho bạn tiền thưởng
johnny 5/11

0

Tôi đã kết thúc bằng văn bản bộ điều khiển năng động. Để giải quyết vấn đề như một công việc xung quanh.

private static TypeBuilder GetTypeBuilder(string assemblyName)
{
    var assemName = new AssemblyName(assemblyName);
    var assemBuilder = AssemblyBuilder.DefineDynamicAssembly(assemName, AssemblyBuilderAccess.Run);
    // Create a dynamic module in Dynamic Assembly.
    var moduleBuilder = assemBuilder.DefineDynamicModule("DynamicModule");
    var tb = moduleBuilder.DefineType(assemblyName,
            TypeAttributes.Public |
            TypeAttributes.Class |
            TypeAttributes.AutoClass |
            TypeAttributes.AnsiClass |
            TypeAttributes.BeforeFieldInit |
            TypeAttributes.AutoLayout,
            null);

    return tb;
}

Bây giờ tôi đang khó mã hóa func trong phương thức nhưng tôi chắc chắn bạn có thể tìm ra cách truyền nó xuống nếu bạn cần.

public static Type CompileResultType(string typeSignature)
{
    TypeBuilder tb = GetTypeBuilder(typeSignature);

    tb.SetParent(typeof(DynamicControllerBase));

    ConstructorBuilder ctor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

    // For this controller, I only want a Get method to server Get request
    MethodBuilder myGetMethod =
        tb.DefineMethod("Get",
            MethodAttributes.Public,
            typeof(String), new Type[] { typeof(Test), typeof(String) });

    // Define parameters
    var parameterBuilder = myGetMethod.DefineParameter(
        position: 1, // 0 is the return value, 1 is the 1st param, 2 is 2nd, etc.
        attributes: ParameterAttributes.None,
        strParamName: "test"
    );
    var attributeBuilder
        = new CustomAttributeBuilder(typeof(FromServicesAttribute).GetConstructor(Type.EmptyTypes), Type.EmptyTypes);
    parameterBuilder.SetCustomAttribute(attributeBuilder);

    // Define parameters
    myGetMethod.DefineParameter(
        position: 2, // 0 is the return value, 1 is the 1st param, 2 is 2nd, etc.
        attributes: ParameterAttributes.None,
        strParamName: "stringParam"
    );

    // Generate IL for method.
    ILGenerator myMethodIL = myGetMethod.GetILGenerator();
    Func<string, string> method = (v) => "Poop";

    Func<Test, string, string> method1 = (v, s) => v.Name + s;

    myMethodIL.Emit(OpCodes.Jmp, method1.Method);
    myMethodIL.Emit(OpCodes.Ret);

    return tb.CreateType();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.