更新分层编号
Updating Hierarchical Numbering
我的应用程序中有以下 classes,其中包含如下分层数据:
Class A
{
string Id{get;set;}
string Name{get;set;}
string Order{get;set;}
string ParentId{get;set;}
}
Class B
{
string Id{get;set;}
string Name{get;set;}
string Order{get;set;}
string ClassAId{get;set;}
}
Class C
{
string Id{get;set;}
string Name{get;set;}
string Order{get;set;}
string ClassBId{get;set;}
}
class A 的示例数据为
Id = "1.1" Id = "2.1"
Name = "SomeName" Name = "Name2"
Order = 1 Order = 1
ParentId = "1" ParentId = 2
Id = "1.2" Id = "2.2"
Name = "Name2" Name = "Name3"
Order = 2 Order = 2
ParentId = "1" ParentId = 2
Class B 的示例数据为
Id = "1.1.1" Id = "2.1.1"
Name = "SomeName" Name = "Name2"
Order = 1 Order = 1
ParentId = "1.1" ParentId = 2.1
Id = "1.2.1" Id = "2.1.2"
Name = "Name2" Name = "Name3"
Order = 2 Order = 2
ParentId = "1.2" ParentId = 2.1
与 Class C 类似,数据看起来像
Id = "1.1.1.1" Id = "2.1.1.1"
Name = "SomeName" Name = "Name2"
Order = 1 Order = 1
ParentId = "1.1.1" ParentId = 2.1.1
现在如果用户想要更新现有值之间的值,它应该工作的方式是,假设我为 Class A 输入一个介于 1.2 和 1.3 之间的值,它应该首先创建一个新的值称为 1.4,然后将 1.3 及其子项的所有内容移动到 1.4(即,如果 1.3 有自己的子项,如 1.3.1 等和 1.3.1.1 等,则应分别重命名为 1.4.1 和 1.4.1.1和 1.3 应该没有任何具有空值的层次结构。基本上在插入之间应该更新记录的完整层次结构。我能够通过找到当前 ID 并找到最大顺序并向其添加 1 来正确生成下一个序列。问题 I我面临的是插入和更新整个层次结构之间。
任何想法都会有所帮助。下面是我为输入新值而编写的代码 Class A :
//找到最大阶数,加1插入新记录。
var currentClass = listOfClassA.Where(x => x.Id = currentId).SingleOrDefault();
var maxOrder = listOfClassA.Max(x => x.Order);
var objClassA = new A();
objClassA.Order = maxOrder + 1;
objClassA.ParentId = currentClassA.ParentId;
objClassA.Name = "";
objClassA.Id = currentClassA.ParentId + "." + objClassA.Order;
只是一个想法,根据您的要求,但是将 类 包含到您的分层数据结构中不是更容易吗,例如:
class A
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
//this is the tricky one.
public string ParentId{get;set;}
}
class B
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public A ClassA{get;set;}
}
class C
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public B ClassB{get;set;}
}
另一个想法:由于您的 类 非常相似,您也可以像这样创建一个真正的层次结构:
public class Foo
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public Foo Parent {get;set;}
public Foo Child{get;set;}
}
展开之前的思路,你会发现你创建了一个LinkedList
的排序,例如:
public class Foo
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
}
var list = new LinkedList<Foo>();
还有另一种选择:这是一个更像 tree
的结构,我相信它叫做 composite pattern:
public class Foo
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public Foo Parent {get;set;}
public IEnumerable<Foo> Children{get;set;}
}
希望对您有所帮助。使用上述模式之一将使生成分层 ID 字符串变得容易,例如 1.2.3.4.e.t.c.
,如果您甚至需要的话;因为序列化整个东西也很容易。不过要注意循环引用 ;-)
此外,使用 SelectMany
或递归调用,找到最大值或最小值的其他操作也很可行。
我们决定更改我们的设计以使用动态 expando 对象并使用它们来生成层次编号 [我们的要求是使用动态集合 (mongodb) 并动态生成它们]。尽管 class 结构将与上面相同。下面是我们为分层编号编写的代码。此代码将 Id 作为输入(例如 1.1 或 1.2 或 1.1.1 或 1.1.1.1)
dynamic expando = new ExpandoObject();
var collectionModel = expando as IDictionary<string, Object>;
var lastDotPosition = value.LastIndexOf('.');
var parentId = value.Substring(0, lastDotPosition);
collectionModel.Add("ParentId", parentId);
var order = Convert.ToInt32(value.Split('.').Last());
var newOrder = order + 1;
var Id = parentId + '.' + newOrder.ToString();
collectionModel.Add("Index", newOrder);//newOrder
collectionModel["Id"] = Id;
var filter = Builders<dynamic>.Filter.Gt("Index", order);
filter = filter & Builders<dynamic>.Filter.Eq("ParentId", parentId);
var collection = _db.GetCollection<dynamic>(collectionName);
var remainingList = collection.Find(filter).ToList();
var dynamicList = new List<ExpandoObject>();
dynamicList.Add((ExpandoObject)collectionModel);
// below updates the next record ids and parent id to ensure numbering is maintained
for (int i = 0; i < remainingList.Count; i++)
{
var remainingRecord = remainingList[i] as IDictionary<string, object>;
var newRecord = new ExpandoObject() as IDictionary<string, Object>;
for(int j = 0; j < listOfFieldNames.Count; j++)
{
var fieldName = listOfFieldNames[j];
Object dictValue = "";
remainingRecord.TryGetValue(fieldName, out dictValue);
if (fieldName == "Id")
{
newRecord[fieldName] = parentId + "." + (newOrder + 1);
}
else
{
newRecord[fieldName] = dictValue;
}
}
newRecord["Index"] = newOrder + 1;
newRecord["ParentId"] = parentId;
newOrder++;
dynamicList.Add((ExpandoObject)newRecord);
}
//Now update child or grandchild if any
var updateForChildList = remainingList.OrderByDescending(x => ((IDictionary<string, object>)x)["Index"]).ToList();
for (int k = 0; k < updateForChildList.Count; k++)
{
var oldRecord = updateForChildList[k] as IDictionary<string, object>;
var oldParentId = oldRecord["Id"];
Object dictValue = "";
oldRecord.TryGetValue("Index", out dictValue);
var newParentId = oldRecord["ParentId"] + "." + Convert.ToString(Convert.ToInt32(dictValue.ToString()) + 1);
UpdateParentIdForChildren(oldParentId.ToString(), newParentId, Convert.ToInt32(collectionOrder + 1));
}
collection.DeleteMany(filter);
collection.InsertMany(dynamicList);
使用递归查找子孙并更新他们的 parentids 和 ids
public void UpdateParentIdForChildren(string oldParentId, string newParentId, int collectionIndex)
{
if (collectionIndex > collectionList.Count)
{
return;
}
var currentCollection = _db.GetCollection<dynamic>(collectionName);
var filter = Builders<dynamic>.Filter.Eq("ParentId", oldParentId);
var oldParentIdList = currentCollection.Find(filter).ToList();
var reoldParentIdList = oldParentIdList.OrderByDescending(x => ((IDictionary<string, object>)x)["Index"]).ToList();
if (reoldParentIdList.Count > 0)
{
for (int i = 0; i < reoldParentIdList.Count; i++)
{
var remainingRecord = reoldParentIdList[i] as IDictionary<string, object>;
Object OldIdValue = "";
remainingRecord.TryGetValue("Id", out OldIdValue);
Object indexValue = "";
remainingRecord.TryGetValue("Index", out indexValue);
var newId = newParentId + '.' + indexValue;
currentCollection.UpdateOne(filter, Builders<dynamic>.Update.Set("Id", newId));
currentCollection.UpdateOne(filter, Builders<dynamic>.Update.Set("ParentId", newParentId));
UpdateParentIdForChildren(OldIdValue.ToString(), newId, collectionIndex + 1);
}
}
}
我的应用程序中有以下 classes,其中包含如下分层数据:
Class A
{
string Id{get;set;}
string Name{get;set;}
string Order{get;set;}
string ParentId{get;set;}
}
Class B
{
string Id{get;set;}
string Name{get;set;}
string Order{get;set;}
string ClassAId{get;set;}
}
Class C
{
string Id{get;set;}
string Name{get;set;}
string Order{get;set;}
string ClassBId{get;set;}
}
class A 的示例数据为
Id = "1.1" Id = "2.1"
Name = "SomeName" Name = "Name2"
Order = 1 Order = 1
ParentId = "1" ParentId = 2
Id = "1.2" Id = "2.2"
Name = "Name2" Name = "Name3"
Order = 2 Order = 2
ParentId = "1" ParentId = 2
Class B 的示例数据为
Id = "1.1.1" Id = "2.1.1"
Name = "SomeName" Name = "Name2"
Order = 1 Order = 1
ParentId = "1.1" ParentId = 2.1
Id = "1.2.1" Id = "2.1.2"
Name = "Name2" Name = "Name3"
Order = 2 Order = 2
ParentId = "1.2" ParentId = 2.1
与 Class C 类似,数据看起来像
Id = "1.1.1.1" Id = "2.1.1.1"
Name = "SomeName" Name = "Name2"
Order = 1 Order = 1
ParentId = "1.1.1" ParentId = 2.1.1
现在如果用户想要更新现有值之间的值,它应该工作的方式是,假设我为 Class A 输入一个介于 1.2 和 1.3 之间的值,它应该首先创建一个新的值称为 1.4,然后将 1.3 及其子项的所有内容移动到 1.4(即,如果 1.3 有自己的子项,如 1.3.1 等和 1.3.1.1 等,则应分别重命名为 1.4.1 和 1.4.1.1和 1.3 应该没有任何具有空值的层次结构。基本上在插入之间应该更新记录的完整层次结构。我能够通过找到当前 ID 并找到最大顺序并向其添加 1 来正确生成下一个序列。问题 I我面临的是插入和更新整个层次结构之间。 任何想法都会有所帮助。下面是我为输入新值而编写的代码 Class A :
//找到最大阶数,加1插入新记录。
var currentClass = listOfClassA.Where(x => x.Id = currentId).SingleOrDefault();
var maxOrder = listOfClassA.Max(x => x.Order);
var objClassA = new A();
objClassA.Order = maxOrder + 1;
objClassA.ParentId = currentClassA.ParentId;
objClassA.Name = "";
objClassA.Id = currentClassA.ParentId + "." + objClassA.Order;
只是一个想法,根据您的要求,但是将 类 包含到您的分层数据结构中不是更容易吗,例如:
class A
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
//this is the tricky one.
public string ParentId{get;set;}
}
class B
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public A ClassA{get;set;}
}
class C
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public B ClassB{get;set;}
}
另一个想法:由于您的 类 非常相似,您也可以像这样创建一个真正的层次结构:
public class Foo
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public Foo Parent {get;set;}
public Foo Child{get;set;}
}
展开之前的思路,你会发现你创建了一个
LinkedList
的排序,例如:
public class Foo
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
}
var list = new LinkedList<Foo>();
还有另一种选择:这是一个更像
tree
的结构,我相信它叫做 composite pattern:
public class Foo
{
public string Id{get;set;}
public string Name{get;set;}
public string Order{get;set;}
public Foo Parent {get;set;}
public IEnumerable<Foo> Children{get;set;}
}
希望对您有所帮助。使用上述模式之一将使生成分层 ID 字符串变得容易,例如 1.2.3.4.e.t.c.
,如果您甚至需要的话;因为序列化整个东西也很容易。不过要注意循环引用 ;-)
此外,使用 SelectMany
或递归调用,找到最大值或最小值的其他操作也很可行。
我们决定更改我们的设计以使用动态 expando 对象并使用它们来生成层次编号 [我们的要求是使用动态集合 (mongodb) 并动态生成它们]。尽管 class 结构将与上面相同。下面是我们为分层编号编写的代码。此代码将 Id 作为输入(例如 1.1 或 1.2 或 1.1.1 或 1.1.1.1)
dynamic expando = new ExpandoObject();
var collectionModel = expando as IDictionary<string, Object>;
var lastDotPosition = value.LastIndexOf('.');
var parentId = value.Substring(0, lastDotPosition);
collectionModel.Add("ParentId", parentId);
var order = Convert.ToInt32(value.Split('.').Last());
var newOrder = order + 1;
var Id = parentId + '.' + newOrder.ToString();
collectionModel.Add("Index", newOrder);//newOrder
collectionModel["Id"] = Id;
var filter = Builders<dynamic>.Filter.Gt("Index", order);
filter = filter & Builders<dynamic>.Filter.Eq("ParentId", parentId);
var collection = _db.GetCollection<dynamic>(collectionName);
var remainingList = collection.Find(filter).ToList();
var dynamicList = new List<ExpandoObject>();
dynamicList.Add((ExpandoObject)collectionModel);
// below updates the next record ids and parent id to ensure numbering is maintained
for (int i = 0; i < remainingList.Count; i++)
{
var remainingRecord = remainingList[i] as IDictionary<string, object>;
var newRecord = new ExpandoObject() as IDictionary<string, Object>;
for(int j = 0; j < listOfFieldNames.Count; j++)
{
var fieldName = listOfFieldNames[j];
Object dictValue = "";
remainingRecord.TryGetValue(fieldName, out dictValue);
if (fieldName == "Id")
{
newRecord[fieldName] = parentId + "." + (newOrder + 1);
}
else
{
newRecord[fieldName] = dictValue;
}
}
newRecord["Index"] = newOrder + 1;
newRecord["ParentId"] = parentId;
newOrder++;
dynamicList.Add((ExpandoObject)newRecord);
}
//Now update child or grandchild if any
var updateForChildList = remainingList.OrderByDescending(x => ((IDictionary<string, object>)x)["Index"]).ToList();
for (int k = 0; k < updateForChildList.Count; k++)
{
var oldRecord = updateForChildList[k] as IDictionary<string, object>;
var oldParentId = oldRecord["Id"];
Object dictValue = "";
oldRecord.TryGetValue("Index", out dictValue);
var newParentId = oldRecord["ParentId"] + "." + Convert.ToString(Convert.ToInt32(dictValue.ToString()) + 1);
UpdateParentIdForChildren(oldParentId.ToString(), newParentId, Convert.ToInt32(collectionOrder + 1));
}
collection.DeleteMany(filter);
collection.InsertMany(dynamicList);
使用递归查找子孙并更新他们的 parentids 和 ids
public void UpdateParentIdForChildren(string oldParentId, string newParentId, int collectionIndex)
{
if (collectionIndex > collectionList.Count)
{
return;
}
var currentCollection = _db.GetCollection<dynamic>(collectionName);
var filter = Builders<dynamic>.Filter.Eq("ParentId", oldParentId);
var oldParentIdList = currentCollection.Find(filter).ToList();
var reoldParentIdList = oldParentIdList.OrderByDescending(x => ((IDictionary<string, object>)x)["Index"]).ToList();
if (reoldParentIdList.Count > 0)
{
for (int i = 0; i < reoldParentIdList.Count; i++)
{
var remainingRecord = reoldParentIdList[i] as IDictionary<string, object>;
Object OldIdValue = "";
remainingRecord.TryGetValue("Id", out OldIdValue);
Object indexValue = "";
remainingRecord.TryGetValue("Index", out indexValue);
var newId = newParentId + '.' + indexValue;
currentCollection.UpdateOne(filter, Builders<dynamic>.Update.Set("Id", newId));
currentCollection.UpdateOne(filter, Builders<dynamic>.Update.Set("ParentId", newParentId));
UpdateParentIdForChildren(OldIdValue.ToString(), newId, collectionIndex + 1);
}
}
}