การรวมภายหลังแก้ปัญหาแบบไดนามิกหลังจากเข้าสู่คอนโทรลเลอร์


9

ฉันกำลังมองหาวิธีแก้ไขโมเดลหลังจากเข้าสู่การกระทำในคอนโทรลเลอร์วิธีที่ง่ายที่สุดในการอธิบายปัญหาคือ:

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

หากคุณกำลังมองหาข้อมูลเพิ่มเติมว่าทำไมฉันถึงพยายามทำเช่นนั้นคุณสามารถอ่านต่อเพื่อรับภาพเต็ม

TL; DR

ฉันกำลังมองหาวิธีในการแก้ไขแบบจำลองคำขอโดยกำหนดชื่อพารามิเตอร์ที่จะได้รับการแก้ไขจากสตริงข้อความค้นหาฉันจะลงทะเบียนตัวกรองแบบไดนามิกได้อย่างไรจากการเริ่มต้น ฉันมีชั้นเรียนที่จะจัดการกับการลงทะเบียนตัวกรองของฉัน

ในระดับเริ่มต้นของฉันฉันต้องการที่จะสามารถลงทะเบียนตัวกรองแบบไดนามิกกับส่วนที่เหลือของฉันบริการ ฉันมีตัวเลือกที่ฉันใช้ส่งผ่านไปยัง ControllerFeatureProvider ที่กำหนดเองซึ่งมีลักษณะโดยประมาณดังนี้:

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;
        });
    }
}

ตัวควบคุมของฉันจะติดตามตัวเลือกและใช้ตัวเลือกเหล่านี้เพื่อให้ตัวกรองสำหรับจุดสิ้นสุดการเพจและ 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>();
    }
}

ฉันมีปัญหาในการหาวิธีแก้ไขโมเดลที่ได้รับ HttpContext แบบไดนามิกฉันคิดว่าจะทำสิ่งนี้เพื่อรับโมเดล แต่นี่เป็นรหัสหลอกที่ใช้ไม่ได้

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);
}

หลังจากขุดลงไปในแหล่งที่มาฉันเห็นสิ่งที่มีแนวโน้มModelBinderFactoryและControllerActionInvokerคลาสเหล่านี้ถูกใช้ในไพพ์ไลน์สำหรับการรวมโมเดล

ฉันคาดหวังว่าจะเปิดเผยอินเตอร์เฟสที่เรียบง่ายเพื่อแก้ไขชื่อพารามิเตอร์จาก QueryString บางอย่างเช่นนี้:

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

อย่างไรก็ตามวิธีเดียวที่ฉันเห็นการแก้ไขโมเดลจากตัวยึดโมเดลคือการสร้างตัวอธิบายคอนโทรลเลอร์ปลอมและจำลองสิ่งต่างๆ

ฉันจะยอมรับพารามิเตอร์ที่ จำกัด ไว้ในโปรแกรมควบคุมของฉันได้อย่างไร


2
ฉันไม่เห็นการใช้งานสำหรับสิ่งนี้ นอกจากนี้แม้ว่าคุณสามารถผูกโมเดลตามพารามิเตอร์สตริง .... คุณจะไม่สามารถใช้วิธีการทั่วไปเช่น GetValueFor <T> เนื่องจาก T ต้องได้รับการแก้ไขในเวลาคอมไพล์ ... หมายความว่าผู้โทรต้องรู้ ประเภทของ T ในเวลารวบรวมซึ่งจะเอาชนะวัตถุประสงค์ของการผูกประเภทแบบไดนามิก นั่นหมายความว่าผู้รับสืบทอดของ DynamicControllerBase จะต้องรู้ประเภทของ TDTO .... สิ่งหนึ่งที่คุณสามารถลองได้คือรับ JSON ในพารามิเตอร์และให้แต่ละการใช้งานของ DynamicControllerBase ทำให้สตริงเป็นแบบจำลองและผู้แทน
Jonathan Alfaro

@Darkonekt ถ้าคุณดูวิธี 'AddFilter' คุณจะต้องพิมพ์พารามิเตอร์ทั่วไปที่เก็บไว้ในสถานะปิดเมื่อคุณลงทะเบียน funcs มันค่อนข้างสับสน แต่ฉันรับรองคุณได้และสามารถทำงานได้
johnny 5

ฉันไม่ต้องการที่จะเสียบเข้ากับ JSON เพราะผมไม่ต้องการที่จะมีการเปลี่ยนแปลงวิธีการที่จะ WebAPI ธรรมชาติพารามิเตอร์แก้ไข
จอห์นนี่ 5

