我使用 EF 启动了一个数据库模式,并在尝试手动修改 CLR 和/或数据库表时遇到了多个问题。首先是 EF 放置在表中的“Employee_ID”列。我删除了它,dbo.EdmMetaData 和 dbo.__MigrationHistory 表,并摸索了引发的运行时错误。 现在,我正在努力解决以下错误:
ReferentialConstraint 中的依赖属性映射到存储生成的列。列:“员工 ID”。
我的实现使用具有 3 个计算列的 TimeCardEntity CLR。这些列恰好映射到另一个表的主键。另一个表是 EmployeeRecord。
目标)我不希望 EF 自动映射这 3 列。由于 EF 提供的复杂性,我打算自己填写它们,但我不能告诉 EF 停止创建导航关系和/或引用约束。
第 1 点)我有一个 EmployeeRecord 表,它有一个 Guid ID 主键,它映射到 CLR 类 EmployeeRecord
第 2 点)我有一个 TimeCardEntity 表,它有 3 个计算列,称为 EmployeeID、ManagerID、DivisionManagerID,它们与 EmployeeRecord 相关。所有都声明为 NULL,但显然需要 EmployeeID,因为如果不声明员工,您就无法拥有考勤卡。ManagerID 和 DivisionManagerID 稍后填写。
第 3 点)请不要问我“为什么要计算这些?”,因为这是有原因的。我也觉得这与问题无关。简而言之,计算出的 EmployeeID(无论是员工、经理还是部门经理)都存储在一个 xml 属性中,其中包含员工的批准和签名数据——这提供了非信誉。
第 4 点)我有 3 个存储函数,分别称为 fxGetEmployeeID(xml)、fxGetManagerID(xml) 和 getDivisonManagerID(xml)。这些中的每一个都分别用于计算列 EmployeeID、ManagerID 和 DivisionManagerID。
为简洁起见,以下是简化的类声明:
public enum TimeCardEmployeeTypeEnum {
Employee,
Manager,
DivisionManager
}
[DataContract]
[Serializable]
[Table("EmployeeRecord", Schema = "TimeCard")]
public class EmployeeRecord {
#region Exposed Propert(y|ies)
[DataMember]
public Guid ID { get; set; }
/// <summary>
/// Customers internal company employee ID. Can be null, SSN, last 4, or what ever...
/// I included it just in case it was part of my pains...
/// </summary>
[CustomValidation(typeof(ModelValidator), "EmployeeRecord_EmployeeID", ErrorMessage = "Employee ID is not valid.")]
public string EmployeeID { get; set; }
#endregion
}
[DataContract]
[Serializable]
[Table("TimeCardEntry", Schema = "TimeCard")]
public class TimeCardEntry {
#region Member Field(s)
[NonSerialized]
XDocument m_TimeEntries;
#endregion
#region Con/Destructor(s)
public TimeCardEntry() {
this.m_TimeEntries = "<root />".ToXDocument();
}
public TimeCardEntry(Guid employeeID) {
if (employeeID == Guid.Empty)
throw new ArgumentNullException("employeeID");
this.m_TimeEntries = "<root />".ToXDocument();
this.EmployeeID = employeeID;
}
#endregion
#region Exposed Propert(y|ies)
[NotMapped]
[IgnoreDataMember]
public XDocument TimeEntries {
get {
if (this.m_TimeEntries == null) {
if (!string.IsNullOrEmpty(this.TimeEntriesXml))
this.m_TimeEntries = this.TimeEntriesXml.ToXDocument();
}
return this.m_TimeEntries;
}
set {
this.m_TimeEntries = value;
if (this.m_TimeEntries != null)
this.TimeEntriesXml = this.m_TimeEntries.ToString();
else
this.TimeEntriesXml = null;
this.OnPropertyChanged("TimeEntriesXml");
this.OnPropertyChanged("TimeEntries");
}
}
[DataMember]
[EditorBrowsable(EditorBrowsableState.Never)]
[Required]
public string TimeEntriesXml {
get {
if (this.m_TimeEntries == null)
return null;
return this.m_TimeEntries.ToString();
}
set {
this.m_TimeEntries = value.ToXDocument();
this.OnPropertyChanged("TimeEntriesXml");
this.OnPropertyChanged("TimeEntries");
}
}
[IgnoreDataMember]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public Guid? EmployeeID {
get {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeID");
if (attribute != null)
return (Guid)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.Manager))
throw new ArgumentException("Property cannot be changed once the manager signature has been set.", "EmployeeID");
if (value != null && value.Value != Guid.Empty)
this.m_TimeEntries.Root.SetAttributeValue("EmployeeID", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeID");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("EmployeeID");
}
}
public virtual EmployeeRecord Employee { get; set; }
[NotMapped]
[IgnoreDataMember]
public DateTime? EmployeeApprovalDate {
get {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeApprovalDate");
if (attribute != null)
return (DateTime)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.Manager))
throw new ArgumentException("Property cannot be changed once the manager signature has been set.", "EmployeeApprovalDate");
if (value.HasValue)
this.m_TimeEntries.Root.SetAttributeValue("EmployeeApprovalDate", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("EmployeeApprovalDate");
}
}
[NotMapped]
[IgnoreDataMember]
public byte[] EmployeeSignature {
get {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeSignature");
if (attribute != null)
return Convert.FromBase64String((string)attribute);
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.Manager))
throw new ArgumentException("Property cannot be changed once the manager signature has been set.", "EmployeeSignature");
if (value != null) {
if (value.Length > 1024)
throw new ArgumentException("Signature cannot be larger than 1KB.", "EmployeeSignature");
this.m_TimeEntries.Root.SetAttributeValue("EmployeeSignature", Convert.ToBase64String(value));
} else {
var attribute = this.m_TimeEntries.Root.Attribute("EmployeeApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("EmployeeSignature");
}
}
[IgnoreDataMember]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public Guid? ManagerID {
get {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerID");
if (attribute != null)
return (Guid)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.DivisionManager))
throw new ArgumentException("Property cannot be changed once the division manager signature has been set.", "ManagerID");
if (value.HasValue) {
if (value.Value == Guid.Empty)
throw new ArgumentNullException("ManagerID");
this.m_TimeEntries.Root.SetAttributeValue("ManagerID", value);
} else {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerID");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("ManagerID");
}
}
public virtual EmployeeRecord Manager { get; set; }
[NotMapped]
[IgnoreDataMember]
public DateTime? ManagerApprovalDate {
get {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerApprovalDate");
if (attribute != null)
return (DateTime)attribute;
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.DivisionManager))
throw new ArgumentException("Property cannot be changed once the division manager signature has been set.", "ManagerApprovalDate");
if (value.HasValue)
this.m_TimeEntries.Root.SetAttributeValue("ManagerApprovalDate", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("ManagerApprovalDate");
}
}
[NotMapped]
[IgnoreDataMember]
public byte[] ManagerSignature {
get {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerSignature");
if (attribute != null)
return Convert.FromBase64String((string)attribute);
return null;
}
set {
if (this.ValidateSignature(TimeCardEmployeeTypeEnum.DivisionManager))
throw new ArgumentException("Property cannot be changed once the division manager signature has been set.", "ManagerSignature");
if (value != null) {
if (value.Length > 1024)
throw new ArgumentException("Signature cannot be larger than 1KB.", "ManagerSignature");
this.m_TimeEntries.Root.SetAttributeValue("ManagerSignature", Convert.ToBase64String(value));
} else {
var attribute = this.m_TimeEntries.Root.Attribute("ManagerSignature");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("ManagerSignature");
}
}
[IgnoreDataMember]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)]
public Guid? DivisionManagerID {
get {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerID");
if (attribute != null)
return (Guid)attribute;
return null;
}
set {
if (value.HasValue) {
if (value.Value == Guid.Empty)
throw new ArgumentNullException("DivisionManagerID");
this.m_TimeEntries.Root.SetAttributeValue("DivisionManagerID", value);
} else {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerID");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("DivisionManagerID");
}
}
public virtual EmployeeRecord DivisionManager { get; set; }
[NotMapped]
[IgnoreDataMember]
public DateTime? DivisionManagerApprovalDate {
get {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerApprovalDate");
if (attribute != null)
return (DateTime)attribute;
return null;
}
set {
if (value.HasValue)
this.m_TimeEntries.Root.SetAttributeValue("DivisionManagerApprovalDate", value);
else {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerApprovalDate");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("DivisionManagerApprovalDate");
}
}
[NotMapped]
[IgnoreDataMember]
public byte[] DivisionManagerSignature {
get {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerSignature");
if (attribute != null)
return Convert.FromBase64String((string)attribute);
return null;
}
set {
if (value != null) {
if (value.Length > 1024)
throw new ArgumentException("Signature cannot be larger than 1KB.", "DivisionManagerSignature");
this.m_TimeEntries.Root.SetAttributeValue("DivisionManagerSignature", Convert.ToBase64String(value));
} else {
var attribute = this.m_TimeEntries.Root.Attribute("DivisionManagerSignature");
if (attribute != null)
attribute.Remove();
}
this.OnPropertyChanged("DivisionManagerSignature");
}
}
#endregion
}
这是数据库上下文声明
public sealed class DatabaseContext : DbContext {
public DatabaseContext(bool autoDetectChangesEnabled = false, bool lazyLoadingEnabled = false, bool proxyCreationEnabled = false, bool validateOnSaveEnabled = false) {
this.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
this.Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
this.Configuration.ProxyCreationEnabled = proxyCreationEnabled;
this.Configuration.ValidateOnSaveEnabled = validateOnSaveEnabled;
}
public DbSet<EmployeeRecord> EmployeeRecords { get; set; }
public DbSet<TimeCardEntry> TimeCards { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<System.Data.Entity.Infrastructure.IncludeMetadataConvention>();
}
}
更新 我必须添加另一个观察到的 EF 行为。当我将“NotMappedAttribute”添加到 TimeCardEntry 的 EmployeeID 列时,我遇到了另一个问题。EF 将“Employee_ID”列添加回自动生成模式。请参阅下面的 TSQL 配置文件跟踪:
exec sp_executesql N'SELECT
[Limit1].[C1] AS [C1],
[Limit1].[ID] AS [ID],
[Limit1].[TimeEntriesXml] AS [TimeEntriesXml],
[Limit1].[ManagerID] AS [ManagerID],
[Limit1].[DivisionManagerID] AS [DivisionManagerID],
[Limit1].[CreatedBy] AS [CreatedBy],
[Limit1].[Created] AS [Created],
[Limit1].[UpdatedBy] AS [UpdatedBy],
[Limit1].[Updated] AS [Updated],
[Limit1].[Employee_ID] AS [Employee_ID]
FROM ( SELECT TOP (2)
[Extent1].[ID] AS [ID],
[Extent1].[TimeEntriesXml] AS [TimeEntriesXml],
[Extent1].[ManagerID] AS [ManagerID],
[Extent1].[DivisionManagerID] AS [DivisionManagerID],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[Created] AS [Created],
[Extent1].[UpdatedBy] AS [UpdatedBy],
[Extent1].[Updated] AS [Updated],
[Extent1].[Employee_ID] AS [Employee_ID],
1 AS [C1]
FROM [TimeCard].[TimeCardEntry] AS [Extent1]
WHERE [Extent1].[ID] = @p0
) AS [Limit1]',N'@p0 uniqueidentifier',@p0='10F3E723-4E12-48CD-8750-5922A1E42AA3'