在SubSonic中创建Query对象时出错

时间:2013-04-30 16:18:51

标签: subsonic

我在其中一个环境中收到以下错误。它似乎发生在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;
    }

1 个答案:

答案 0 :(得分: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. 在接受请求之前,
  3. “热身”亚音速。实现这一目标的技术取决于您的应用程序设计。对于ASP.NET,您有一个Application_Start方法,该方法仅在您的应用程序生命周期中执行一次 http://msdn.microsoft.com/en-us/library/ms178473(v=vs.100).aspx

  4. 所以你基本上可以放一个

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

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