Saeed_m_Farid
سه شنبه 06 تیر 1391, 11:40 صبح
با سلام
پیرو صحبت هایی که با دوستان کردیم، این سوال رو با جزئیاتش مطرح میکنم:
پیش زمینه:
در یک پروژه که مدیریت کاربران با XML هست، میخوام در انتهای کاربران مربوط به یک حوزه، کاربر موردنظر رو اضافه/ویرایش کنم؛ کلمه عبور هم بصورت MD5 بعنوان attribute در اون node ذخیره میشه
مشکل:
من نمیتونم یک کوئری درست بنویسم که دقیقاً اون Node ای که اسمش رو دارم رو بهم بده تا من XElement موردنظرم رو بهش اضافه یا ویرایش کنم، البته یه کارهایی کردم ولی کاملاً من درآوردی هست، که در پایین بهش اشاره میکنم ...
Code Snippet
برای جزئیات بیشتر فرض کنید ساختار XML من یه اینصورت هست:
<?xml version="1.0" encoding="utf-8"?>
<X1>
<X2>
<X3>
<!--More...!-->
<X4>
<!--More...!-->
<X5>
<X5_1>
<!--More...!-->
<X5_1>
<!--More...!-->
<X5_n>
<Element> blw-blw-blw </Element>
<Element> blw-blw-blw </Element>
<Element> blw-blw-blw </Element>
<!--More...!-->
<!-- My XElement Must add HHHHHHHEEEEEERRRRREEEEEE!-->
</X5_n>
<!--More...!-->
</X5>
<!--More...!-->
</X4>
<!--More...!-->
</X3>
</X2>
</X1>
که البته واقعیش هم میشه فایل زیر:
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<OurCompany>
<Settings>
<!--More...!-->
<Regions>
<!--More...!-->
<Cities>
<Abeshahmad>
<XMLRPCUser encrypt="5a105e8b9d40e1329780d62ea2265d8a">user1</XMLRPCUser>
<!--More...!-->
<XMLRPCUser encrypt="5a105e8b9d40e1329780d62ea2265d8a">user_n</XMLRPCUser>
<!-- My user Must add HHHHHHHEEEEEERRRRREEEEEE!-->
</City>
<!--More...!-->
</Cities>
<!--More...!-->
</Regions>
<!--More...!-->
</Settings>
</OurCompany>
</Configuration>
همونطورکه میبینید من میخوام بجای <!-- My XElement Must add HHHHHHHEEEEEERRRRREEEEEE!--> یک المنت (XElement) اضافه کنم و درصورت موجود بودن تعییرش بدم؛ کاری که کردم این بوده که مجبور شدم یه تابع بنویسم GetSpecifiedNode که بره و XElement موردنظر من رو پیدا کنه و برگردونه (و اگه نباشه ایجاد کنه)، ولی اولاً خیلی احمقانه بنظر میرسه(پس حتماً راه بهتری هست) و مهمتر اینکه کاملاً هاردکد هست و اصلاً انعطاف پذیری نداره:
/// <summary>
/// Opening XML configuration file[1] and assign to a XElement
/// instance refereced as root, then find Users child node
/// </summary>
/// <param name="file">
/// Setting file path, default is "Configs.config"
/// </param>
/// <param name="root">
/// Called by reference XElement root node of Xml to assign
/// </param>
/// <returns>
/// Hierarchial Users child node in Xml Structure
/// </returns>
/// <remarks>
/// Note: Created a new xml document if one isn't found,
/// </remarks>
private static XElement GetSpecifiedNode(string file, ref XElement root, string node)
{
try
{
XElement child;
// Configuration setting XML hierarchy nodes
string[] nodes =
new string[] {
"Configuration",
"OurCompany",
"Settings",
"Regions",
"Cities",
node
};
// Root node assignment
if (File.Exists(file))
root = XElement.Load(file);
else
root = new XElement(nodes[0],
new XElement(nodes[1],
new XElement(nodes[2],
new XElement(nodes[3],
new XElement(nodes[4],
new XElement(nodes[5]))));
// Checking if our specified node exist or not?
if (root.Element(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Elements(nodes[5]).Any())
child = root.Element(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Elements(nodes[5]).Last();
else
{
if (!root.Elements(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Any())
root.Add(
new XElement(nodes[0],
new XElement(nodes[1],
new XElement(nodes[2],
new XElement(nodes[3],
new XElement(nodes[4]))));
root.Element(nodes[1]).Element(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Add(new XElement(nodes[5]));
child = root.Elements(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Elements(nodes[5]).Last();
}
return child;
}
catch (Exception ex)
{
Tracer.LogError(
ex.Message,
"Settings",
(new System.Diagnostics.StackFrame()).GetMethod().Name) ;
throw;
}
}
برای ویرایش هم مشکل رو با یک Extension method بصورت زیر حل کردم:
/// <summary>
/// Replace the contents of a node in an XElement hierarchy
/// when the element name and all the attribute names and
/// values match an input element.
/// (If there is no match, the new element can be added.)
/// </summary>
/// <param name="source">
/// Source root XElement to replace/add
/// </param>
/// <param name="node">
/// XElemenet to replacement or add
/// </param>
private static void ReplaceOrAdd(this XElement source, XElement node)
{
var q = from element in source.Elements()
where element.Name == node.Name
&& element.Attributes().All
(a => node.Attributes().Any
(b => a.Name == b.Name && a.Value == b.Value))
select element;
var n = q.LastOrDefault();
if (n == null) source.Add(node);
else n.ReplaceWith(node);
}
با این تفاسیر، تابع افزودن کاربر هم به اینصورت شد:
/// <summary>
/// Add a new authentication entry as username & password if not exist,
/// otherwise replace md5 password.
/// </summary>
/// <param name="username">
/// String value represent username
/// </param>
/// <param name="md5Password">
/// Hashed with MD5 provider password to save and chack later
/// </param>
/// <param name="file">
/// Setting file path, if leave as empty default is "Configs.config"
/// </param>
/// <param name="city">
/// City name that specified User belong it
/// </param>
public static void AddUserInfo(string username,
string pass,
string file = "",
string city = "")
{
try
{
file = (file == String.Empty) ? XmlConfigPath : file;
XElement root = new XElement("Initial"),
child = GetSpecifiedNode(file, ref root, "Users");
// Add user authentication info to specified node
city = (city == String.Empty) ? "Deaulet" : city;
child.ReplaceOrAdd(new XElement("XMLRPCUser", username,
new XAttribute("encrypt", Security.GetMd5Hash(pass))));
// Save change(s) to file
root.Save(file);
}
catch (Exception ex)
{
Tracer.LogError(
ex.Message,
"Settings",
(new System.Diagnostics.StackFrame()).GetMethod().Name) ;
throw;
}
}
جمع بندی:
بیزحمت یه حجم کد نگاه نکنید، من چون راهی نداشتم اینهمه خزعبلات سرهم بندی کردم! ولی ممکنه مثلاً تمام کد GetSpecifiedNode با یه خط کد حل بشه، من اون رو میخوام اگه اطلاع داشته باشید.
تشکر و قدردانی:
از تمام کسانی که این پست رو میخونن، دلداری میدن، از خدا برام شفا میخوان، راه حلی به ذهنشون میرسه، فحش نمیدن و یا خدای نکرده جایگزینی براش دارن پیشاپیش کمال تقدیر و تشکر بعمل میاد.
والسلام، نامه تمام.
پیرو صحبت هایی که با دوستان کردیم، این سوال رو با جزئیاتش مطرح میکنم:
پیش زمینه:
در یک پروژه که مدیریت کاربران با XML هست، میخوام در انتهای کاربران مربوط به یک حوزه، کاربر موردنظر رو اضافه/ویرایش کنم؛ کلمه عبور هم بصورت MD5 بعنوان attribute در اون node ذخیره میشه
مشکل:
من نمیتونم یک کوئری درست بنویسم که دقیقاً اون Node ای که اسمش رو دارم رو بهم بده تا من XElement موردنظرم رو بهش اضافه یا ویرایش کنم، البته یه کارهایی کردم ولی کاملاً من درآوردی هست، که در پایین بهش اشاره میکنم ...
Code Snippet
برای جزئیات بیشتر فرض کنید ساختار XML من یه اینصورت هست:
<?xml version="1.0" encoding="utf-8"?>
<X1>
<X2>
<X3>
<!--More...!-->
<X4>
<!--More...!-->
<X5>
<X5_1>
<!--More...!-->
<X5_1>
<!--More...!-->
<X5_n>
<Element> blw-blw-blw </Element>
<Element> blw-blw-blw </Element>
<Element> blw-blw-blw </Element>
<!--More...!-->
<!-- My XElement Must add HHHHHHHEEEEEERRRRREEEEEE!-->
</X5_n>
<!--More...!-->
</X5>
<!--More...!-->
</X4>
<!--More...!-->
</X3>
</X2>
</X1>
که البته واقعیش هم میشه فایل زیر:
<?xml version="1.0" encoding="utf-8"?>
<Configuration>
<OurCompany>
<Settings>
<!--More...!-->
<Regions>
<!--More...!-->
<Cities>
<Abeshahmad>
<XMLRPCUser encrypt="5a105e8b9d40e1329780d62ea2265d8a">user1</XMLRPCUser>
<!--More...!-->
<XMLRPCUser encrypt="5a105e8b9d40e1329780d62ea2265d8a">user_n</XMLRPCUser>
<!-- My user Must add HHHHHHHEEEEEERRRRREEEEEE!-->
</City>
<!--More...!-->
</Cities>
<!--More...!-->
</Regions>
<!--More...!-->
</Settings>
</OurCompany>
</Configuration>
همونطورکه میبینید من میخوام بجای <!-- My XElement Must add HHHHHHHEEEEEERRRRREEEEEE!--> یک المنت (XElement) اضافه کنم و درصورت موجود بودن تعییرش بدم؛ کاری که کردم این بوده که مجبور شدم یه تابع بنویسم GetSpecifiedNode که بره و XElement موردنظر من رو پیدا کنه و برگردونه (و اگه نباشه ایجاد کنه)، ولی اولاً خیلی احمقانه بنظر میرسه(پس حتماً راه بهتری هست) و مهمتر اینکه کاملاً هاردکد هست و اصلاً انعطاف پذیری نداره:
/// <summary>
/// Opening XML configuration file[1] and assign to a XElement
/// instance refereced as root, then find Users child node
/// </summary>
/// <param name="file">
/// Setting file path, default is "Configs.config"
/// </param>
/// <param name="root">
/// Called by reference XElement root node of Xml to assign
/// </param>
/// <returns>
/// Hierarchial Users child node in Xml Structure
/// </returns>
/// <remarks>
/// Note: Created a new xml document if one isn't found,
/// </remarks>
private static XElement GetSpecifiedNode(string file, ref XElement root, string node)
{
try
{
XElement child;
// Configuration setting XML hierarchy nodes
string[] nodes =
new string[] {
"Configuration",
"OurCompany",
"Settings",
"Regions",
"Cities",
node
};
// Root node assignment
if (File.Exists(file))
root = XElement.Load(file);
else
root = new XElement(nodes[0],
new XElement(nodes[1],
new XElement(nodes[2],
new XElement(nodes[3],
new XElement(nodes[4],
new XElement(nodes[5]))));
// Checking if our specified node exist or not?
if (root.Element(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Elements(nodes[5]).Any())
child = root.Element(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Elements(nodes[5]).Last();
else
{
if (!root.Elements(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Any())
root.Add(
new XElement(nodes[0],
new XElement(nodes[1],
new XElement(nodes[2],
new XElement(nodes[3],
new XElement(nodes[4]))));
root.Element(nodes[1]).Element(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Add(new XElement(nodes[5]));
child = root.Elements(nodes[1]).Elements(nodes[2]).Elements(nodes[3]).Elements(nodes[4]).Elements(nodes[5]).Last();
}
return child;
}
catch (Exception ex)
{
Tracer.LogError(
ex.Message,
"Settings",
(new System.Diagnostics.StackFrame()).GetMethod().Name) ;
throw;
}
}
برای ویرایش هم مشکل رو با یک Extension method بصورت زیر حل کردم:
/// <summary>
/// Replace the contents of a node in an XElement hierarchy
/// when the element name and all the attribute names and
/// values match an input element.
/// (If there is no match, the new element can be added.)
/// </summary>
/// <param name="source">
/// Source root XElement to replace/add
/// </param>
/// <param name="node">
/// XElemenet to replacement or add
/// </param>
private static void ReplaceOrAdd(this XElement source, XElement node)
{
var q = from element in source.Elements()
where element.Name == node.Name
&& element.Attributes().All
(a => node.Attributes().Any
(b => a.Name == b.Name && a.Value == b.Value))
select element;
var n = q.LastOrDefault();
if (n == null) source.Add(node);
else n.ReplaceWith(node);
}
با این تفاسیر، تابع افزودن کاربر هم به اینصورت شد:
/// <summary>
/// Add a new authentication entry as username & password if not exist,
/// otherwise replace md5 password.
/// </summary>
/// <param name="username">
/// String value represent username
/// </param>
/// <param name="md5Password">
/// Hashed with MD5 provider password to save and chack later
/// </param>
/// <param name="file">
/// Setting file path, if leave as empty default is "Configs.config"
/// </param>
/// <param name="city">
/// City name that specified User belong it
/// </param>
public static void AddUserInfo(string username,
string pass,
string file = "",
string city = "")
{
try
{
file = (file == String.Empty) ? XmlConfigPath : file;
XElement root = new XElement("Initial"),
child = GetSpecifiedNode(file, ref root, "Users");
// Add user authentication info to specified node
city = (city == String.Empty) ? "Deaulet" : city;
child.ReplaceOrAdd(new XElement("XMLRPCUser", username,
new XAttribute("encrypt", Security.GetMd5Hash(pass))));
// Save change(s) to file
root.Save(file);
}
catch (Exception ex)
{
Tracer.LogError(
ex.Message,
"Settings",
(new System.Diagnostics.StackFrame()).GetMethod().Name) ;
throw;
}
}
جمع بندی:
بیزحمت یه حجم کد نگاه نکنید، من چون راهی نداشتم اینهمه خزعبلات سرهم بندی کردم! ولی ممکنه مثلاً تمام کد GetSpecifiedNode با یه خط کد حل بشه، من اون رو میخوام اگه اطلاع داشته باشید.
تشکر و قدردانی:
از تمام کسانی که این پست رو میخونن، دلداری میدن، از خدا برام شفا میخوان، راه حلی به ذهنشون میرسه، فحش نمیدن و یا خدای نکرده جایگزینی براش دارن پیشاپیش کمال تقدیر و تشکر بعمل میاد.
والسلام، نامه تمام.