簡體   English   中英

無效的列名或Identity_Insert關閉

[英]Invalid Column Name or Identity_Insert off

我整個上午都和CF EF一起轉轉。 我認為我已經解決了所有問題,但我確信一個問題是由於我缺乏知識/理解。

問題集中在一個表和該表的映射上,因此我先給出定義,然后解釋問題。 這是表的定義方式:

在此處輸入圖片說明

現在,我將類表示形式定義為:

public partial class ExternalForumCredentials : BaseEntity
{
   public virtual int Customer_Id { get; set; }
   public virtual int ExternalForumBoardId { get; set; }
   public virtual string Username { get; set; }
   public virtual string Password { get; set; }
 }

和映射:

public ExternalForumCredentialsMap()
{
   this.ToTable("ExternalForumCredentials");
   this.HasKey(ef => ef.Id);
   this.HasKey(ef => ef.Customer_Id);
   this.HasKey(ef => ef.ExternalForumBoardId);
   this.Property(ef => ef.Username).IsRequired().HasMaxLength(100);
   this.Property(ef => ef.Password).IsRequired().HasMaxLength(100);

 }

現在我沒有顯示控制器代碼,因為我認為問題出在我的ef配置中。 但是,如果我錯了,只需告訴我我需要添加什么,我會立即這樣做。

所以...鑒於ID,Customer_Id和ExternalForumBoardId是主鍵,我定義了映射,如上所示。

使用此配置,一切順利,直到插入為止……然后崩潰,說我正在嘗試執行IDENTITY_INSERT。 好的,因為我正在為控制器中的Customer_Id和ExternalForumBoardId分配一個值,並且由於映射將它們作為鍵,所以存在沖突。

所以我將映射更改為:

public ExternalForumCredentialsMap()
{
        this.ToTable("ExternalForumCredentials");
        this.HasKey(ef => ef.Id);
        this.Property(ef => ef.Customer_Id);
        this.Property(ef => ef.ExternalForumBoardId);
        this.Property(ef => ef.Username).IsRequired().HasMaxLength(100);
        this.Property(ef => ef.Password).IsRequired().HasMaxLength(100);       
}

現在,在執行插入操作之前,我得到一個錯誤:無效的列名Customer_Id,當然由於我的有限知識,我不理解,因為我已經定義了Customer_Id。

我也在映射中嘗試過HasRequired,但是甚至不會編譯說“類型int必須是引用類型,才能將其用作參數...”

在這種情況下,其他映射選項(如忽略)似乎沒有意義。

任何幫助解釋我做錯了的事情將不勝感激。


Db上下文接口:

public interface IDbContext 
{
    IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;

    int SaveChanges();

    IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
        where TEntity : BaseEntity, new();

    /// <summary>
    /// Creates a raw SQL query that will return elements of the given generic type.  The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
    /// </summary>
    /// <typeparam name="TElement">The type of object returned by the query.</typeparam>
    /// <param name="sql">The SQL query string.</param>
    /// <param name="parameters">The parameters to apply to the SQL query string.</param>
    /// <returns>Result</returns>
    IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters);

    /// <summary>
    /// Executes the given DDL/DML command against the database.
    /// </summary>
    /// <param name="sql">The command string</param>
    /// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
    /// <param name="parameters">The parameters to apply to the command string.</param>
    /// <returns>The result returned by the database after executing the command.</returns>
    int ExecuteSqlCommand(string sql, int? timeout = null, params object[] parameters);
   }
}

和:

public static class DbContextExtensions {
    /// <summary>
    /// Loads the database copy.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="context">The context.</param>
    /// <param name="currentCopy">The current copy.</param>
    /// <returns></returns>
    public static T LoadDatabaseCopy<T>(this IDbContext context, T currentCopy) where T : BaseEntity {
        return InnerGetCopy(context, currentCopy, e => e.GetDatabaseValues());
    }

    private static T InnerGetCopy<T>(IDbContext context, T currentCopy, Func<DbEntityEntry<T>, DbPropertyValues> func) where T : BaseEntity {
        //Get the database context
        DbContext dbContext = CastOrThrow(context);

        //Get the entity tracking object
        DbEntityEntry<T> entry = GetEntityOrReturnNull(currentCopy, dbContext);

        //The output 
        T output = null;

        //Try and get the values
        if (entry != null) {
            DbPropertyValues dbPropertyValues = func(entry);
            if(dbPropertyValues != null) {
                output = dbPropertyValues.ToObject() as T;
            }
        }

        return output;
    }

    /// <summary>
    /// Gets the entity or return null.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="currentCopy">The current copy.</param>
    /// <param name="dbContext">The db context.</param>
    /// <returns></returns>
    private static DbEntityEntry<T> GetEntityOrReturnNull<T>(T currentCopy, DbContext dbContext) where T : BaseEntity {
        return dbContext.ChangeTracker.Entries<T>().Where(e => e.Entity == currentCopy).FirstOrDefault();
    }

    private static DbContext CastOrThrow(IDbContext context) {
        DbContext output = (context as DbContext);

        if(output == null) {
            throw new InvalidOperationException("Context does not support operation.");
        }

        return output;
    }

    /// <summary>
    /// Loads the original copy.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="context">The context.</param>
    /// <param name="currentCopy">The current copy.</param>
    /// <returns></returns>
    public static T LoadOriginalCopy<T>(this IDbContext context, T currentCopy) where T : BaseEntity {
        return InnerGetCopy(context, currentCopy, e => e.OriginalValues);
    }
}

和實現:

public class ObjectContext : DbContext, IDbContext
{
    public ObjectContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {
        //((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //dynamically load all configuration
        System.Type configType = typeof(LanguageMap);   //any of your configuration classes here
        var typesToRegister = Assembly.GetAssembly(configType).GetTypes()
        .Where(type => !String.IsNullOrEmpty(type.Namespace))
        .Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
        foreach (var type in typesToRegister)
        {
            dynamic configurationInstance = Activator.CreateInstance(type);
            modelBuilder.Configurations.Add(configurationInstance);
        }
        //...or do it manually below. For example,
        //modelBuilder.Configurations.Add(new LanguageMap());



        base.OnModelCreating(modelBuilder);
    }

    /// <summary>
    /// Attach an entity to the context or return an already attached entity (if it was already attached)
    /// </summary>
    /// <typeparam name="TEntity">TEntity</typeparam>
    /// <param name="entity">Entity</param>
    /// <returns>Attached entity</returns>
    protected virtual TEntity AttachEntityToContext<TEntity>(TEntity entity) where TEntity : BaseEntity, new()
    {
        //little hack here until Entity Framework really supports stored procedures
        //otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context
        var alreadyAttached = Set<TEntity>().Local.Where(x => x.Id == entity.Id).FirstOrDefault();
        if (alreadyAttached == null)
        {
            //attach new entity
            Set<TEntity>().Attach(entity);
            return entity;
        }
        else
        {
            //entity is already loaded.
            return alreadyAttached;
        }
    }

    public string CreateDatabaseScript()
    {
        return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
    }

    public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
    {
        return base.Set<TEntity>();
    }

    public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
    {
        //HACK: Entity Framework Code First doesn't support doesn't support output parameters
        //That's why we have to manually create command and execute it.
        //just wait until EF Code First starts support them
        //
        //More info: http://weblogs.asp.net/dwahlin/archive/2011/09/23/using-entity-framework-code-first-with-stored-procedures-that-have-output-parameters.aspx

        bool hasOutputParameters = false;
        if (parameters != null)
        {
            foreach (var p in parameters)
            {
                var outputP = p as DbParameter;
                if (outputP == null)
                    continue;

                if (outputP.Direction == ParameterDirection.InputOutput ||
                    outputP.Direction == ParameterDirection.Output)
                    hasOutputParameters = true;
            }
        }



        var context = ((IObjectContextAdapter)(this)).ObjectContext;
        if (!hasOutputParameters)
        {
            //no output parameters
            var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList();
            for (int i = 0; i < result.Count; i++)
                result[i] = AttachEntityToContext(result[i]);

            return result;

            //var result = context.ExecuteStoreQuery<TEntity>(commandText, parameters).ToList();
            //foreach (var entity in result)
            //    Set<TEntity>().Attach(entity);
            //return result;
        }
        else
        {

            //var connection = context.Connection;
            var connection = this.Database.Connection;
            //Don't close the connection after command execution


            //open the connection for use
            if (connection.State == ConnectionState.Closed)
                connection.Open();
            //create a command object
            using (var cmd = connection.CreateCommand())
            {
                //command to execute
                cmd.CommandText = commandText;
                cmd.CommandType = CommandType.StoredProcedure;

                // move parameters to command object
                if (parameters != null)
                    foreach (var p in parameters)
                        cmd.Parameters.Add(p);

                //database call
                var reader = cmd.ExecuteReader();
                //return reader.DataReaderToObjectList<TEntity>();
                var result = context.Translate<TEntity>(reader).ToList();
                for (int i = 0; i < result.Count; i++)
                    result[i] = AttachEntityToContext(result[i]);
                //close up the reader, we're done saving results
                reader.Close();
                return result;
            }

        }
    }

    /// <summary>
    /// Creates a raw SQL query that will return elements of the given generic type.  The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
    /// </summary>
    /// <typeparam name="TElement">The type of object returned by the query.</typeparam>
    /// <param name="sql">The SQL query string.</param>
    /// <param name="parameters">The parameters to apply to the SQL query string.</param>
    /// <returns>Result</returns>
    public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
    {
        return this.Database.SqlQuery<TElement>(sql, parameters);
    }

    /// <summary>
    /// Executes the given DDL/DML command against the database.
    /// </summary>
    /// <param name="sql">The command string</param>
    /// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
    /// <param name="parameters">The parameters to apply to the command string.</param>
    /// <returns>The result returned by the database after executing the command.</returns>
    public int ExecuteSqlCommand(string sql, int? timeout = null, params object[] parameters)
    {
        int? previousTimeout = null;
        if (timeout.HasValue)
        {
            //store previous timeout
            previousTimeout = ((IObjectContextAdapter) this).ObjectContext.CommandTimeout;
            ((IObjectContextAdapter) this).ObjectContext.CommandTimeout = timeout;
        }

        var result = this.Database.ExecuteSqlCommand(sql, parameters);

        if (timeout.HasValue)
        {
            //Set previous timeout back
            ((IObjectContextAdapter) this).ObjectContext.CommandTimeout = previousTimeout;
        }

        //return result
        return result;
    }
}

您還有另一個與ExternalForumCredentials有關系的類,並且該類的配置不正確。 EntityFramework將盡最大努力猜測您的約定,因此猜測它可以連接到一個Customer_Id

轉到使用ExternalForumCredentials任何地圖,並正確配置它們。 例如:

this.HasMany(ef => ef.ExternalForumCredentials)
    .WithRequired()
    .HasForeignKey(ef => ef.Customer_Id);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM