rx 绑定自定义cell_使用网络核心自定义模型绑定

rx 绑定自定义cell

介绍 (Introduction)

In .Net Core, there are two types of custom binding, binding to Entity objects in Entity Framework and binding to Model properties in an MVC application. Both are for translating objects to and from the raw data that is needed by the underlying model into something useable by the framework, be it a database or web-page. In general, Entity Framework binding is the more straightforward of the two, so I will cover that briefly in the first section. While the View to Model binding is more complex, having some pitfalls that are not obvious at first glance. Because this type of binding is inherently more complicated, I will be spending more time describing how to write and configure the supporting classes for using this binding.

在.Net Core中,有两种类型的自定义绑定,即在Entity Framework中绑定到Entity对象和在MVC应用程序中绑定到Model属性。 两者都用于将对象与基础模型所需的原始数据进行来回转换,以供框架使用,无论是数据库还是网页。 通常,实体框架绑定是两者中更直接的一个,因此我将在第一部分中简要介绍。 尽管“视图到模型”绑定更为复杂,但乍一看仍存在一些陷阱。 因为这种类型的绑定本质上更加复杂,所以我将花费更多的时间来描述如何编写和配置使用该绑定的支持类。

绑定实体框架实体对象 (Binding Entity Framework Entity Objects)

Binding this is straightforward. The purpose is to provide some conversion from your object to some object that the underlying database can handle. The underlying object can be a string, an XML object, a binary, anything that you want to stuff into an entity in the database that you can convert reliably back to your object. For usability, I decided to write most of the Entities to the database as strings in a format that parseable by any of the classes. This was a good balance between human-readable data in the database and a clean interface in the code. For each entity, the block of code that does this looks like:

绑定很简单。 目的是提供从您的对象到基础数据库可以处理的某些对象的某种转换。 基础对象可以是字符串,XML对象,二进制文件,也可以是您想要填充到数据库中的实体中的任何东西,您可以将它们可靠地转换回对象。 为了提高可用性,我决定将大多数实体以字符串形式写入任何类型的数据库,并将其解析为任何类。 这在数据库中的人类可读数据与代码中的简洁界面之间取得了很好的平衡。 对于每个实体,执行此操作的代码块如下所示:

builder.Entity<Star>().Property(p => p.Mass)
.HasConversion(m => m.ToString(), s => Mass.Parse(s));
builder.Entity<Star>().Property(p => p.Radius)
.HasConversion(m => m.ToString(), s => Length.Parse(s));
builder.Entity<Star>().Property(p => p.OrbitalDistance)
.HasConversion(o => o.ToString(), s => Length.Parse(s));
builder.Entity<Star>().Property(ss => ss.Location)
.HasConversion(l => l.ToString(), s => Point3D.Parse(s));
builder.Entity<Star>().Property(p => p.Temperature)
.HasConversion(t => t.ToString(), s => Temperature.Parse(s));

To have this work, ToString() must produce an output string that Parse() can well parse. Also, Parse must create a correct value for the object it is parsing or throw an Invalid Operation Exception if the string is recognizably not able to be parsed. Failing that return null if there is some other exception while attempting to parse the string.

为了完成这项工作,ToString()必须产生一个Parse()可以很好解析的输出字符串。 另外,如果可识别的字符串无法解析,则Parse必须为其解析的对象创建正确的值,否则将引发Invalid Operation Exception。 如果在尝试解析字符串时还有其他异常,则失败返回null。

The only caveat with this particular choice is that string operations are time-consuming. So, I chose to trade off some database performance for having my data be human-readable in the database table. This is a tradeoff that needs to be decided by each database designer for their specific application.

唯一需要特别注意的是,字符串操作非常耗时。 因此,我选择权衡一些数据库性能,以使我的数据在数据库表中易于阅读。 这是每个数据库设计人员都需要针对其特定应用程序做出的权衡。

This definition is needed for each property on each entity that is not mappable to a database type. These definitions are made in the Database Context OnModelCreateing() method, just like all of the other configurations for the Framework model. There is enough to configuring an Entity Model that discussing it will take its own article to cover in any depth.

对于无法映射到数据库类型的每个实体上的每个属性,都需要此定义。 这些定义是在Database Context OnModelCreateing()方法中进行的,就像Framework模型的所有其他配置一样。 配置实体模型已经足够了,对其进行讨论将以其自己的文章深入探讨。

