EF を使用して db スキーマを開始し、CLR や db テーブルを手動で変更しようとすると、複数の問題が発生しました。1 つ目は、EF がテーブルに配置した "Employee_ID" 列です。dbo.EdmMetaData テーブルと dbo.__MigrationHistory テーブルを削除し、発生した実行時エラーをいじりました。 今、私は次のエラーに取り組んでいます:
ReferentialConstraint の依存プロパティは、ストアで生成された列にマップされます。列: 'EmployeeID'。
私の実装では、3 つの計算列を持つ TimeCardEntity CLR を使用しています。これらの列はたまたま別のテーブルの主キーにマップされています。この他のテーブルは EmployeeRecord です。
GOAL) EF がこれらの 3 つの列を自動マップすることは望んでいません。EFが提供する複雑さのために、私はそれらを自分で埋めるつもりですが、EFにナビゲーション関係や参照制約の作成を停止するように指示することはできません.
ポイント #1) Guid ID の主キーを持つ EmployeeRecord テーブルがあり、CLR クラス EmployeeRecord にマップされます。
ポイント #2) EmployeeRecord に関連する EmployeeID、ManagerID、DivisionManagerID という3 つの計算列を持つ TimeCardEntity テーブルがあります。すべてが NULL 宣言されていますが、従業員を宣言しないとタイムカードを取得できないため、明らかに EmployeeID が必要です。ManagerID と DivisionManagerID は後で入力されます。
ポイント #3) 「なぜこれらが計算されるのですか?」と聞かないでください。理由があるからです。また、それは問題とは無関係だと感じています。つまり、計算された EmployeeID (従業員、マネージャー、または部門マネージャーのいずれか) は、従業員の承認と署名のデータと共に xml プロパティに格納されます。これにより、評判がなくなります。
ポイント 4) fxGetEmployeeID(xml)、fxGetManagerID(xml)、および getDivisonManagerID(xml) という 3 つのストアド関数があります。これらはそれぞれ、計算列 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
}
これは db コンテキスト宣言です
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 の別の観察された動作を追加する必要があります。TimeCardEntry の EmployeeID 列に「NotMappedAttribute」を追加すると、別の問題が発生します。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'