Entity Framework 4.0 在插入之前自动截断/修剪字符串
假设我有一个表,其中包含描述列 varchar(100)。如果尝试插入超过 100 个字符的字符串,则插入将失败。
实体框架中是否有一种方法可以在插入列之前自动截断或修剪字符串以适合该列?在我的场景中,我真的不在乎字符串是否被截断,我只想插入它而不是仅仅失败并记录错误。
由于模型已经知道长度限制,因此我认为实体框架可能有一种方法可以为我做到这一点。
如果不支持,最好的方法是什么?扩展自动生成的部分类并重写 On*Changed 方法?我不想对长度限制进行硬编码,而是使用实体模型中已定义的长度限制。我怎样才能访问这个?
编辑
我的最终解决方案是实现自动生成实体的 On*Changed 部分方法。
我用了 这个方法从实体实例中获取ObjectContext,然后使用下面的方法提取最大长度,并截断字符串。
Suppose I have a table with the column Description, varchar(100). If try to insert a string with more than 100 characters, the insert will fail.
Is there a way in Entity Framework to automatically truncate or trim the string to fit into the column before inserting into the column? In my scenario, I really don't care whether the string is truncated, I just want it inserted rather than just failing and logging the rror.
Since the model already knows the length limits, I was thinking there might be a way for Entity Framework to do this for me.
If this is not supported, what is the best way to do this? Extend the auto-generated partial classes and override the On*Changed methods? I would prefer not to hard-code the length limits, but rather use the length limits already defined in the entity model. How could I get access to this?
Edit
My final solution was to implement the On*Changed partial method of the autogenerated entity.
I used this method of getting the ObjectContext from the entity instance, and then used the below method to extract the max length, and truncate the string.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是我的单行解决方案
(调用它是一行,实现多一点)
我从 @elbweb 获取代码并根据我的目的进行调整。就我而言,我正在解析 EDI 文件,其中一些文件具有 15 个不同的层次结构级别,我不想显式指定所有 15 种不同类型 - 我想要一个适用于所有实体类型的单行代码。
虽然有点不同,但现在打电话很方便。这肯定会对性能造成影响,但对我来说是可以接受的。本质上,将其放入 DbContext 类中,然后手动调用它(或者您可以通过覆盖 SaveChanges 来调用它来自动调用它)。
DbContext 中的代码:
消耗
在此代码之前,当您尝试插入太大的字符串时,
SaveChanges
将触发上面示例中的捕获。添加TruncateAllStringsOnAllEntitiesToDbSize
行后,现在效果很好!我确信可以进行一些优化,所以请批评/贡献! :-)注意:我只在 EF 6.1.3 上尝试过此操作
Here's my One-Line Solution
(invoking it is one line, the implementation is a little more)
I took the code from @elbweb and adapted it for my purposes. In my case I was parsing EDI files, some of which had 15 different levels to the hierarchy and I didn't want to explicitly specify all 15 different types - I wanted a one-liner that worked for all entity types.
It's a bit different but it's now painless to call. There is definitely a performance hit on this but it's acceptable for me. Essentially put this inside of your DbContext class and then it's a one-liner to manually call (or you can automatically call it by overriding SaveChanges to invoke it).
Code in your DbContext:
Consumption
Before this code, the
SaveChanges
would trigger the catch in my example above when you tried inserting a string that was too large. After adding theTruncateAllStringsOnAllEntitiesToDbSize
line, it works great now! I'm sure there are some optimizations that can go into this, so do please critique/contribute! :-)Note: I have only tried this on EF 6.1.3
这将为您提供列的最大长度。
This will give you the max length of a column..
我从理查德的答案中获取了一些逻辑,并将其转化为一种方法,用于根据实体框架对象的最大长度(如果它们受到限制)截断所有字符串。
I took some of the logic from Richard's answer and turned it into a method to truncate all strings of an entity framework object based on their max length, if they're limited.
我使用了稍微不同的策略,但也利用了 On*Changed 方法。我正在使用 EF 使用的 .tt 文件的精简版本生成部分类。相关部分是生成属性的地方。最大长度可用,可用于截断字符串。
I used a slightly different tack, but also utilizing the On*Changed methods. I'm generating partial classes using a stripped down version of the .tt file used by EF. The relevant section is where the properties are generated. The Maximum length is available and can be used to truncate the string.
此方法使用对象属性的属性,因此它适用于 EF 或其他场景。如果属性具有“StringLength”属性,它将被截断。
使用此示例属性测试正确(由于 StringLength)
This approach uses the attributes of the object properties, so it works with EF or perhaps other scenarios. If a property has a "StringLength" attribute, it will be truncated.
this tested correctly using this example property (due to StringLength)
我将针对这个问题提出一个独特的解决方案。
在我的用例中,我希望更好地控制自动截断哪些字段。
因此,我创建了一个属性来注释我想要截断的实体属性:
接下来,我需要一种实际截断实体的方法。我希望在保存实体时作为预处理步骤自动发生此操作。
因此,我重写了 DbContext.SaveAs() 方法,并为截断功能添加了一个存根方法:
我可以在此时停止,只需复制其他答案中的代码并将其粘贴到
>PreProcessEntities()
方法。例如,我可以循环遍历添加和修改的实体,获取实体类型,使用 AutoTruncate 和 MaxLength 属性查找属性,然后根据需要截断属性值。但!这种方法存在一些问题。
例如,就我而言,我还希望一些包含数值的字符串属性以不同的方式格式化。
所以我想知道是否可以缓存每个实体类型要执行的操作。事实证明...有:)。它需要一些代码,但很容易遵循和实现。
首先,我需要一个线程安全的单例来缓存每个实体的操作。然后我想要一个可以分析实体类及其属性的类或方法。分析结果将是在保存到数据库之前应用于该类型实例的操作列表。
我创建了三个类/接口:
IEntityProcessor
是一个接受实体并对其执行某些操作的接口AutoTruncateProcessor
实现IEntityProcessor
并截断实体的属性已标记有AutoTruncate
和MaxLength
EntityProcessorChecker
分析实体的某些属性,例如AutoTruncate
并生成一个列表IEntityProcessor
实例。EntityProcessorCache
为每个实体维护一个IEntityProcessor
的线程安全缓存。命名不是我的强项,但希望你能明白。
首先,我将向您展示`DbContext.PreProcessEntities() 的实现。
这可以使用泛型来实现,也可以不使用泛型来实现。
第一种方法不使用泛型。缺点是我们正在访问添加或修改的每个实体的缓存。这可能会增加对性能的影响。
第二种方法使用泛型。缺点是我们需要使用显式类型显式调用泛型方法。也许有一种方法可以使用反射来解决这个问题。好处是每种类型只能访问缓存一次,如果没有操作则可以提前退出。
最后,这里是
IEntityProcessor
、AutoTruncateProcessor
、EntityProcessorChecker
和EntityProcessorCache
的实现。EntityProcessorChecker
会检查给定类型的属性,并在找到匹配的属性时实例化IEntityProcessor
的子类。您可以在此处根据自己的应用程序需求自定义功能。在我的实现中,我想查找 AutoTruncate 和 NumberFormat 属性。EntityProcessorCache
意味着我们不需要持续分析实体类型来了解我们需要在其实例上执行的操作。这是一个优化步骤。单例设计的灵感来自于这篇文章。哇,有很多代码和解释!
希望您能看到这种方法的一些好处。
我喜欢的是,我可以快速轻松地创建新属性来标记我的实体属性,并使用处理器在保存之前修改我的实体。
当然,还有很大的改进空间:
EntityProcessorChecker
可能应该进行重构,以使分析过程在查看实体时更加可扩展且更加明显。定义和检查新属性并创建相应的处理器应该很容易。EntityProcessorCache
的设计方式与DbContext
在 .Net Framework 中的构建方式有关。在 .Net Core 中,我认为我们可以使用内置的 DI 系统来创建和管理单例实例。只要 DbContext 能够访问它,我就假设缓存将通过构造函数传入。I'm going to present a unique solution to this problem.
In my use-case I wanted greater control over which fields are auto-truncated.
So, I created an attribute to annotate the entity properties I want truncated:
Next, I needed a way to actually truncate the entity. I want this to happen automatically as a pre-process step when the entity is saved.
So, I override the
DbContext.SaveAs()
methods and added a stub method for my truncation functionality:I could stop at this point and just copy the code from the other answers and paste it into the
PreProcessEntities()
method. Eg I could loop through the added and modified entities, get the entity type, find properties with theAutoTruncate
, andMaxLength
attributes, then truncate the property value if needed.BUT! There are a couple of problems with this approach.
Eg In my case, I also want some string properties containing numerical values to be to be formatted in different ways.
SaveAs()
must give a noticeable performance hit.So I wondered if it possible to cache the actions to be performed for each entity type. Turns out... there is :). It takes a bit of code but is easy enough to follow and implement.
Firstly, I need a thread-safe singleton to cache the actions for each entity. Then I want a class or method that can analyse an entity class and its attributes. The result of the analysis will be a list of actions to be applied to instances of that type before being saved to the database.
I created three classes/interfaces:
IEntityProcessor
is an interface that accepts an entity and performs some action on itAutoTruncateProcessor
implementsIEntityProcessor
and truncates the entity's properties which have been marked withAutoTruncate
andMaxLength
EntityProcessorChecker
analyses an entity for certain attributes such asAutoTruncate
and produces a list ofIEntityProcessor
instances.EntityProcessorCache
maintains a thread-safe cache ofIEntityProcessor
for each entity.Naming isn't my strong suit, hopefully you get the idea though.
First, I'll show you the implementation of `DbContext.PreProcessEntities().
This can be implemented either using generics or not.
The first approach is without generics. The drawback is that we are accessing the cache for every entity being added or modified. This which may increase the hit to performance.
The second approach uses generics. The drawback is we need to explicitly call our generic method with explicit types. Maybe there's a way around this using reflection. The benefit is the cache is accessed only once per type, and an early exit if there are no actions.
Finally, here is the implementation for
IEntityProcessor
,AutoTruncateProcessor
,EntityProcessorChecker
, andEntityProcessorCache
.The
EntityProcessorChecker
looks through the properties of the given type and instantiates subclasses ofIEntityProcessor
when it finds matching attributes. This is where you customise the functionality based on your own applications needs. In my implementation I want to look forAutoTruncate
andNumberFormat
attributes.The
EntityProcessorCache
means we don't need to continually analyse entity types for the actions we need to perform on their instances. It's an optimisation step. The singleton design was inspired by this article.Wow that was a lot of code and explanation!
Hopefully you can see some benefits in this approach.
I like that I can quickly and easily create new attributes to mark up my entity properties and processers to modify my entities before save.
There is of course plenty of room for improvements:
EntityProcessorChecker
should probably be refactored to make the analysis process more extensible and explicitly obvious when looking at an entity. It should be easy to define and check for new attributes and create corresponding processors.EntityProcessorCache
was designed the way it was because of how theDbContext
is built in .Net Framework. In .Net Core, I presume we could use the built-in DI system to create and manage the singleton instance instead. As long as theDbContext
is able to access it, I assume the cache would be passed in via the constructor.