Geç bağlama Denetleyiciye girdikten sonra Modelleri Dinamik Olarak Çöz


9

Bir denetleyicide bir eylem girdikten sonra bir modeli çözmek için bir yol arıyorum, sorunu tanımlamak için en basit yolu olurdu:

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

Neden yapmaya çalıştığım hakkında daha fazla bilgi arıyorsanız, resmin tamamını almak için okumaya devam edebilirsiniz.

TL; DR

Ben her zaman sorgu dizesinden çözülecek bir parametre adı verilen bir modeli bir isteği çözmek için bir yol arıyorum Nasıl dinamik olarak başlangıçtaki filtreleri kaydedebilirsiniz. Filtrelerimi kaydettirecek bir sınıfım var.

Başlangıç ​​sınıfımda dinamik olarak restServices ile filtreleri kaydedebilmek istiyorum. Kabaca şöyle görünüyor benim özel ControllerFeatureProvider geçmek için kullandığım bir seçenek var:

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

Denetleyicim seçenekleri takip eder ve bunları uç noktaları ve OData disk belleği için filtreler sağlamak üzere kullanır.

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 verilen bir modeli dinamik olarak çözmek nasıl anlamaya sorun yaşıyorum, modeli elde etmek için böyle bir şey yapmayı düşünürdüm ama bu çalışmayan sözde kod

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

Kaynağa Kazandıktan Sonra ModelBinderFactory ve ControllerActionInvoker'da bazı umut verici şeyler gördüm. Bu sınıflar boru hattında model bağlama için kullanılır,

Ben bir parametre adı QueryString, böyle bir şey çözmek için basit bir arayüz ortaya çıkarmak beklenir:

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

Ancak, bir modeli model bağlayıcıdan çözmek için gördüğüm tek yol sahte denetleyici tanımlayıcıları oluşturmak ve bir sürü şeyi alay etmektir.

Geç bağlı parametreleri denetleyicime nasıl kabul edebilirim?


2
Bunun için bir fayda görmüyorum. Ayrıca, bir dize parametresine dayalı olarak modeli bağlasanız bile ...., GetValueFor <T> gibi genel yöntemi kullanamazsınız, çünkü T derleme zamanı çözülmelidir ... Bu, arayan kişinin bilmesi gerektiği anlamına gelir türün dinamik olarak bağlanması amacını bozacak derleme zamanında T tipi. Bu, DynamicControllerBase mirasçısı TDTO türünü bilmeniz gerektiği anlamına gelir .... Deneyebileceğiniz bir şey parametre JSON almak ve her DynamicControllerBase uygulaması dize bir model ve viceversa serisini deserialize var.
Jonathan Alfaro

@Darkonekt 'AddFilter' yöntemine bakarsanız, fonkları kaydettiğinizde kapanışta saklanan yazılı genel parametrelere sahipsiniz. Biraz kafa karıştırıcı ama sizi yaşayabilir ve çalışabilirim
johnny 5

Json'a takmak istemiyorum, çünkü webapi'nin doğal olarak parametreleri çözme yolunu değiştirmek istemiyorum
johnny 5

Bu tür işlevselliğin gerekli olduğu kullanım durumu ve gerçek hayat senaryosu hakkında biraz daha açıklarsanız, bu çok yardımcı olacaktır. Muhtemelen daha basit bir çözüm bile var .. kim bilir. Kendime gelince, bazen işleri karmaşıklaştırmaktan hoşlanırım .. Sadece söyleyerek ..
Bay Blond

@ Mr.Blond Ben crud ve liste işlevselliği sağlayan genel bir dinlenme servisi var. Bazen hizmetlerimin get listesinden verileri filtrelemesi gerekir, ancak bir filtre sağlamak için ihtiyacım olan tüm hizmetin tamamını yazmak istemiyorum
johnny 5

Yanıtlar:


2

Düşüncelerine katılıyorum

hizmetlerin get listesinden verileri filtrelemesi gerekiyor, ancak bir filtre sağlamak için ihtiyacım olan tüm hizmetin tamamını yazmak istemiyorum

Neden olası her kombinasyon için bir widget / filtre / uç nokta yazmalısınız?

Tüm verileri / özellikleri elde etmek için basit işlemler sağlamanız yeterlidir. Daha sonra filtre (son kullanıcının izin vermek için GraphQL kullanmak modeli için) kendi ihtiyaçlarına.

Gönderen 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 kullanmayı düşünüyorum ama şu anki uygulamama çok bağlıyım
johnny 5

2

Bunu yaptık, kodumuz bu siteyi referans alıyor: https://prideparrot.com/blog/archive/2012/6/gotchas_in_explicit_model_binding

Özellikle, kodumuza bakarak, hile, denetleyici yönteminizde bir FormCollection'ı kabul etmek ve ardından model bağlayıcı, model ve form verilerini kullanmaktır:

Bağlantıdan alınan örnek:

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

(Not: site kapalı görünüyor, bağlantı archive.org'a)


Yardımınız için teşekkürler, Şu anda, MVC kullanmıyorum, örn. (1'den fazla giriş parametresi olabilir) Parametreleri adıyla çözmem gerekiyor. Ayrıca .Net-Core kullanıyorum. Bunun eski sürüm .net için yazılmış olduğunu düşünüyorum. Lütfen yöntem saplamasını tamamlayın: cevabın kabul edilmesi için:this.Resolve<MyCustomType>("MyParamName");
johnny 5

Bunu minimal olarak çoğaltmak için bir ortamım olmadığı için bunu yapamayacağım - dotnetcore gereksinimini kaçırdığım için özür dilerim.
Brian,

Net-Core'a çevirebilirim, sorun değil, bana parametre adı ile nasıl çözüleceğini gösterirsen kabul edeceğim
johnny 5

Bu istediğim cevaba en yakın şey, bu yüzden sana ödül vereceğim
johnny 5

0

Sonunda dinamik kontrolörler yazdım. Sorunu bir çözüm olarak çözmek için.

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

Şimdilik yöntemde func kodlama zor ama eminim eğer ihtiyacınız varsa onu aşağı geçmek anlayabiliyorum.

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();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.