如何将 MS_DESCRIPTION 属性添加到 M:N 连接表中的列?
Posted
技术标签:
【中文标题】如何将 MS_DESCRIPTION 属性添加到 M:N 连接表中的列?【英文标题】:How to add MS_DESCRIPTION property to column in a M:N join table? 【发布时间】:2021-02-27 07:10:53 【问题描述】:参考我之前的问题:
How do you add column description for a foreign key in another table utilizing EF 6?
还有这个之前的帖子:
How to add description to columns in Entity Framework 4.3 code first using migrations?
基于上述参考,我如何为多对多关系插入扩展属性(例如 MS_Description) EF 6 会在后台自动创建连接表吗?
仅供参考,为您提供更多参考资料:
Towards the Self-Documenting SQL Server Database
SQL Server extended properties
sp_addextendedproperty (Transact-SQL)
【问题讨论】:
【参考方案1】:好的,是的,可以通过迁移来实现。这绝对是对 EF 工作方式的一次有趣的深入了解,因为我倾向于避免迁移。
第一个线索在这里:(https://karatejb.blogspot.com/2018/09/entity-framework-6-code-first-6-add-or.html) 使用 EF 读写 MS_Description。从那里开始,可以为迁移启用此功能。
对于包含两个实体 Fidget
和 Spinner
的 DbContext
,其中每个都是多对多,我想要一个由 EF 生成的 FidgetSpinner
表:
[Table("Fidgets")]
public class Fidget
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FidgeId get; set;
public string Name get; set;
public virtual ICollection<Spinner> Spinners get; set; = new List<Spinner>();
[Table("Spinners")]
public class Spinner
[Key]
public int SpinnerId get; set;
public string Name get; set;
public virtual ICollection<Fidget> Fidgets get; set; = new List<Fidget>();
然后映射到指定我的 FidgetSpinner 表:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Fidget>()
.HasMany(x => x.Spinners)
.WithMany(x => x.Fidgets)
.Map(x =>
x.MapLeftKey("FidgetId");
x.MapRightKey("SpinnerId");
x.ToTable("FidgetSpinners");
);
很简单,但是我们想在这个生成的表的 FidgetId 和 SpinnerId 列中添加描述。有效的解决方案是利用表注释和迁移:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);
string descriptions = JsonConvert.SerializeObject(new[]
new KeyValuePair<string, string>("SpinnerId", "FK Reference to a Spinner"),
new KeyValuePair<string, string>("FidgetId", "FK Reference to a Fidget")
);
modelBuilder.Entity<Fidget>()
.HasMany(x => x.Spinners)
.WithMany(x => x.Fidgets)
.Map(x =>
x.MapLeftKey("FidgetId");
x.MapRightKey("SpinnerId");
x.ToTable("FidgetSpinners");
x.HasTableAnnotation("Descriptions", descriptions);
);
在这里,我将我的注释附加为 JSON 字符串,其中包含我要添加的列名称和描述的 KeyValuePairs。这可以很容易地成为一个自定义类型的容器。但是,您发送的任何内容都必须可序列化为字符串。
接下来将创建一个 SQL 生成器,它将响应创建表操作并检查我们的描述表注释,然后对于任何匹配的列,使用 sp_addextendedproperty 附加它们的描述:
public class DescriptionMigrationSqlGenerator : SqlServerMigrationSqlGenerator
protected override void Generate(CreateTableOperation createTableOperation)
base.Generate(createTableOperation);
if (createTableOperation.Annotations.TryGetValue("Descriptions", out object descriptionData))
try
var descriptionValues = JsonConvert.DeserializeObject<KeyValuePair<string, string>[]>(descriptionData.ToString());
foreach (var descriptionValue in descriptionValues)
var column = createTableOperation.Columns.SingleOrDefault(x => x.Name.ToLower() == descriptionValue.Key.ToLower());
if (column != null)
var tableNameParts = createTableOperation.Name.Split('.');
var schemaName = tableNameParts.First();
var tableName = tableNameParts.Last();
var statement = string.Format(@"EXEC sp_addextendedproperty @name=N'MS_Description', @value = N'0', @level0type=N'Schema', @level0name= 1, @level1type=N'Table', @level1name=2, @level2type=N'Column', @level2name=3",
descriptionValue.Value, schemaName, tableName, column.Name);
Statement(statement);
catch // TODO: Handle situation where invalid description annotation provided. (I.e. not provided collection of key value pairs...
这只是一个模板,应该扩展异常处理,例如处理无效值或请求不存在的列的情况。 (目前被忽略)createTableOperation.Name 包含架构名称,因此我们将其拆分以获取存储过程调用的架构和表名称(非常粗略,建议对此进行更好的检查)。
最后一步是使用迁移配置注册此 SQL 生成器:
internal sealed class Configuration : DbMigrationsConfiguration<TestDbContext>
public Configuration()
AutomaticMigrationsEnabled = true;
SetSqlGenerator("System.Data.SqlClient", new DescriptionMigrationSqlGenerator());
这假定自动生成,或者您可以将其设置为手动生成。
现在,当迁移创建我们用描述注释的连接表时,我们请求的描述被添加到 DB 列中。
【讨论】:
【参考方案2】:感谢Steve Py 的回答,它让我产生了创建自己的属性并与question 中的先前解决方案相结合的想法。
以前的解决方案很容易适用于 1:1,并且需要完全定义的关系(请参阅here)来处理 1:n 关系。由于 EF 6 自动为 m:n 关系创建连接表,因此该解决方案将抛出异常,指出外键列不存在,因为它正在查找错误的表。
我实施的解决方案是给定解决方案的另一种变体,它使用自定义属性来定义描述,并且还需要为 m:n 关系定义外键和连接表.
我相信那里有更优雅的答案,但在它发布之前,我正在实现下面的代码。
以下是我创建的自定义属性:
public class CustomTblDesc : Attribute
private string desc = "";
public CustomTblDesc(string description)
desc = description;
public string Description get return desc;
public class CustomColDesc : Attribute
private string desc = "";
private string table = "";
private string propName = "";
public CustomColDesc(string description)
desc = description;
public CustomColDesc(string description, string tableName)
desc = description;
table = tableName;
public CustomColDesc(string description, string tableName, string linkedTablePropertyName)
desc = description;
table = tableName;
propName = linkedTablePropertyName;
public string Description get return desc;
public string LinkedTable get return table;
public string LinkedTablePropertyName get return propName;
下面是我根据question的解决方案修改的代码:
private void SetTableDescriptions(Type tableType) string tableName = tableType.Name;
// -- Set table description
CustomTblDesc tblDesc = null;
var custDescs = tableType.GetCustomAttributes(typeof(CustomTblDesc), false);
if (custDescs != null && custDescs.Length > 0)
tblDesc = custDescs[0] as CustomTblDesc;
SetTableDescription(tableName, tblDesc.Description);
// -- Set column description
foreach (var prop in tableType.GetProperties(System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
continue;
else
CustomColDesc colDesc = null;
custDescs = prop.GetCustomAttributes(typeof(CustomColDesc), false);
if (custDescs != null && custDescs.Length > 0)
colDesc = custDescs[0] as CustomColDesc;
if (string.IsNullOrEmpty(colDesc.LinkedTable))
SetColumnDescription(tableName, prop.Name, colDesc.Description);
else
SetColumnDescription(colDesc.LinkedTable, colDesc.LinkedTablePropertyName, colDesc.Description);
【讨论】:
以上是关于如何将 MS_DESCRIPTION 属性添加到 M:N 连接表中的列?的主要内容,如果未能解决你的问题,请参考以下文章
PowerDesigner15.1给自定义架构表字段添加MS_Description出错