EntityFramework 6 - 处理用户定义的属性
EntityFramework 6 - Handling User-defined Attributes
祝大家新年快乐!我刚刚开始为经营工具租赁业务的客户建立 ASP.NET MVC 5 应用程序的数据模型。该解决方案的一部分涉及构建一个管理(后端)功能,管理员用户可以通过该功能 create/edit 自定义属性或附加到特定工具组中每个工具的工具元数据。我正在研究这样一个概念,即在运行时应用程序不应该知道元数据模式是什么。所以我从这个开始:
是的,我知道……另一个 EAV 噩梦!我知道如果数据被正确规范化,并且创建了相关索引,那么它应该不会太糟糕。但老实说,我看不到任何其他选择。例如:
博世电钻
- 工具组:钻头
- 品牌:博世(ListItem - 从
MetaAttributeListOption
table 预填充)
- 类型:无绳(listItem - 从
MetaAttributeListOption
table 预填充)
- 无钥匙卡盘:是(布尔)
- 电压:14.4 伏(文本)
- ...
现在这些属性将用于 3 个目的:
- 在前端显示为“规范”
- 用于前端过滤工具
- (可能)在报告中用于确定“热门品牌”(例如)
所以我想我为此坚持使用 RDBMS(SQL 服务器)。我知道一个流行的方法是使用一些 NoSQL 解决方案,但老实说,我没有太多的实践经验来将它与 MSSQL 结合使用.我可以将 Values
table 组合成一个 table,其中每个数据类型值都在其自己的列中,但这会让我有很多空值需要处理。
如果你能帮我解决以下问题,我还有以下问题:
- 这个模型能满足我的要求吗?我不确定我是否正确设计了
MetaAttributeListOption
table 的关系。
- 是否有这种 EAV 方法的替代方法?
- 假设我上面的模型(或其衍生模型)是我唯一的选择,我将如何使用 Entity Framework 6 来实现它?对于管理后端中的 ASP 查看页面,我想我需要某种 HTML 帮助程序来确定要呈现的正确编辑器,然后相应地进行填充。
非常感谢 Whosebug 社区对此提供的任何帮助。如果您需要更多信息,请告诉我,如果您认为它偏离主题,请不要关闭它,因为我相信我的问题与编程相关。谢谢!
编辑:
我开始悬赏我自己的 200 分...assisting/advising 我的问题 1 和 2 100 分,问题 3 100 分。谢谢
问题的模型看起来可行,并且关系配置正确,但如果有很多重复项,则可能会创建冗余的 OptionLabel。但是,我会做出一些更改和 de-normalizing 妥协。 (参见#3)
根据您的过滤和报告要求,以及对 MS 的相对舒适度SQL 我认为使用 RDBMS 是最好的选择
我已经看到下面显示的方法在其他一些开发人员的 API 中使用,它似乎是一个足够好的折衷方案,标准化程度较低,但使数据模型更简单并查询值更灵活
- 我添加了 MetaAttributeList 以允许一个列表应用于多个 MetaAttributes。在此模型中,布尔值将表示为 Yes/No ListOption。
- 该问题的模型要求搜索值检查 3 个 table 中的(其中之一),并且始终事先知道适用的 MetaAttribute
- 问题的模型,默认情况下使用 EF Code First,会有多个 CASCADE 路径的问题,这将需要使用 FluentApi(不是什么大问题,但可能不方便跟踪)
- 这种方法(可选?)要求在代码中而非数据库中处理有效 ListOption 条目的强制执行
- 显示不同类型的值不需要任何额外的工作即可正确呈现
管理界面需要检查 MetaAttribute.ListOption 以确定是否显示 TextBox 或 ListItem(如果 ListItemOptions 为 Yes/No,则可能显示复选框)
您可能想要为工具组添加另一个 table,以缩小呈现给用户的元属性
注意:由于未指定 EF 方法和语言,因此我使用了 EF Code First 和 VB.Net。 IMO 迁移和更容易过渡到 EF7 是使用 Code First 的充分理由。我更喜欢 VB.Net 的可读性,但如果需要,我会很乐意更改为 C#(或使用 this 转换器)。
Imports System.ComponentModel.DataAnnotations
Namespace Models
'I didn't bother specifying string lengths with <StringLength(#)>
Public Class HireTool
Public Property Id As Integer
'... other properties
'Navigation Properties
Public Overridable Property HireToolMetaAttributes As ICollection(Of HireToolMetaAttribute)
End Class
Public Class MetaAttribute
Public Enum MetaAttributeTypeEnum
Text = 1
ListItem = 2
End Enum
Public Property Id As Integer
Public Property Code As String
Public Property Label As String
Public Property Type As MetaAttributeTypeEnum
Public Property Required As Boolean
Public Property Position As Integer
'Navigation Properties
Public Overridable Property List As MetaAttributeList
End Class
Public Class MetaAttributeList
Public Property ID As Integer
Public Property Name As String
'Navigation Properties
<Required>
Public Property ListOptions As ICollection(Of MetaAttributeListOption)
End Class
Public Class MetaAttributeListOption
Public Property Id As Integer
Public Property OptionLabel As String
End Class
Public Class HireToolMetaAttribute
Public Property Id As Integer
<Schema.Index> <StringLength(1000)>
Public Property Value As String
<Required>
Public Overridable Property HireTool As HireTool
<Required>
Public Overridable Property MetaAttribute As MetaAttribute
End Class
End Namespace
编辑:这是生成的 SQL:
CREATE TABLE [dbo].[MetaAttributeLists] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_dbo.MetaAttributeLists] PRIMARY KEY CLUSTERED ([ID] ASC)
);
CREATE TABLE [dbo].[HireTools] (
[Id] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_dbo.HireTools] PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[MetaAttributeListOptions] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[OptionLabel] NVARCHAR (MAX) NULL,
[MetaAttributeList_ID] INT NULL,
CONSTRAINT [PK_dbo.MetaAttributeListOptions] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MetaAttributeListOptions_dbo.MetaAttributeLists_MetaAttributeList_ID] FOREIGN KEY ([MetaAttributeList_ID]) REFERENCES [dbo].[MetaAttributeLists] ([ID])
);
CREATE TABLE [dbo].[MetaAttributes] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Code] NVARCHAR (MAX) NULL,
[Label] NVARCHAR (MAX) NULL,
[Type] INT NOT NULL,
[Required] BIT NOT NULL,
[Position] INT NOT NULL,
[List_ID] INT NULL,
CONSTRAINT [PK_dbo.MetaAttributes] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MetaAttributes_dbo.MetaAttributeLists_List_ID] FOREIGN KEY ([List_ID]) REFERENCES [dbo].[MetaAttributeLists] ([ID])
);
CREATE TABLE [dbo].[HireToolMetaAttributes] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Value] NVARCHAR (1000) NULL,
[HireTool_Id] INT NOT NULL,
[MetaAttribute_Id] INT NOT NULL,
CONSTRAINT [PK_dbo.HireToolMetaAttributes] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.HireToolMetaAttributes_dbo.HireTools_HireTool_Id] FOREIGN KEY ([HireTool_Id]) REFERENCES [dbo].[HireTools] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.HireToolMetaAttributes_dbo.MetaAttributes_MetaAttribute_Id] FOREIGN KEY ([MetaAttribute_Id]) REFERENCES [dbo].[MetaAttributes] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_Value]
ON [dbo].[HireToolMetaAttributes]([Value] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_HireTool_Id]
ON [dbo].[HireToolMetaAttributes]([HireTool_Id] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_MetaAttribute_Id]
ON [dbo].[HireToolMetaAttributes]([MetaAttribute_Id] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_MetaAttributeList_ID]
ON [dbo].[MetaAttributeListOptions]([MetaAttributeList_ID] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_List_ID]
ON [dbo].[MetaAttributes]([List_ID] ASC);
祝大家新年快乐!我刚刚开始为经营工具租赁业务的客户建立 ASP.NET MVC 5 应用程序的数据模型。该解决方案的一部分涉及构建一个管理(后端)功能,管理员用户可以通过该功能 create/edit 自定义属性或附加到特定工具组中每个工具的工具元数据。我正在研究这样一个概念,即在运行时应用程序不应该知道元数据模式是什么。所以我从这个开始:
博世电钻
- 工具组:钻头
- 品牌:博世(ListItem - 从
MetaAttributeListOption
table 预填充) - 类型:无绳(listItem - 从
MetaAttributeListOption
table 预填充) - 无钥匙卡盘:是(布尔)
- 电压:14.4 伏(文本)
- ...
现在这些属性将用于 3 个目的:
- 在前端显示为“规范”
- 用于前端过滤工具
- (可能)在报告中用于确定“热门品牌”(例如)
所以我想我为此坚持使用 RDBMS(SQL 服务器)。我知道一个流行的方法是使用一些 NoSQL 解决方案,但老实说,我没有太多的实践经验来将它与 MSSQL 结合使用.我可以将 Values
table 组合成一个 table,其中每个数据类型值都在其自己的列中,但这会让我有很多空值需要处理。
如果你能帮我解决以下问题,我还有以下问题:
- 这个模型能满足我的要求吗?我不确定我是否正确设计了
MetaAttributeListOption
table 的关系。 - 是否有这种 EAV 方法的替代方法?
- 假设我上面的模型(或其衍生模型)是我唯一的选择,我将如何使用 Entity Framework 6 来实现它?对于管理后端中的 ASP 查看页面,我想我需要某种 HTML 帮助程序来确定要呈现的正确编辑器,然后相应地进行填充。
非常感谢 Whosebug 社区对此提供的任何帮助。如果您需要更多信息,请告诉我,如果您认为它偏离主题,请不要关闭它,因为我相信我的问题与编程相关。谢谢!
编辑: 我开始悬赏我自己的 200 分...assisting/advising 我的问题 1 和 2 100 分,问题 3 100 分。谢谢
问题的模型看起来可行,并且关系配置正确,但如果有很多重复项,则可能会创建冗余的 OptionLabel。但是,我会做出一些更改和 de-normalizing 妥协。 (参见#3)
根据您的过滤和报告要求,以及对 MS 的相对舒适度SQL 我认为使用 RDBMS 是最好的选择
我已经看到下面显示的方法在其他一些开发人员的 API 中使用,它似乎是一个足够好的折衷方案,标准化程度较低,但使数据模型更简单并查询值更灵活
- 我添加了 MetaAttributeList 以允许一个列表应用于多个 MetaAttributes。在此模型中,布尔值将表示为 Yes/No ListOption。
- 该问题的模型要求搜索值检查 3 个 table 中的(其中之一),并且始终事先知道适用的 MetaAttribute
- 问题的模型,默认情况下使用 EF Code First,会有多个 CASCADE 路径的问题,这将需要使用 FluentApi(不是什么大问题,但可能不方便跟踪)
- 这种方法(可选?)要求在代码中而非数据库中处理有效 ListOption 条目的强制执行
- 显示不同类型的值不需要任何额外的工作即可正确呈现
管理界面需要检查 MetaAttribute.ListOption 以确定是否显示 TextBox 或 ListItem(如果 ListItemOptions 为 Yes/No,则可能显示复选框)
您可能想要为工具组添加另一个 table,以缩小呈现给用户的元属性
注意:由于未指定 EF 方法和语言,因此我使用了 EF Code First 和 VB.Net。 IMO 迁移和更容易过渡到 EF7 是使用 Code First 的充分理由。我更喜欢 VB.Net 的可读性,但如果需要,我会很乐意更改为 C#(或使用 this 转换器)。
Imports System.ComponentModel.DataAnnotations
Namespace Models
'I didn't bother specifying string lengths with <StringLength(#)>
Public Class HireTool
Public Property Id As Integer
'... other properties
'Navigation Properties
Public Overridable Property HireToolMetaAttributes As ICollection(Of HireToolMetaAttribute)
End Class
Public Class MetaAttribute
Public Enum MetaAttributeTypeEnum
Text = 1
ListItem = 2
End Enum
Public Property Id As Integer
Public Property Code As String
Public Property Label As String
Public Property Type As MetaAttributeTypeEnum
Public Property Required As Boolean
Public Property Position As Integer
'Navigation Properties
Public Overridable Property List As MetaAttributeList
End Class
Public Class MetaAttributeList
Public Property ID As Integer
Public Property Name As String
'Navigation Properties
<Required>
Public Property ListOptions As ICollection(Of MetaAttributeListOption)
End Class
Public Class MetaAttributeListOption
Public Property Id As Integer
Public Property OptionLabel As String
End Class
Public Class HireToolMetaAttribute
Public Property Id As Integer
<Schema.Index> <StringLength(1000)>
Public Property Value As String
<Required>
Public Overridable Property HireTool As HireTool
<Required>
Public Overridable Property MetaAttribute As MetaAttribute
End Class
End Namespace
编辑:这是生成的 SQL:
CREATE TABLE [dbo].[MetaAttributeLists] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_dbo.MetaAttributeLists] PRIMARY KEY CLUSTERED ([ID] ASC)
);
CREATE TABLE [dbo].[HireTools] (
[Id] INT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_dbo.HireTools] PRIMARY KEY CLUSTERED ([Id] ASC)
);
CREATE TABLE [dbo].[MetaAttributeListOptions] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[OptionLabel] NVARCHAR (MAX) NULL,
[MetaAttributeList_ID] INT NULL,
CONSTRAINT [PK_dbo.MetaAttributeListOptions] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MetaAttributeListOptions_dbo.MetaAttributeLists_MetaAttributeList_ID] FOREIGN KEY ([MetaAttributeList_ID]) REFERENCES [dbo].[MetaAttributeLists] ([ID])
);
CREATE TABLE [dbo].[MetaAttributes] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Code] NVARCHAR (MAX) NULL,
[Label] NVARCHAR (MAX) NULL,
[Type] INT NOT NULL,
[Required] BIT NOT NULL,
[Position] INT NOT NULL,
[List_ID] INT NULL,
CONSTRAINT [PK_dbo.MetaAttributes] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MetaAttributes_dbo.MetaAttributeLists_List_ID] FOREIGN KEY ([List_ID]) REFERENCES [dbo].[MetaAttributeLists] ([ID])
);
CREATE TABLE [dbo].[HireToolMetaAttributes] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Value] NVARCHAR (1000) NULL,
[HireTool_Id] INT NOT NULL,
[MetaAttribute_Id] INT NOT NULL,
CONSTRAINT [PK_dbo.HireToolMetaAttributes] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.HireToolMetaAttributes_dbo.HireTools_HireTool_Id] FOREIGN KEY ([HireTool_Id]) REFERENCES [dbo].[HireTools] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.HireToolMetaAttributes_dbo.MetaAttributes_MetaAttribute_Id] FOREIGN KEY ([MetaAttribute_Id]) REFERENCES [dbo].[MetaAttributes] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_Value]
ON [dbo].[HireToolMetaAttributes]([Value] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_HireTool_Id]
ON [dbo].[HireToolMetaAttributes]([HireTool_Id] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_MetaAttribute_Id]
ON [dbo].[HireToolMetaAttributes]([MetaAttribute_Id] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_MetaAttributeList_ID]
ON [dbo].[MetaAttributeListOptions]([MetaAttributeList_ID] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_List_ID]
ON [dbo].[MetaAttributes]([List_ID] ASC);