หากคุณจะอธิบายเพิ่มเติมเล็กน้อยเกี่ยวกับกรณีการใช้งานและสถานการณ์ในชีวิตจริงที่จำเป็นต้องใช้ฟังก์ชั่นประเภทนี้นั่นจะช่วยได้มาก อาจเป็นไปได้ว่ายังมีวิธีแก้ปัญหาที่ง่ายกว่าเดิม .. ใครจะรู้ สำหรับตัวฉันเองบางครั้งฉันก็อยากจะทำอะไรที่ยุ่งยาก .. แค่พูด ..
มิสเตอร์บลอนด์

@ Mr.Blond ฉันมีบริการส่วนที่เหลือทั่วไปที่ให้การทำงานที่หยาบและรับฟังก์ชั่นรายการ บางครั้งบริการของฉันต้องกรองข้อมูลจากรายการได้รับ แต่ฉันไม่ต้องการที่จะต้องมีการเขียนบริการทั้งหมดของทั้งหมดที่ฉันจำเป็นต้องที่จะให้ตัวกรอง
จอห์นนี่ 5

คำตอบ:


2

ฉันเห็นด้วยกับความคิดของคุณ

บริการจำเป็นต้องกรองข้อมูลจากรับรายการ แต่ฉันไม่ต้องการที่จะต้องเขียนบริการทั้งหมดของทั้งหมดที่ฉันต้องการเพื่อให้ตัวกรอง

เหตุใดจึงต้องเขียนวิดเจ็ต / ตัวกรอง / จุดสิ้นสุดสำหรับชุดค่าผสมที่เป็นไปได้ทั้งหมด

เพียงแค่ให้การดำเนินงานพื้นฐานเพื่อรับข้อมูล / คุณสมบัติทั้งหมด จากนั้นใช้ GraphQL เพื่ออนุญาตให้ผู้ใช้กรอง ( รุ่น ) ตามความต้องการ

จาก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.


ฉันได้พิจารณาใช้ GraphQL แต่ฉันผูกติดอยู่กับการใช้งานปัจจุบันของฉัน
johnny 5

2

เราได้ทำสิ่งนี้แล้วโค้ดของเราอ้างอิงถึงไซต์นี้: https://prideparrot.com/blog/archive/2012/6/gotchas_in_explicit_model_binding

โดยเฉพาะอย่างยิ่งเมื่อดูที่รหัสของเราสิ่งที่หลอกลวงคือการยอมรับ FormCollection ในวิธีการควบคุมของคุณจากนั้นใช้เครื่องผูกรุ่นรุ่นและข้อมูลในแบบฟอร์ม:

ตัวอย่างจากลิงค์:

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();
}

(หมายเหตุ: ไซต์ดูเหมือนจะไม่ทำงานลิงก์คือไปยัง archive.org)


ขอบคุณสำหรับความช่วยเหลือของคุณขณะนี้ฉันไม่ได้ใช้ MVC เช่น (อาจมีพารามิเตอร์อินพุตมากกว่า 1 รายการ) ฉันต้องแก้ไขพารามิเตอร์ด้วยชื่อ นอกจากนี้ฉันใช้. Net-Core ฉันคิดว่านี่เป็นลายลักษณ์อักษรสำหรับ. net รุ่นเก่า กรุณากรอกวิธีต้นขั้ว: สำหรับคำตอบที่จะได้รับการยอมรับ:this.Resolve<MyCustomType>("MyParamName");
จอห์นนี่ 5

เนื่องจากฉันไม่มีสภาพแวดล้อมในการทำซ้ำให้น้อยที่สุดฉันจะไม่สามารถทำเช่นนั้นได้ - ฉันต้องขออภัยที่ขาดข้อกำหนด dotnetcore
ไบรอันพูดว่า Reinstate Monica

ฉันสามารถแปลเป็นภาษาสุทธิ-Core ที่ดีของถ้าคุณแสดงให้ฉันถึงวิธีการแก้ปัญหาโดยใช้ชื่อพารามิเตอร์ฉันจะยอมรับ
จอห์นนี่ 5

นี่คือสิ่งที่ใกล้เคียงที่สุดกับคำตอบที่ฉันต้องการดังนั้นฉันจะมอบรางวัลให้คุณ
johnny 5

0

ฉันสิ้นสุดการเขียนตัวควบคุมแบบไดนามิก เพื่อแก้ปัญหาที่เกิดขึ้นเช่นการหลีกเลี่ยง

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;
}

ฉันกำลังเขียนโค้ด func ในวิธีนี้อย่างหนัก แต่ตอนนี้ฉันแน่ใจว่าคุณสามารถหาวิธีที่จะผ่านมันได้หากคุณต้องการ

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();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.