For now, this gives a basic idea of how to configure conversions for Entity Framework object properties.

现在,这给出了有关如何配置实体框架对象属性的转换的基本思想。

Image for post
https://pixabay.com/users/qimono-1962238/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2125180">Arek Socha</a> from <a href=” https://pixabay.com/users/qimono-1962238/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2125180 "> Arek Socha </a>来自<a href =” https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2125180">Pixabay</a> https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2125180 “> Pixabay </a>

自定义绑定MVC模型属性 (Custom binding MVC model properties)

The first thing to realize about this custom binding is that it has significantly more moving parts in more places than its Entity Framework relative. The basic idea is the same, define a way to get the data from one form into another form where the underlying system and libraries do not have a direct conversion. However, due to the nature of web applications in general and MVC applications, specifically, the specific implementation is slightly less straightforward.

关于此自定义绑定的第一件事是,与它的Entity Framework相对,它在更多的地方具有更多的活动部件。 基本思想是相同的,定义一种将数据从一种形式转换为另一种形式的方法,其中基础系统和库没有直接转换。 但是,由于一般的Web应用程序和MVC应用程序的性质,特定的实现方式不太简单。

The first step somewhat similar to its simpler cousin. You need a method to turn the incoming data into the object you are working with. However, it can’t be as direct as calling Parse(), although that is what happens in the end. First, figure out what kind of binding object this is. Then decide what type of object you have bound to. Then decode the binding message, parse the binding message, and parse the value from the binding message, set the result in a response message, and tell the caller if the task has completed successfully or not.

第一步与其简单的表亲相似。 您需要一种将输入数据转换为正在使用的对象的方法。 但是,尽管最终会发生这种情况,但它不能像调用Parse()那样直接。 首先,找出这是哪种绑定对象。 然后确定绑定到的对象类型。 然后解码绑定消息,解析绑定消息,并从绑定消息中解析值,将结果设置为响应消息,并告诉调用方任务是否成功完成。

So to do this, you need two classes. The first is a class that inherits from IModelBinder this class must implement the BindModelAsync method. This method receives a ModelBindingContext as a parameter, and the majority of this method involves interpreting this object to decide if this specific context is both carrying a type you know how to deal with and has a valid value.

为此,您需要两个类。 第一个是从IModelBinder继承的类,该类必须实现BindModelAsync方法。 此方法将ModelBindingContext作为参数接收,并且该方法的大部分操作都涉及解释该对象,以确定此特定上下文是否同时携带您知道如何处理的类型并具有有效值。

I am going to break this method down into parts because it will be easier to explain. First, on entry, make sure that the context is both non-null and has an interface we know how to deal with:

我将把这种方法分解为几个部分,因为它更容易解释。 首先,在进入时,请确保上下文既非空又具有我们知道如何处理的接口:

public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext is null)
{
throw new ArgumentNullException(
$"{nameof(bindingContext)} must not be null");
}
if (!(bindingContext.Model is IQuantity q))
{
throw new ArgumentException(
$"{bindingContext.ModelMetadata.ModelType} is not an IQuantity");
}
QuantityType quantityType = q.Type;

The above code is directly from the template for this method except that I added the check for IQuantity — the base interface of all the types I am dealing with in this particular handler.

上面的代码直接来自此方法的模板,除了我添加了对IQuantity的检查— IQuantity是我在此特定处理程序中处理的所有类型的基本接口。

A couple of notes here, QuantityType is an Enum of the various types that IQuantity can take on and q.Type is overridden in each of the inherited objects to return. It’s QuantityType — which is part of the construction of the object, so I know it will be set in every object of this base interface.

这里有几点说明,QuantityType是IQuantity可以接受的各种类型的Enum,并且q.Type在每个继承的对象中都被覆盖以返回。 它是QuantityType-这是对象构造的一部分,因此我知道它将在此基本接口的每个对象中设置。

Then comes the first thing that makes me think that whoever designed these objects and interfaces was laying traps for the poor suckers who followed behind. This block gets the property name in your model that you are looking for and short returns if there is no property.

接下来是第一件事,使我认为设计这些对象和界面的人正在为那些紧随其后的可怜的吸盘迷住陷阱。 该块获取您正在寻找的模型中的属性名称,如果没有属性,则返回简短的返回值。

