实体框架模型产生奇怪的错误消息

本文关键字:错误 消息 框架 模型 实体 | 更新日期: 2023-09-27 18:13:12

我有一个PostgreSql 9.04数据库,其中包含一个postgres实现的asp.net会员服务数据库。这个实现与我自己的表在同一个数据库中,但是在一个名为"security"的不同模式中。

出于商业原因,我在实体模型中包含了aspnet_Users, aspnet_Membership和aspnet_Profiles表。下面是这些表的DDL。

CREATE TABLE security.aspnet_users (
    userid UUID NOT NULL,
    applicationname VARCHAR(255) NOT NULL,
    username VARCHAR(255),
    lastactivitydate TIMESTAMP,
    isanonymous INT,
    CONSTRAINT aspnet_users_userid_pk PRIMARY KEY (userid),
    CONSTRAINT aspnet_users_username_pk UNIQUE (username, applicationname)
);
CREATE TABLE security.aspnet_membership (
    userid UUID NOT NULL,
    password VARCHAR(255),
    passwordsalt VARCHAR(255),
    passwordformat INT,
    email VARCHAR(255),
    passwordquestion VARCHAR(255),
    passwordanswer VARCHAR(255),
    comments VARCHAR(255),
    isapproved INT,
    islockedout INT,
    creationdate TIMESTAMP,
    lastlogindate TIMESTAMP,
    lastpasswordchangeddate TIMESTAMP,
    lastlockoutdate TIMESTAMP,
    failedpasswordattemptcount INT,
    failedpasswordattemptstart TIMESTAMP,
    failedpasswordanswercount INT,
    failedpasswordanswerstart TIMESTAMP,
    CONSTRAINT aspnet_membership_userid_pk PRIMARY KEY (userid),
    CONSTRAINT aspnet_membership_userid_ref FOREIGN KEY (userid) REFERENCES security.aspnet_users (userid)
);
CREATE TABLE security.aspnet_profiles (
    userid                      UUID,
    propertynames               BYTEA NOT NULL,
    propertyvaluesstring        BYTEA NOT NULL,
    propertyvaluesbinary        BYTEA NULL,
    lastupdateddate             TIMESTAMP NOT NULL,
    CONSTRAINT aspnet_profiles_userid_ref FOREIGN KEY (userid) REFERENCES security.aspnet_users (userid),
    CONSTRAINT aspnet_profiles_userid_key UNIQUE (userid)
);

同样,这些是该模式中唯一存在于我的模型中的表。

现在,当我向这些表中插入一行时,我调用SaveChanges,我得到以下错误消息:

无法确定"Membership_Users"关系的主体端。多个添加的实体可以有相同的主键。

下面是我用来插入新行或更新现有行的代码示例。这代表了我在更新数据库方面所做的一切。

public static void SaveUserProfile( CarSystemEntities context, UserProfileData data ) {
    if ( context == null )
        throw new ArgumentNullException( "context", "You must pass a non-null CarSystemEntities instance." );
    if ( data == null )
        throw new ArgumentNullException( "data", "You must pass a non-null UserProfileData instance." );
    try {
        Profile profile = null;
        if ( !context.Profiles.Any( p => p.Userid == data.ID ) ) {
            profile = new CarSystem.Profile{
                LastUpdatedDate      = data.LastUpdatedDate.LocalDateTime,
                PropertyNames        = data.PropertyNames, 
                PropertyValuesBinary = data.PropertyValuesBinary, 
                PropertyValuesString = data.PropertyValuesString,
                Userid               = data.ID
            };
            context.Profiles.AddObject( profile );
        } else {
            profile = QueryUerProfiles( context ).Where( p => p.Userid == data.ID ).Single();
            if ( profile.LastUpdatedDate      != data.LastUpdatedDate )      profile.LastUpdatedDate      = data.LastUpdatedDate.LocalDateTime;
            if ( profile.PropertyNames        != data.PropertyNames )        profile.PropertyNames        = data.PropertyNames;
            if ( profile.PropertyValuesBinary != data.PropertyValuesBinary ) profile.PropertyValuesBinary = data.PropertyValuesBinary;
            if ( profile.PropertyValuesString != data.PropertyValuesString ) profile.PropertyValuesString = data.PropertyValuesString;
        }
    } catch ( Exception ex ) {
        throw new DataAccessException( DataAccessOperation.SaveProfile, FailureReason.DatabaseError, ex );
    }
}

