避免类的循环引用

时间:2016-10-17 23:37:29

标签: c# recursion circular-dependency circular-reference

我有一个创建并返回“Person”的函数。 “人”有一个“配偶”财产,当然是另一个“人”。这会导致无限递归,因为每次调用函数时,创建的“Person”始终是“new”。有没有办法使用相同的功能(如下所示)而不会导致无限循环?

public PersonModel Load(int personID)
{
    PersonModel person = new PersonModel();
    using (SqlConnection conn = new SqlConnection())
    {
        conn.ConnectionString = Helpers.ConnectionDB;
        SqlCommand command = new SqlCommand();
        command.Connection = conn;
        command.CommandType = CommandType.StoredProcedure;
        command.CommandText = "LoadPerson";
        command.Parameters.Add(new SqlParameter("@PersonID", personID));
        conn.Open();
        SqlDataReader reader = command.ExecuteReader();
        if (reader.Read())
        {
            person.PersonID = int.Parse(reader["PersonID"].ToString());
            person.FirstName = reader["FirstName"].ToString();
            person.LastName = reader["LastName"].ToString();
            person.MiddleName = reader["MiddleName"].ToString();
            person.Age = reader["Age"] != DBNull.Value ? int.Parse(reader["Age"].ToString()) : (int?)null;
            person.SpouseID = reader["SpouseID"] != DBNull.Value ? int.Parse(reader["SpouseID"].ToString()) : (int?)null;
            if (person.SpouseID != null && person.Spouse == null)
            {
                person.Spouse = this.Load(person.SpouseID.Value);
            }
        }
        conn.Close();
    }
    return person;
}

3 个答案:

答案 0 :(得分:2)

使用Dictionary<Int32,PersonModel>跟踪已加载的实体:

public PersonModel Load(Dictionary<Int32,PersonModel> dict, int personId) {

    PersonModel ret;
    if( dict.TryGetValue( personId, out ret ) ) return ret;

    // load from database here, but do not call recursively just yet
    ret = new PersonModel() { ... };

    dict.Add( ret.PersonId, ret );

    ret.Spouse = this.Load( dict, person.SpouseId.Value );
}

答案 1 :(得分:0)

我可以想到几个不同的选择:

  1. Load()添加可选参数,表示您不应尝试加载配偶。这有点像黑客但很简单。

    public PersonModel Load(int personID, bool loadSpouse = true)
    {
        ...
        if (loadSpouse && person.SpouseID != null && person.Spouse == null)
        {
            person.Spouse = this.Load(person.SpouseID.Value, false);
        }
        ...
    
        return person;
    }
    
  2. 使用左外连接选择存储过程中配偶的属性。这样做会更好,如果您想要选择所有员工,这将更有效率,因为它只是一个数据库访问。您可以在if语句中添加这样的代码来构建配偶对象:

    person.Spouse = new PersonModel()
    person.Spouse.FirstName = reader["SpouseFirstName"].ToString();
    ...
    

答案 2 :(得分:0)

不完全符合OP的问题,但由于我因为标题而登陆这里,因此为此添加一般答案可能会有所帮助。

所以在我的场景中,我需要在整个图形中更改属性,将其所有子属性设置为任意深度。

为了确保我没有执行两次实例(因此允许StackOverflowException),我在每次调用时传递HashSet<T>,因此我们知道跳过执行的项目:< / p>

//The executed parameter needn't ever be provided externally.
static void SetState(IObject obj, State state, HashSet<IObject> executed = null)
{
  if(executed == null) executed = new HashSet<IObject>(/* provide comparer if needed*/);
  if(!executed.Add(obj)) return;

  //safe to continue walking graph
  obj.State = state;
  var props = obj.GetObjectPropertyValues().OfType<IObject>();
  foreach (var prop in props)
    SetState(obj, state, executed);
}

注意,这些项必须实现正确的相等和GetHashCode函数,或者应该将comparer参数传递给HashSet的构造函数。