// ModelName is a horrible name, this is the Property Name of the property in
// the Model you are looking up. If you use the Model name, or the Type name
// of the object you are looking for this will fail right here due to
// not finding the correct value provider with this lookup.
string propertyName = bindingContext.ModelName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(propertyName);if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}

It should be fairly obvious since I put in a comment to make sure that no one who had to maintain this code later but the absolutely insanely named “ModelName” is actually the name of the property you are going to be processing. So, this block uses “GetValue(),” which you might assume “gets” the value, well you would be wrong. It retrieves a flag that tells you if there is actually a value available to get. If there isn’t, then return to the caller, that yes, we processed the nothing you sent to us. Then we move on to the next block of code that drives home how insane the designer was who designed this mess.

这很明显,因为我添加了评论以确保没有人以后需要维护此代码,但绝对疯狂的名为“ ModelName”实际上是您要处理的属性的名称。 因此,此块使用“ GetValue()”,您可能会假设它“获取”了值,那么您错了。 它检索一个标志,告诉您是否确实有一个值可以获取。 如果没有,请返回呼叫者,是的,我们处理了您发送给我们的所有内容。 然后,我们进入下一个代码块,该代码块使设计师了解设计此烂摊子的疯狂程度。

bindingContext.ModelState.SetModelValue(propertyName, valueProviderResult);var value = valueProviderResult.FirstValue;// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value))
{

Your next step is to set the property value into that variable you created above with GetValue(), and grab the first value out of it. Now I suspect that there are cases where you might want something other than the first value — except I have never seen any cases where there is anything except a first value in this variable. So, the implication being, maybe sometimes you get a Second or Third Value, and you need to know when you need to grab that instead? Anyway, check to see if the value is null, i.e., again, the caller sent in emptiness. If it is null, tell the caller we successfully parsed the nothingness they sent us.

下一步是将属性值设置为您在上面使用GetValue()创建的变量中,并从中获取第一个值。 现在,我怀疑在某些情况下您可能需要除第一个值以外的其他内容-除非我从未见过在此变量中除第一个值外还有其他任何情况的情况。 因此,隐含的含义是,也许有时您获得了第二或第三值,并且您需要知道何时需要使用它? 无论如何,请检查该值是否为空,即再次调用方为空。 如果为空,请告诉呼叫者我们已成功解析了他们发送给我们的内容。

Then we can get to the actual parsing. Which is straightforward, use the QuantityType we got above to figure out which parser to use, use TryParse, so we know if the parse succeeded, then set the return value, and either give an error return or return success with the value we parsed. Either way, the caller gets success as a return value. A general note, I would rather have the caller expect some kind of failure response on failure, but trying to return anything except a completed task caused exceptions and crashes. So, nope just stick with the generic — sure everything is great in here we finished, nope nothing wrong here response.

然后我们可以进行实际的解析。 这很简单,请使用上面获得的QuantityType来确定要使用的解析器,然后使用TryParse,这样我们就知道解析是否成功,然后设置返回值,并使用我们解析的值给出错误返回或返回成功。 无论哪种方式,调用者都将成功作为返回值。 总的来说,我希望调用者期望对失败的某种失败响应,但是尝试返回除已完成任务以外的任何内容都会导致异常和崩溃。 因此,不,只要坚持通用即可-确保我们完成的一切都很好,在这里,没有错,请回应。

object retValue;
bool success;
switch (quantityType)
{
case QuantityType.Acceleration:
{
success = Acceleration.TryParse(value, out Acceleration val);
retValue = val;
break;
}
case QuantityType.Area:
{
success = Area.TryParse(value, out Area val);
retValue = val;
break;
}
case QuantityType.Length:
{
success = Length.TryParse(value, out Length val);
retValue = val;
break;
}
case QuantityType.Mass:
{
success = Mass.TryParse(value, out Mass val);
retValue = val;
break;
}
case QuantityType.Speed:
{
success = Speed.TryParse(value, out Speed val);
retValue = val;break;
}
case QuantityType.Temperature:
{
success = Temperature.TryParse(value, out Temperature val);
retValue = val;
break;
}default:
{
throw new NotSupportedException(
$"{quantityType} binding not supported yet");
}
}if (!success)
{
// Non-integer arguments result in model state errors
bindingContext.ModelState.TryAddModelError(
propertyName, $"value must be parsable as a {quantityType}.");return Task.CompletedTask;
}bindingContext.Result = ModelBindingResult.Success(retValue);
return Task.CompletedTask;

So, that gets the parser side done. How do you set up to call this? Well, there are three parts to that, first is an attribute on each property that uses this specific class as it’s binder. This looks like:

这样,解析器就完成了。 您如何设置此称呼? 好吧,这包括三个部分,首先是每个属性的属性,该属性使用此特定类作为其绑定器。 看起来像:

[BindProperty(BinderType = typeof(QuantityValueEntityBinder), Name = "Temperature")]
public Temperature Temperature { get; set; }

You have to tell it what the type of the binder object that knows how to deal with this property type is and what the name of the property that you are binding to is. This may not be the best example because I have both type and property named Temperature. To be clear, what I am referring to is the property name that is set in the Name field of the Bind Property attribute.

您必须告诉它知道如何处理此属性类型的活页夹对象的类型是什么,以及要绑定到的属性的名称是什么。 这可能不是最佳示例,因为我同时拥有类型和属性,名称为Temperature。 明确地说,我指的是在“绑定属性”属性的“名称”字段中设置的属性名称。

If you stop here, nothing will happen, and you will spend time pulling your hair out, wondering why the hell this doesn’t work as advertised. The reason you might stop here is that I found several articles about this topic that got this far and stopped. But I am not going to do that. There are more steps, the next one is to write another class. This is a relatively simple class that does the binding between the model property and the class we wrote above. This class inherits from IModelBinderProvider and must implement the GetBinder() method. This method accepts a ModelBinderProviderContext as a parameter and returns an IModelBinder. See the connection? This is the factory class that knows how to create the IModelBinder class you created above. Mine looks like:

如果停在这里,将什么也不会发生,并且您将花费时间将头发拉出来,想知道为什么这根本不像广告中所说的那样起作用。 您可能会在这里停下来的原因是,我发现了有关该主题的几篇文章,这些文章已经停滞不前了。 但是我不会那样做。 还有更多步骤,下一个是编写另一个类。 这是一个相对简单的类,它在model属性和我们上面编写的类之间进行绑定。 此类从IModelBinderProvider继承,并且必须实现GetBinder()方法。 此方法接受ModelBinderProviderContext作为参数,并返回IModelBinder。 看到连接了吗? 这是工厂类,它知道如何创建上面创建的IModelBinder类。 我的看起来像:

using Microsoft.AspNetCore.Mvc.ModelBinding;
using UnitsNet;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using TevandelSupportLibrary.Classes.Interfaces;public class QuantityValueBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}if (context.Metadata.ModelType is IQuantity)
{
return new BinderTypeModelBinder(typeof(QuantityValueEntityBinder));
}
else if(context.Metadata.ModelType is IUnit3D)
{
return new BinderTypeModelBinder(typeof(SupportEntityBinder));
}
return null;
}
}
Image for post
https://pixabay.com/users/qimono-1962238/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1804499">Arek Socha</a> from <a href=” https://pixabay.com/users/qimono-1962238/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1804499 "> Arek Socha </a>来自<a href =” https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1804499">Pixabay</a> https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1804499 “> Pixabay </a>

However, you aren’t done yet. I found two or three articles that got this far, and I guess they assumed everyone knew the last step. Well, I didn’t know when I started doing this, and I am going to consider that some of you don’t either. So, the last step, you need to configure the framework to include this factory in its list of binding provider factories. If you miss this step, then nothing works, and you can sit with breakpoints in all of your classes, and they never happen. I’ll let you guess who that happened to, and the first two guesses don’t count.

但是,您尚未完成。 我发现有两到三篇文章走到了这一步,我想他们假设每个人都知道最后一步。 好吧,我不知道我什么时候开始这样做的,我将考虑其中一些人也不是。 因此,最后一步,您需要配置框架以将该工厂包括在其绑定提供程序工厂列表中。 如果您错过了这一步,那么什么都行不通,并且您可以在所有类中都带有断点,而这些断点永远不会发生。 我让你猜猜是谁发生的,前两个猜想不算在内。

The configuration happens in the Startup class in the ConfigureServices() method. When you add the Razor Pages service add additional MVC options like this:

该配置在ConfigureServices()方法的Startup类中进行。 添加Razor Pages服务时,请添加其他MVC选项,如下所示:

services.AddRazorPages()
.AddMvcOptions( options =>
{
options.ModelBinderProviders.Add(new QuantityValueBinderProvider());
});