这个错误的原因是什么?我该如何解决这个问题?

感谢托尼

公立小学

进一步调查后发现,问题不在于插入行,而在于试图在与插入行相同的事务中更新行。

本质上,有一个对象队列要写入数据库。依次从队列中删除每个对象,并调用类似于上面的方法将其保存到适当的表中。

第一次看到一个特定的ID时,该方法插入它。也就是说,Any检查返回false。代码创建新的实体对象,并为与表对应的实体集调用AddObject方法。到目前为止一切顺利。

之后,假定Any检查将返回true,表明已经存在具有给定ID的行。然后,它应该更新行。但是,似乎Any检查第二次也返回false,即使我在第一次调用时对应于表的实体集上调用AddObject。

你知道我做错了什么吗?

PPS

我已经做了一些更多的测试与Sql监视器打开。我们正在使用Devart dotConnect for PostgreSql库,他们有一个你可以下载的工具,叫做DbMonitor。这允许您跟踪发出的SQL &由实体框架执行。

结果是(可预见的,当它发生时),对。any的调用立即作为对数据库的查询执行,而所有的插入都排队并在调用SaveChanges后应用。Any调用似乎不考虑正在等待插入的任何行,只考虑数据库调用返回的行。由于要插入的行尚未插入,因此在调用SaveChanges之前,此查询将始终返回false。

结果,我的逻辑就这样行不通了。当一个挂起插入的行被更新时(即,它在队列中第二次出现),插入逻辑将第二次运行。有时我会得到一个异常,表示违反了唯一键或主键约束,其他时候我会得到我最初发布的消息。

我需要在单个事务中完成所有的插入和更新,并且我需要在调用AddObject时在数据库中进行插入。然而,我仍然需要所有的插入&如果插入或更新单行出错,或者c#代码中出现其他错误,则将更新回滚。

谁有任何想法,我怎么能做到这一点与实体框架?

实体框架模型产生奇怪的错误消息

看起来不像是在添加到db的Profile对象上设置主键。Guid的默认值为Guid。空,因此第一个对象被保存,但随后的每个对象都失败。

下面的代码设置主键
if ( !context.Profiles.Any( p => p.Userid == data.ID ) ) {
            profile = new CarSystem.Profile{
                LastUpdatedDate      = data.LastUpdatedDate.LocalDateTime,
                PropertyNames        = data.PropertyNames, 
                PropertyValuesBinary = data.PropertyValuesBinary, 
                PropertyValuesString = data.PropertyValuesString,
                Userid               = data.ID
                //add the next line to ensure there is a pk field
                ID                   = Guid.NewGuid()
            };

我已经按照我想要的方式解决了这个问题。

我所有的问题的原因,首先是实体框架等待,直到你调用SaveChanges写入任何更改到数据库。但是,当执行. any()调用时,会立即生成并执行SQL,忽略正在向数据库写入的任何挂起的更改。所以我的代码总是认为它必须插入新行,即使一个是真正的插入,另一个应该是更新。

接下来,我尝试使用TransactionScope对象,在插入或更新每个对象后调用SaveChanges()。然后调用TransactionScope实例的Completed()方法来"提交"更改。嗯,low &瞧,结果和等待调用SaveChanges时完全一样!在调用scope.Completed()之前,不会向数据库写入任何内容!

我发现有效的解决方案如下面的代码所示:

using ( CarSystemEntities context = new CarSystemEntities() ) { 
    if ( context.Connection.State != ConnectionState.Open ) { 
        context.Connection.Open(); 
    } 
    DbTransaction transaction = context.Connection.BeginTransaction(); 
    try { 
        <Data processing code here> 
        transaction.Commit(); 
    } catch ( Exception ex ) { 
        transaction.Rollback(); 
        <More exception code here as needed> 
    }             
}

结合调用SaveChanges在每个context..AddObject或属性更新后,一切都是我需要它工作。INSERT &生成更新语句&在调用SaveChanges时执行。对. any的调用还会考虑通过前一次迭代插入到表中的任何行。一切都在一个真实的数据库事务中,可以作为一个整体提交或回滚。