为什么 entity framework 不尊重我的数据注释?

Why is entity framework not respecting my data annotations?

为什么 entity framework 不尊重我的数据注释?

一点背景知识:我刚开始一份新工作,我的任务是将 entity framework 添加到 ASP.net 项目中。项目结构如下: Data.csproj -> 有我新添加的代码优先 entity framework 代码。这个项目将容纳我们的模型 MVC.csproj -> 包含我们 MVC 结构的所有控制器的类库。该项目目前充满了 MVC 领域。我添加了 OWIN 以及 UserStore、UserManager 和 SignInManager Web.vbproj -> 这个 is/was 一个混合了 MVC 和 webforms 代码的 Asp.Net web 表单项目。根据我的老板,系统架构师的说法,除了 html 视图、css 和 javascript 之外,该项目的计划是什么都没有。我们目前正在将该项目从 asp.net 网络表单迁移到上述 MVC 结构。

我正在使用 EntityFramework v6.1.1

在 Data.csproj 项目中,我定义了以下实体:

[Table("Terminals", Schema = "dbo")]
public class TerminalEx
{
    #region Constructors
    public TerminalEx()
    {

    }
    #endregion

    #region Public Properties
    [Key]
    public int TerminalId { get; set; }

    public string License { get; set; }

    [Column("TerminalName")]
    public string Name { get; set; }

    public string RootPublicDomainAddress { get; set; }
    public string CompanyName { get; set; }
    public int TimeZoneId { get; set; }
    public string Zip { get; set; }
    public string DispatchEmail { get; set; }

    [Column("useAutoAddlDrvPmt")]
    public byte UseAutoAddDriverPayment { get; set; }

    public byte UseTerminalAutoAssignment { get; set; }
    public byte UseMapCodes { get; set; }

    [Column("OEautoPopulateAccountNo")]
    public byte AutoPopulateAccountNumberForOrderEntry { get; set; }

    [Column("OEautoPopulateAddresses")]
    public byte AutoPopulateAddressesForOrderEntry { get; set; }
    #endregion
}

这是我的 DbContext:

public class XceleratorContext : DbContext
{
    public static void DoTest()
    {
        XceleratorContext db = null;
        try
        {
            db = new XceleratorContext();
            var bleh = db.Terminals.ToList();

            foreach (TerminalEx term in bleh)
            {
                System.Diagnostics.Debug.WriteLine(term.TerminalId);
                System.Diagnostics.Debug.WriteLine(term.Name);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.ToString());
            throw;
        }
        finally
        {
            if (db != null)
            {
                db.Dispose();
                db = null;
            }
        }
    }

    #region Contructors
    public XceleratorContext()
        : base(ConfigurationManager.AppSettings["ConnectionString"])
    {
        Database.SetInitializer<XceleratorContext>(null);
        this.Database.Log += WriteLog;
    }

    private void WriteLog(string obj)
    {
        System.Diagnostics.Debug.WriteLine(obj);
    }
    #endregion

    #region Public Properties
    public virtual DbSet<TerminalEx> Terminals { get; set; }
    #endregion

    #region Encapsulation of DbSets
    public T Add<T>(T entity) where T : class
    {
        return Set<T>().Add(entity);
    }

    public T Attach<T>(T entity) where T : class
    {
        Set<T>().Attach(entity);
        Entry<T>(entity).State = EntityState.Modified;
        return entity;
    }

    public T Detach<T>(T entity) where T : class
    {
        Entry<T>(entity).State = EntityState.Detached;
        return entity;
    }

    public T Remove<T>(T entity) where T : class
    {
        if (Entry<T>(entity).State == EntityState.Detached)
        {
            Entry<T>(entity).State = EntityState.Deleted;
            return Entry<T>(entity).Entity;
        }
        else
            return Set<T>().Remove(entity);
    }
    #endregion

    #region Method Overrides
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<EFUser>();
        var terminalConfig = modelBuilder.Entity<TerminalEx>();

        //terminalConfig.Map(b => 
        //{
        //    b.Property(c => c.Name).HasColumnName("TerminalName");
        //    b.Property(c => c.UseAutoAddDriverPayment).HasColumnName("useAutoAddlDrvPmt");
        //    b.Property(c => c.AutoPopulateAccountNumberForOrderEntry).HasColumnName("OEautoPopulateAccountNo");
        //    b.Property(c => c.AutoPopulateAddressesForOrderEntry).HasColumnName("OEautoPopulateAddresses");                
        //}).ToTable("Terminals", "dbo").HasKey(d => d.TerminalId);

        // base.OnModelCreating(modelBuilder);
    }        
    #endregion

    #region Stored Proicedure Calls
    public IEnumerable<EFUser> GetUser(int? id = null, string userName = null, string passwordHash = null)
    {
        return this.Database.SqlQuery<EFUser>(
            " exec dbo.GetUser @id, @userName, @passwordHash ", 
            GetSqlParameter("id", System.Data.SqlDbType.Int, value: id),
            GetSqlParameter("userName", System.Data.SqlDbType.NVarChar, 100, userName),
            GetSqlParameter("passwordHash", System.Data.SqlDbType.NVarChar, 100, passwordHash));            
    }
    #endregion

    #region Private Methods
    private static SqlParameter GetSqlParameter<T>(string name, System.Data.SqlDbType type, int? size = null, T? value = null) where T : struct
    {
        var res = new SqlParameter(name, type) { Value = value ?? (object)DBNull.Value };

        if (size.HasValue)
            res.Size = size.Value;

        return res;
    }

    private static SqlParameter GetSqlParameter(string name, System.Data.SqlDbType type, int size, string value)
    {
        return new SqlParameter(name, type, size) { Value = value ?? (object)DBNull.Value };
    }
    #endregion
}

最后在 Web.vbproj 里面是 index.aspx.vb

的 Page_Load
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    Xcelerator.Data.BizObjects.XceleratorContext.DoTest()

End Sub

我的问题是出现以下错误。我通过创建一个新的 asp.net webforms VB proj 并在索引页面的页面加载上调用相同的方法来测试我的代码并且工作正常。我已经从 web.vbpoj 中删除了尽可能多的代码、参考和包,但我仍然遇到同样的错误。出于某种原因,仅在此 Web.vbproj 中,代码优先 entity framework 不尊重我的数据注释。

System.Data.Entity.Core.EntityCommandExecutionException was caught
HResult=-2146232004 Message=An error occurred while executing the command definition. See the inner exception for details.
Source=EntityFramework StackTrace: at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) at System.Data.Entity.Core.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext context, ObjectParameterCollection parameterValues) at System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass3.<GetResults>b__2() at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess) at System.Data.Entity.Core.Objects.ObjectQuery1.<>c__DisplayClass3.<GetResults>b__1() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1 operation) at System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption) at System.Data.Entity.Core.Objects.ObjectQuery1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0() at System.Lazy1.CreateValue() at System.Lazy1.LazyInitValue() at System.Lazy1.get_Value() at System.Data.Entity.Internal.LazyEnumerator1.MoveNext() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at Xcelerator.Data.BizObjects.XceleratorContext.DoTest() in c:\svonaRepo\XceleratorLocal - Copy\Xcelerator.Data\BizObjects\XceleratorContext.cs:line 22
InnerException: System.Data.SqlClient.SqlException HResult=-2146232060 Message=Invalid object name 'dbo.TerminalExes'. Source=.Net SqlClient Data Provider ErrorCode=-2146232060 Class=16 LineNumber=1 Number=208 Procedure="" Server=KSS-DT18 State=1 StackTrace: at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds, Boolean describeParameterEncryptionRequest) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<>c__DisplayClassb.<Reader>b__8() at System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TInterceptionContext,TResult](Func1 operation, TInterceptionContext interceptionContext, Action1 executing, Action`1 executed) at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext) at System.Data.Entity.Internal.InterceptableDbCommand.ExecuteDbDataReader(CommandBehavior behavior) at System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) at System.Data.Entity.Core.EntityClient.Internal.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) InnerException:

这部分代码说,"I want to do Fluent Code First with POCOs" 也就是说,我的 POCO 没有注释,这是我想要的映射自定义。

然后您继续对映射进行 NO 自定义。因此,您将获得默认映射。

#region Method Overrides
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<EFUser>();
    var terminalConfig = modelBuilder.Entity<TerminalEx>();

    //terminalConfig.Map(b => 
    //{
    //    b.Property(c => c.Name).HasColumnName("TerminalName");
    //    b.Property(c => c.UseAutoAddDriverPayment).HasColumnName("useAutoAddlDrvPmt");
    //    b.Property(c => c.AutoPopulateAccountNumberForOrderEntry).HasColumnName("OEautoPopulateAccountNo");
    //    b.Property(c => c.AutoPopulateAddressesForOrderEntry).HasColumnName("OEautoPopulateAddresses");                
    //}).ToTable("Terminals", "dbo").HasKey(d => d.TerminalId);

    // base.OnModelCreating(modelBuilder);
}        
#endregion

只需删除 DbContext 的那一部分,一切都会好起来的。

终于找到问题了。 Web.vbproj 项目在项目中包含了 NuGet 包文件夹。通过从项目中排除文件夹,我的 EF code/method 按预期工作。