0

我在我们的一种环境中收到以下错误。它似乎在 IIS 重新启动时发生,但我们还没有缩小细节来重现它。

A DataTable named 'PeoplePassword' already belongs to this DataSet. 
at System.Data.DataTableCollection.RegisterName(String name, String tbNamespace)
at System.Data.DataTableCollection.BaseAdd(DataTable table) 
at System.Data.DataTableCollection.Add(DataTable table) 
at SubSonic.SqlDataProvider.GetTableSchema(String tableName, TableType tableType) 
at SubSonic.DataService.GetSchema(String tableName, String providerName, TableType tableType) 
at SubSonic.DataService.GetTableSchema(String tableName, String providerName) 
at SubSonic.Query..ctor(String tableName) 
at Wad.Elbert.Data.Enrollment.FetchByUserId(Int32 userId) 

基于堆栈跟踪,我相信在创建查询对象时该方法的第二行发生了错误。如果其他人有这个问题,请告诉我。谢谢!

该函数的代码是:

        public static List<Enrollment> FetchByUserId(int userId)
    {
        List<Enrollment> enrollments = new List<Enrollment>();
        SubSonic.Query query = new SubSonic.Query("Enrollment");
        query.SelectList = "userid, prompt, response, validationRegex, validationMessage, responseType, enrollmentSource";
        query.QueryType = SubSonic.QueryType.Select;
        query.AddWhere("userId", userId);
        DataSet dataset = query.ExecuteDataSet();
        if (dataset != null &&
            dataset.Tables.Count > 0)
        {
            foreach (DataRow dr in dataset.Tables[0].Rows)
            {
                enrollments.Add(new Enrollment((int)dr["userId"], dr["prompt"].ToString(), dr["response"].ToString(), dr["validationRegex"] != null ? dr["validationRegex"].ToString() : string.Empty, dr["validationMessage"] != null ? dr["validationMessage"].ToString() : string.Empty, (int)dr["responseType"], (int)dr["enrollmentSource"]));
            }
        }
        return enrollments;
    }
4

1 回答 1

0

这是一个线程问题。

Subsonic 在第一次调用时加载它的模式,SubSonic.DataService.GetTableSchema(...)但这不是线程安全的。

让我用一个小例子来证明这一点

private static Dictionary<string, DriveInfo> drives = new Dictionary<string, DriveInfo>;

private static DriveInfo GetDrive(string name)
{
    if (drives.Count == 0)
    {
        Thread.Sleep(10000); // fake delay
        foreach(var drive in DriveInfo.GetDrives)
            drives.Add(drive.Name, drive);
    }
    if (drives.ContainsKey(name))
        return drives[name];
    return null;
}

这很好地解释了会发生什么,在第一次调用此方法时字典为空
如果是这种情况,该方法将预加载所有驱动器。

对于每次调用,都会返回请求的驱动器(或 null)。

但是如果你在启动后直接触发该方法两次会发生什么?然后两个执行都尝试在字典中加载驱动器。添加驱动器的第一个获胜,第二个将引发 ArgumentException(元素已存在)。

初始预加载后,一切正常。

长话短说,你有两个选择。

  1. 修改亚音速源以使SubSonic.DataService.GetTableSchema(...)线程安全。 http://msdn.microsoft.com/de-de/library/c5kehkcz(v=vs.80).aspx

  2. 在接受请求之前“热身”亚音速。实现这一点的技术取决于您的应用程序设计。对于 ASP.NET,您有一个Application_Start在应用程序生命周期中只执行一次的方法 http://msdn.microsoft.com/en-us/library/ms178473(v=vs.100).aspx

所以你基本上可以放一个

var count = new SubSonic.Query("Enrollment").GetRecordCount();

在强制亚音速初始化表模式本身的方法中。

于 2013-05-29T12:55:15.873 回答