我正在调用我的WebAPI的方法,发送一个我希望与模型匹配(或绑定)的json.
在控制器中我有一个方法,如:
public Result Post([ModelBinder(typeof(CustomModelBinder))]MyClass model);
Run Code Online (Sandbox Code Playgroud)
'MyClass',作为参数给出的是一个抽象类.我想这样,根据传递的json的类型,实例化正确的继承类.
为了实现它,我正在尝试实现自定义绑定器.问题是(我不知道它是否非常基本,但我找不到任何东西)我不知道如何检索请求中的原始Json(或更好的,某种序列化).
我知道了:
但是所有方法都暴露为异步.我不知道这适合将生成模型传递给控制器方法...
非常感谢!
更新(2016 年9月21日) - 感谢Digbyswift评论此解决方案仍然适用于MVC5.
更新(2012 年4月30日) - 注意那些在搜索等问题上遇到这个问题的人 - 接受的答案不是我最终如何做到这一点 - 但我接受了它,因为它可能在某些情况下有效. 我自己的答案包含我使用的最终解决方案,它可以重复使用并适用于任何项目.
它也被证实可以在MVC框架的v3和v4中工作.
我有以下模型类型(类的名称及其属性已更改以保护其身份):
public class MyExampleModel
{
public string[] LongPropertyName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
然后将此属性绑定到一堆(> 150)复选框,其中每个输入名称当然都是LongPropertyName.
表单使用HTTP GET提交到url,并说用户选择其中三个复选框 - url将具有查询字符串 ?LongPropertyName=a&LongPropertyName=b&LongPropertyName=c
那么很大的问题是,如果我选择所有(甚至只有一半!)复选框,我超过了IIS上请求过滤器强制执行的最大查询字符串长度!
我不想扩展它 - 所以我想要一种方法来减少这个查询字符串(我知道我可以切换到POST - 但即便如此,我仍然希望最小化客户端发送的数据中的绒毛数量) .
我想要做的是LongPropertyName绑定到简单的'L',因此查询字符串将成为?L=a&L=b&L=c但不更改代码中的属性名称.
有问题的类型已经有一个自定义模型绑定器(从DefaultModelBinder派生),但它附加到它的基类 - 所以我不想在那里为派生类放置代码.所有属性绑定当前都由标准的DefaultModelBinder逻辑执行,我知道它使用System.ComponentModel中的TypeDescriptors和Property Descriptors等.
我有点希望可能有一个属性我可以申请该物业来完成这项工作 - 是吗?或者我应该考虑实施ICustomTypeDescriptor?
该项目是Asp.Net Web API Web服务.
我有一个类型层次结构,我需要能够序列化到JSON和从JSON序列化,所以我从这个SO中获取代码:如何在JSON.NET中实现自定义JsonConverter来反序列化基类对象的列表?,并将转换器应用于我的层次结构的基类; 像这样的东西(这里有伪代码隐藏无关紧要):
[JsonConverter(typeof(TheConverter))]
public class BaseType
{
// note the base of this type here is from the linked SO above
private class TheConverter : JsonCreationConverter<BaseType>
{
protected override BaseType Create(Type objectType, JObject jObject)
{
Type actualType = GetTypeFromjObject(jObject); /*method elided*/
return (BaseType)Activator.CreateInstance(actualType);
}
}
}
public class RootType
{
public BaseType BaseTypeMember { get; set; }
}
public class DerivedType : BaseType
{
}
Run Code Online (Sandbox Code Playgroud)
所以,如果我反序列化RootType实例,其BaseTypeMember等于实例DerivedType,然后将它反序列化回到该类型的实例.
对于记录,这些JSON对象包含一个'$type' …
我有一个具有许多Nullable <T>属性的类,我想将其作为属性序列化为XML.这显然是禁忌,因为它们被认为是"复杂类型".所以,我实现了*Specified模式,我在其中创建了一个add*Value和*Specified属性,如下所示:
[XmlIgnore]
public int? Age
{
get { return this.age; }
set { this.age = value; }
}
[XmlAttribute("Age")]
public int AgeValue
{
get { return this.age.Value; }
set { this.age = value; }
}
[XmlIgnore]
public bool AgeValueSpecified
{
get { return this.age.HasValue; }
}
哪个工作正常 - 如果'Age'属性有值,则将其序列化为属性.如果它没有值,则不会序列化.
问题在于,正如我所提到的,我的班级中有很多Nullable-s,这种模式只是让事情变得混乱和无法管理.
我希望有一种方法可以使Nullable更友好的XmlSerializer.或者,如果失败了,那就是创建Nullable替换的方法.
有没有人有任何想法我怎么能这样做?
谢谢.
更新:我现在有了一个解决方案,我对此更加满意,虽然没有解决我提出的所有问题,但它确实让我们明白了.我已经更新了自己的答案以反映这一点.
原始问题
给定App域,Fusion(.Net程序集加载器)将针对给定程序集进行探测,有许多不同的位置.显然,我们认为这个功能是理所当然的,因为探测似乎嵌入在.Net运行时Assembly._nLoad内部(内部方法似乎是反射加载时的入口点 - 我假设隐式加载可能由同一个底层覆盖算法),作为开发人员,我们似乎无法访问这些搜索路径.
我的问题是我有一个组件可以执行大量动态类型解析,并且需要能够确保在开始工作之前预先加载给定AppDomain的所有用户部署的程序集.是的,它减慢了启动速度 - 但我们从这个组件中获得的好处完全超出了这一点.
我已经编写的基本加载算法如下.它深扫描任何.dll文件(.EXE文件被排除一组文件夹的那一刻),并使用Assembly.LoadFrom如果它的AssemblyName不能在集已经加载到AppDomain中组件可以找到加载DLL(这实现效率低下,但可以在以后进行优化):
void PreLoad(IEnumerable<string> paths)
{
foreach(path p in paths)
{
PreLoad(p);
}
}
void PreLoad(string p)
{
//all try/catch blocks are elided for brevity
string[] files = null;
files = Directory.GetFiles(p, "*.dll", SearchOption.AllDirectories);
AssemblyName a = null;
foreach (var s in files)
{
a = AssemblyName.GetAssemblyName(s);
if (!AppDomain.CurrentDomain.GetAssemblies().Any(
assembly => AssemblyName.ReferenceMatchesDefinition(
assembly.GetName(), a)))
Assembly.LoadFrom(s);
}
}
Run Code Online (Sandbox Code Playgroud)
使用LoadFrom是因为我发现使用Load()会导致Fusion加载重复的程序集,如果它在探测它时,它找不到从它希望找到它的地方加载的程序集.
因此,有了这个,我现在要做的就是获得Fusion在搜索程序集时将要使用的搜索路径的优先顺序(从高到低)的列表.然后我可以简单地遍历它们.
GAC与此无关,我对Fusion可能使用的任何环境驱动的固定路径不感兴趣 - 只有那些可以从AppDomain收集的路径,其中包含为应用程序明确部署的程序集.
我的第一次迭代只使用了AppDomain.BaseDirectory.这适用于服务,表单应用程序和控制台应用程序.
但是,它不适用于Asp.Net网站,因为至少有两个主要位置 - AppDomain.DynamicDirectory(其中Asp.Net放置动态生成的页面类和Aspx页面代码引用的任何程序集),以及然后是站点的Bin文件夹 - 可以从AppDomain.SetupInformation.PrivateBinPath属性中找到它. …
首先我在电话上,所以请原谅糟糕的格式!
我现在已经做了很多搜索,但没有找到明确的答案.如果没有一个,那么公平,但我确信有人比我必须有一个好答案更聪明!
我正在使用RNG加密提供程序以真正天真的方式生成范围内的数字:
byte[] bytes = new byte[4];
int result = 0;
while(result < min || result > max)
{
RNG.GetBytes(bytes);
result = BitConverter.ToInt32(bytes);
}
Run Code Online (Sandbox Code Playgroud)
当范围足够宽以便有可能获得结果时,这是很好的,但是今天早些时候我遇到的范围足够小(在10,000个数字内)可能需要一个年龄.
所以我一直在努力想出一个更好的方法来实现合理的分配,但会更快.但现在我正在深入研究我在学校里没有做过的数学和统计数据,或者至少如果我这样做,我已经忘记了这一切!
我的想法是:
就像我说的那样,这可能非常幼稚,但我相信它会在比目前的实施更快的范围内返回一个匹配.我现在不在电脑前所以无法测试,将在明天早上英国时间这样做.
但当然速度并不是我唯一关心的问题,否则我只会使用随机(如果有人愿意的话,那里需要一些刻度线来正确格式化 - 它们不在Android键盘上!).
我对上述方法的最大担忧是,我总是丢掉由prng生成的最多7位,这看起来很糟糕.我想到了将它们考虑在内的方法(例如一个简单的添加),但它们看起来非常不科学的黑客!
我知道mod技巧,你只需要生成一个序列,但我也知道它的弱点.
这是死路一条吗?最终,如果最好的解决方案是坚持当前的实现,我会觉得必须有更好的方法!
所以我开始使用<%: Url.Content(~/site/blah) %>语法作为CSS,JScript和Image URL的标准 - 确实解决了很多问题; 它至少在WebForms和Razor页面之间是一致的(并不是我的所有开发人员都会做Razor,但他们仍然会在我制作的这个平台上工作).
但是,对于我目前正在做的事情,我真的可以采用一种方法来获取Razor页面中编写的相对Url,并在运行时将其解析为正确的服务器端文件,然后再将其转换为客户端的绝对URL. Url.Content对相对网址没有任何作用.
所以,基本上,我想要一个ResolveUrl或ResolveClientUrl等于Razor级别.
我希望这能够在我的一些MVC视图中启用terser(并且更容忍重命名)资源路径,这些视图可以是从根目录下方的几个文件夹,其内容文件夹将更容易表示为相对路径 - 所以我本可以有:
folder\folder\views\shared\layout.cshtml
和
folder\folder\content\site.css
(我还推断使用布局页面,以反映ResolveUrl解决的问题类型以及WebForms所做的重新编写)
Url.Content按原样使用,我需要指定完整路径:
Url.Content("~/folder/folder/content/site.css")
但我想要的是
Url.Content("../../site.css")
当然,无论当前请求的路径中有多少条路径,都可以完成这项工作.
当然,如果我放弃Url.Content调用并依赖url rebasing,我可以在WebForms中使用它.
Razor有没有相应的东西?
这很奇怪.我们一直试图弄清楚它,但它确实没有任何意义.
我们的Web项目导入目标文件,其目标类似于:
<Target Name="CSSCheckInternal">
<ItemGroup>
<CSSFiles Include="$(MSBuildProjectDirectory)\**\*.css" />
</ItemGroup>
<CSSChecker Files="@(CSSFiles)" />
</Target>
Run Code Online (Sandbox Code Playgroud)
目前,一个分支正在完美建设,按要求执行任务; 但是另一个分支在上述目标上失败了.
失败的原因是@(CSSFiles),当任务收到该项时,该项似乎不会扩展为ITaskItem数组.
该任务编写如下(直到我获得FullPath元数据):
public class CSSChecker : Task
{
[Required]
public ITaskItem[] Files
{
get;
set;
}
public override bool Execute()
{
string fullFilePath = null;
if (Files != null)
{
foreach (var item in Files)
{
fullFilePath = item.GetMetadata("FullPath");
if(!File.Exists(fullFilePath))
throw new InvalidOperationException(
string.Format("{0} does not exist", fullFilePath));
//rest of the code elided
Run Code Online (Sandbox Code Playgroud)
失败的构建是抛出InvalidOperationException最后一行,如下所示:
文件不存在:C:\ Code\Project\**\*.css
因此,似乎MSBuild不是在Include属性中扩展通配符,而只是将字符串传递过来,因此 …
我刚刚写了一个属性设置器,并且脑子里有一个关于为什么我们没有得到一个属性可能涉及操作符链接return的结果的脑波,即:set=
var a = (b.c = d);
Run Code Online (Sandbox Code Playgroud)
(为了清楚起见,我添加了括号 - 但在实践中没有区别)
我开始思考 - C#编译器在哪里导出a上面示例中赋值的值?
逻辑说它应该来自(b.c = d)操作的结果,但是因为用一种void set_blah(value)方法实现它不可能.
所以唯一的其他选择是:
b.c在分配后重新读取并使用该值再利用 d
编辑(自从Eric回答和评论) - 还有第三个选项,就是C#所做的:b.c在任何转换发生后使用写入的值
现在,在我看来,正确阅读上面的代码行是
设置
a到设定的结果b.c来d
我认为这是一个合理的代码阅读 - 所以我想我会测试这是否确实是一个稍微做作的测试会发生什么 - 但问问自己是否认为它应该通过或失败:
public class TestClass
{
private bool _invertedBoolean;
public bool InvertedBoolean
{
get
{
return _invertedBoolean;
}
set
{
//don't ask me why you would with a boolean,
//but consider rounding …Run Code Online (Sandbox Code Playgroud) 那么 - 正如问题主题所述 - 关于如何做到这一点的任何想法?
我一直在查看System.Web.Hosting中的对象,但没有什么是突出的.
原因?我收到了一两个应用程序错误,这些错误通常在回收期间发生(它们相隔大约25小时发生,我将应用程序池的回收时间保留为默认值)所以我想知道它们是否发生在正在关闭的池中的线程,或者正在启动(ed/ing)的线程.
c# ×8
asp.net-mvc ×3
.net ×2
asp.net ×2
assemblies ×1
attributes ×1
iis ×1
iis-7 ×1
json.net ×1
msbuild ×1
nullable ×1
operators ×1
random ×1
razor ×1
reflection ×1
security ×1