After you do the above steps then your binder will be called whenever a property of the type you have bound shows up in a binding list in your controllers, like this:

完成上述步骤后,只要绑定类型的属性出现在控制器的绑定列表中,便会调用绑定器,如下所示:

public async Task<IActionResult> Edit(Guid id, [Bind("Id,BarycenterId,Mass,
Radius,Location,StarSystemId,Temperature,
AbsoluteMagnitude,SpectralClass,OrbitalDistance,Name,Created,
LastUpdated")]Star star)

The significant bit in this line is that the Temperature object in the binding will be parsed, allowing for entry of values like “75 F” or “21 C” as valid temperatures that will be converted to the appropriate temperature value. This might seem like a minor detail, but by hiding this detail from the people who write the controller and the view, it simplifies the use of the Temperature object. Extend that across all of the other unit types such as Mass, Distance, Speed, etc. and you get greater functionality for less effort even if the same person writes both sets of code. Write it once, test it once, use it many times.

该行中的重要位是将解析绑定中的“温度”对象,从而允许输入诸如“ 75 F”或“ 21 C”的值作为有效温度,并将其转换为适当的温度值。 这看起来似乎是次要的细节,但是通过向编写控制器和视图的人员隐藏此细节,可以简化Temperature对象的使用。 将其扩展到所有其他单位类型(例如质量,距离,速度等)上,即使同一人同时编写两组代码,也可以用更少的精力获得更多的功能。 编写一次,测试一次,多次使用。

结论 (Conclusion)

As sarcastic as I might have been in parts of the description of building the binding object structure, it is a tool that you should be aware of and use. It opens the possibility of using custom objects on your user interface without adding masses of code in your controller or middleware to support them. It lets you write and test code, put it into a package or library. Then to use it across multiple projects, enhancing your productivity and allowing you to write the exciting parts rather than rewriting the same code to do the same thing in every project. These types of tools and features are the things that make the difference between experienced software engineers who know their environment and novice’s who write the same block of code in every project. It may not be pleasant or fair, but when I see repeated code that doesn’t have some underlying purpose, I start looking for bugs related to inexperience.

在构建绑定对象结构的描述中,正如我可能曾经讽刺的那样,它是您应该了解和使用的工具。 它提供了在用户界面上使用自定义对象的可能性,而无需在控制器或中间件中添加大量代码来支持它们。 它使您可以编写和测试代码,并将其放入程序包或库中。 然后在多个项目中使用它,从而提高了生产率,并允许您编写令人兴奋的部分,而不用重写相同的代码在每个项目中执行相同的操作。 这些类型的工具和功能使有经验的软件工程师(他们知道自己的环境)和新手(在每个项目中编写相同的代码块)之间有所不同。 这可能不愉快也不公平,但是当我看到重复的代码没有任何潜在的目的时,我开始寻找与经验不足相关的错误。

Frankly, I even do this in my code. If I find myself repeating the same block of code more than two or three times, I will look hard at why I am repeating it and look for a way to factor it out into a base class, common method, or at least an interface. If two types have enough in common that I am writing the same method twice, then what else do they have in common? Do I really need two classes? Or would one do? Or is this a base class with two derived classes with the commonalities implemented in the base? These are all questions that experience makes me ask while I am architecting, designing, writing, testing, or even deploying projects. I may not be able to make the changes to that specific project, but it is something to keep in mind for the next project, or the one after that.

坦白说,我什至在我的代码中做到了。 如果我发现自己重复相同的代码块两次或三遍以上,我将认真思考为什么要重复它,并寻找一种将其分解为基类,通用方法或至少一个接口的方法。 如果两个类型的共同点足以让我编写两次相同的方法,那么它们还有什么共同点? 我真的需要两节课吗? 还是会做? 还是这是具有两个派生类的基类,并且具有在基中实现的共性? 这些都是我在设计,设计,编写,测试甚至部署项目时遇到的所有问题。 我可能无法对该特定项目进行更改,但是下一个项目或之后的一个项目需要牢记。

Good luck, and have fun.

祝好运并玩得开心点。

翻译自: https://medium.com/the-innovation/using-net-core-custom-model-binding-eb4b23b35c43

rx 绑定自定义cell

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值