0

我使用 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'
4

1 回答 1

0

EF 试图Employee_ID在数据库中声明,因为它需要表的外键Employee列。它不能使用您的EmployeeID属性及其列作为外键,因为它被声明为计算 - EF 中的外键不得声明为计算或身份(不支持)。

您的模型的解决方案要么需要放弃导航属性并仅使用 ID(并手动加载相关员工),要么放弃那些计算列 - 我可以想象这两个选项可能会很烦人。

于 2013-02-18T19:55:48.937 回答