XML linq query lists first elements but not all - c#

I have this XML file that I parse into its elements and create a list of a custom object Module.
XDocument kobra = XDocument.Load(new StringReader(results.OuterXml));
XNamespace ns = "#RowsetSchema";
var kobraNodeList = from s in kobra.Descendants(ns + "row")
select new Module
{
id = s.Attribute("ows_ID").Value,
name = s.Attribute("ows_Title").Value,
sourceFile = s.Attribute("ows_Source_x0020_Message_x0020_File_").Value,
scope = Scope.KOBRA,
component = string.Empty
};
and here's my Module struct:
public struct Module
{
public string name;
public Scope scope;
public string component;
public int wordCound;
public string id;
public string sourceFile;
}
The code works fine, but things get weird when I try to convert the var kobraNodeList into a list of Modules, I get a System.NullReferenceException at the AddRange line:
this.moduleList = new List<Module>;
this.moduleList.AddRange(kobraNodeList);
When trying to debug, I notice that although kobraNodeList.Count() also returns System.NullReferenceException, a kobraNodeList.Any() returns true, and kobraNodeList.First() returns a perfectly valid and correct Module struct with the desired data.
The XML file is valid, and if I replace the linq query with this:
var kobraNodeList = from s in kobra.Descendants(ns + "row")
select s;
I get a valid list of XElement, which I can Count() ok.
Can someone explain me what's wrong? BTW, I'm using .NET 3.5.

That looks like one (or more) of kobra.Descendants has ows_ID, ows_Title or ows_Source_x0020_Message_x0020_File_ attribute missing.
Linq uses deferred execution, so it won't try to build the sequence until you ask for the items. When you call Any() or First(), it only needs the first item in the sequence to work, which tells me that the first item in kobra.Descendants does have all of the required nodes.
However, one of the items after the first is probably missing at least one of those attributes - so you end up asking for the Value of a NULL attribute.

Inside
select new Module
{
// properties...
}
You could be running into a NullReferenceException as you access .Value on elements that might not exist in the XML document. Your first object in the collection is likely fine, hence your results when using Any() or First(). Subsequent items could be missing elements/attributes you are trying to use.
Try this as a replacement instead of using .Value directly.
id = (string)s.Attribute("whatever") // etc.

One of your lines such as s.Attribute("ows_Source_x0020_Message_x0020_File_") will be returning null for one of the records so s.Attribute("ows_Source_x0020_Message_x0020_File_").Value would cause the null reference exception.

Related

Dynamically get a DbSet<T> by Entity class name

I'm trying to use System.Reflections to get a DbSet<T> dynamically from its name.
What I've got right now is:
The DbSet name
The DbSet's Type stored on a variable
The issue I'm facing comes out when trying to use the dbcontext.Set<T>() method, since (these are my tries so far):
When I try to assign to <T> my DbSet Type, it throws me the following compilation error:
"XXX is a variable but is used like a type"
If I try with using both the Extension methods that you will find below in my code (which I made in order to try to get an IQueryable<T>), it returns a IQueryable<object>, which unfortunately is not what I am looking for, since of course when I try to manipulate it with further Reflections, it lacks of all the properties that the original class has…
What am I doing wrong? How can I get a DbSet<T>?
My code is the following, but of course, let me know if you need more infos, clarifications or code snippets.
My Controller's Method:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//FIRST TRY
//throws error: "_type is a variable but is used like a type"
var tableSet = _context.Set<_type>();
//SECOND TRY
//returns me an IQueryable<object>, I need an IQueryable<MyType>
var tableSet2 = _context.Set(_type);
//THIRD TRY
//always returns me am IQueryable<object>, I need an IQueryable<MyType>
var calcInstance = Activator.CreateInstance(_type);
var _tableSet3 = _context.Set2(calcInstance);
//...
}
Class ContextSetExtension
public static class ContextSetExtension
{
public static IQueryable<object> Set(this DbContext _context, Type t)
{
var res= _context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null);
return (IQueryable<object>)res;
}
public static IQueryable<T>Set2<T>(this DbContext _context, T t)
{
var typo = t.GetType();
return (IQueryable<T>)_context.GetType().GetMethod("Set").MakeGenericMethod(typo).Invoke(_context, null);
}
}
EDIT Added TypeFinder's inner code.
In brief, this method does the same of Type.GetType, but searches Type on ALL the generated assemblies
public class TypeFinder
{
public TypeFinder()
{
}
public static Type FindType(string name)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
var result = (from elem in (from app in assemblies
select (from tip in app.GetTypes()
where tip.Name == name.Trim()
select tip).FirstOrDefault())
where elem != null
select elem).FirstOrDefault();
return result;
}
}
UPDATE as requested in the comments, here's the specific case:
In my DB i've got some tables which are really similar each other, so the idea was to create a dynamic table-update method which would be good for every table, just passing to this method the table name, the ID of the row to update and the JSON containing data to update.
So, in brief, I would perform some updates on the table given in input as DbSet type, updating the row with ID==id in input with the data contained inside the JSON, which will be parsed inside an object of type X(the same of dbset)/into a dictionary.
In pseudo-code:
public bool MyMethod (string t, int id, string jsonupdate)
{
string _tableName = t;
Type _type = TypeFinder.FindType(_tableName); //returns the correct type
//THIS DOESN'T WORKS, of course, since as said above:
//<<throws error: "_type is a variable but is used like a type">>
var tableSet = _context.Set<_type>();
//parsing the JSON
var newObj = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonupdate, _type);
//THIS OF COURSE DOESN'T WORKS TOO
//selecting the row to update:
var toUpdate = tableSet.Where(x => x.Id == id).FirstOrDefault();
if(toUpdate!=null)
{
var newProperties = newObj.GetType().GetProperties();
var toUpdateProperties = toUpdate.GetType().GetProperties();
foreach(var item in properties)
{
var temp = toUpdateProperties.Where(p => p.Name==item.Name)
{
//I write it really in briefand fast, without lots of checks.
//I think this is enough, I hope
temp.SetValue(toUpdate, item.GetValue());
}
}
_context.SaveChanges();
}
return false;
}
returns me an IQueryable<object>, I need an IQueryable<MyType>
Well, that will never work. Your IQueryable cannot be of type IQueryable<MyType>because that would mean the compiler would need to know what MyType is and that is not possible, because the whole point of this exercise is to decide that on runtime.
Maybe it's enough to know that those objects are in fact instances of MyType?
If not, I think you have painted yourself into a corner here and you are trying to figure out what paint to use to get out of there. Take a step back, it's probably not a technical problem. Why do you need to do this? Why do you have the conflicting needs of knowing the type at runtime only and knowing it at compile time?
You need to think about your requirements, not about the technical details.
I needed to dynamically load a single record from the database for each type in a list of known types, to print a test email when an admin is editing the template, so I did this:
List<object> args = new List<object>();
//...
//other stuff happens that isn't relevant to the OP, including adding a couple fixed items to args
//...
foreach (Type type in EmailSender.GetParameterTypes())
{
//skip anything already in the list
if (args.Any(a => a.GetType().IsAssignableFrom(type))) continue;
//dynamically get an item from the database for this type, safely assume that 1st column is the PK
string sql = dbContext.Set(type).Sql.Replace("SELECT", "SELECT TOP 1") + " ORDER BY 1 DESC";
var biff = dbContext.Set(type).SqlQuery(sql).AsNoTracking().ToListAsync().Result.First();
args.Add(biff);
}
Caveat: I know at least one record will exist for all entities I'm doing this for, and only one instance of each type may be passed to the email generator (which has a number of Debug.Asserts to test validity of implementation).
If you know the record ID you're looking for, rather than the entire table, you can use dbContext.Set(type).Find(). If you want the entire table of whatever type you've sussed out, you can just do this:
string sql = dbContext.Set(type).Sql; //append a WHERE clause here if needed/feasible, use reflection?
var biff = dbContext.Set(type).SqlQuery(sql).ToListAsync().Result;
Feels a little clunky, but it works. There is strangely no ToList without Async, but I can run synchronously here. In my case, it was essential to turn off Proxy Creation, but you look like you want to maintain a contextful state so you can write back to db. I'm doing a bunch of reflection later, so I don't really care about strong typing such a resulting collection (hence a List<object>). But once you have the collection (even just as object), you should be able to use System.Reflection as you are doing in your UPDATE sample code, since you know the type and can use SetValue with known/given property names in such a manner.
And I'm using .NET Framework, but hopefully this may translate over to .NET Core.
EDIT: tested and working:
public async Task<bool> MyMethod(string _type)
{
Type type = Type.GetType(_type);
var tableSet = _context.Set(type);
var list = await db.ToListAsync();
// do something
}
// pass the full namespace of class
var result = await MyMethod("Namespace.Models.MyClass")
IMPORTANT NOTE: your DbContext need to have the DbSet declared to work!
public class MyContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
}

Cannot access or find reference to System.Xml.Linq.LineInfoAnnotation. Why is this?

I have an application which takes an XML document and sorts it by certain attributes. I have information associated with each line of the XML document which I want to include in the sorted document. In order to do this,
When I load the file, I make sure the line info is loaded using XDocument.Load(file, LoadOptions.SetLineInfo).
Then I recursively iterate over each XElement and get its line info. When I ran the app, I noticed that each XElement has two annotations,
one of type System.Xml.Linq.LineInfoAnnotation
and one of type System.Xml.Linq.LineInfoEndElementAnnotation.
They contain the info that I need but in private fields.
I can't find any information on these classes, I can't instantiate them, they do not appear in the Object browser under System.Xml.Linq. Yet they exist and I can run "GetType()" on them and get information about the class.
If they exist, why are they not in MSDN references and why can't I instantiate them or extend them? Why can't I find them in the object browser?
P.S. My workaround for this was to use reflection to get the information contained inside each element. But I still can't pass a class name to tell the method what type it is, I have to isolate the object from XElement.Annotations(typeof(object)), and then run GetType() on it. I've illustrated this below.
public object GetInstanceField(Type type, object instance, string fieldName)
{
//reflective method that gets value of private field
}
XElement xEl = existingXElement; //existingXElement is passed in
var annotations = xEl.Annotations(typeof(object)); //contains two objects, start and end LineInfoAnnotation
var start = annotations.First();
var end = annotations.Last();
var startLineNumber = GetInstanceField(start.GetType(), start, lineNumber); //lineNumber is private field I'm trying to access.
var endLineNumber = GetInstanceField(end.GetType(), end, lineNumber);
This code works, but again, I can't just tell the method "typeof(LineInfoAnnotation)", instead I have to do GetType on the existing object. I cannot make sense of this.
Those classes are private - an implementation detail, if you will.
All XObjects (elements, attributes) implement the IXmlLineInfo interface - but they implement the inteface explicitly, so you must perform a cast to access the properties.
Once you have your IXmlLineInfo, you can use the properties LineNumber and LinePosition.
var data =
#"<example>
<someElement
someAttribute=""val"">
</someElement></example>
";
var doc = XDocument.Load(new MemoryStream(Encoding.UTF8.GetBytes(data)), LoadOptions.SetLineInfo);
foreach(var element in doc.Descendants()) {
var elLineInfo = element as IXmlLineInfo;
Console.Out.WriteLine(
$"Element '{element.Name}' at {elLineInfo.LineNumber}:{elLineInfo.LinePosition}");
foreach(var attr in element.Attributes()) {
var attrLineInfo = attr as IXmlLineInfo;
Console.Out.WriteLine(
$"Attribute '{attr.Name}' at {attrLineInfo.LineNumber}:{attrLineInfo.LinePosition}");
}
}
Output:
Element 'example' at 1:2
Element 'someElement' at 2:2
Attribute 'someAttribute' at 3:3
To get the EndElement information, you have to use a plain old XML reader, since the XObject api doesn't expose any information about where the element ends.
using(var reader = doc.CreateReader()) {
while(reader.Read()) {
var lineInfo = reader as IXmlLineInfo;
Console.Out.WriteLine($"{reader.NodeType} {reader.Name} at {lineInfo.LineNumber}:{lineInfo.LinePosition}");
if(reader.NodeType == XmlNodeType.Element && reader.HasAttributes) {
while(reader.MoveToNextAttribute()) {
Console.Out.WriteLine($"{reader.NodeType} {reader.Name} at {lineInfo.LineNumber}:{lineInfo.LinePosition}");
}
}
}
}
Output:
Element example at 1:2
Element someElement at 2:2
Attribute someAttribute at 3:3
EndElement someElement at 5:5
EndElement example at 5:19

Initialize nested Object from a list

I have a list of integers (Levels). I want to initialize a nested Object of Filter called myFilter as below(Filter is a class with two properties: Value and NextFilter):
var myFilter = new Fliter{
Value = Levels[0],
NextFilter = new Filter{
Value = Levels[1],
NextFilter = new Filter{
Value = Levels[2],
NextFilter = new Filter{
Value = Levels[n],
NextFilter = null
}
}
}
}
Level's count is not static and depends on the input list (I have a multi select list that generates Level)
How can I do that?
This is a classic event for using - the technique of a method that calls itself:
public static Filter CreateFilter(List<int> values) => values.Any() ? new Filter //If the list contains elements, create the filter
{
Value = values.First(), //assign the first item of the values to the value property
NextFilter = CreateFilter(values.Skip(1).ToList()) //Create the rest of the nested object with the rest of the values
} : null; //If there aren't any items left in the list, return null and stop the recursion
You could of course do it in the constructor as well:
public Filter(List<int> values)
{
if (!values.Any()) return;
Value = values.First();
NextFilter = values.Count > 1 ? new Filter(values.Skip(1).ToList()) : null;
}
For more information about recursion, take a look at this: https://www.dotnetperls.com/recursion, for more information on nested classes read through this: https://www.dotnetperls.com/nested-class.
A few more information on recursion:
You can actually achieve everything through recursion - you don't even need loops. That's the reason why in languages like Haskell loops don't exist.
The simplest recursive function is:
public static void EndlessLoop()
{
//Loop body
EndlessLoop();
}
However, even Resharper suggests to convert it to a loop:
Another example, if you want to get the sum of a list you could do:
public static int Sum(List<int> summands) => summands.Count > 0
? summands.First() + Sum(summands.Skip(1).ToList())
: 0;
But those examples aren't useful in C#, as C# isn't a functional programming language, which causes recursion to be slower than loops. Furthermore recursion often causes a StackOverflowException (fitting to this site). If you run the endless loop recursion, it doesn't even take a second till your stack is full.
The reason for this is, that C# adds the address, from which a method got called, to the stack. If a method is called very often (and in 1 second a lot of recursive calls are made) a lot of addresses are added to the stack, so that it overflows.
However I still think, even though those examples aren't useful in c#, that it's quite useful to be able to handle recursion. Recursion is for example the only way to explore a directory structure, for getting for example all files:
public static List<FileInfo> GetAllFiles(DirectoryInfo directory) => directory.GetFiles()
.Concat(directory.GetDirectories().SelectMany(GetAllFiles))
.ToList();
And, as you experienced, it's the only way to fill a nested class from a list properly.
Just make a constructor of Filter, that will get Levels array as a parameter, that will set it's Value as level[0], and init NextFilter = new Filter(level.Skip(1)). Something like that. And it will recursively initialize your object.

Querying sub document Ravendb

int start = 0;
var current = _db.Query<Record>().Take(1024).Skip(start).ToList();
this works fine, a list is returned that contains a list of sub documents/pocos, as shown in the image below :
This shows the Keywords collection, that is a list of keyword pocos. However, I want to return all the keywords, so i tried doing this :
int start = 0;
var current = _db.Query<Keyword>().Take(1024).Skip(start).ToList();
However nothing gets returned ? All imports ok, everything compiling and running, just nothing listed...
Edit
When implementing a static index and using map/reduce the follwing segment of code
Map = record => from keyword in record.Keywords
// here visual studio doesn't allow subdocuments,
// only offers up System.Object methods after record ?
Screenshot :
You can only query on the root documents in RavenDB.
Now, you can query all the root documents that contain a particular value in a child object (not a sub document, there is no such thing), but you're still asking for the root document.
For example, a query would look like: Give me all the records that has the keyword "nice" in them:
session.Query<Record>().Where(x=>x.Keywords.Contains("Nice")).ToList();
And you can ask, from all the records, give me the keywords they have:
session.Query<Record>().Select(x=> x.Keywords).ToList();
But you are always going from the root doc.
If you want to query the keywords directly then you could write an index.
public class KeywordsIndex : AbstractIndexCreationTask<Record, KeywordsIndex.Result>
{
public KeywordsIndex()
{
Map = records => from record in records
from keyword in record.Keywords
select new
{
Keyword = keyword
}
}
public class Result
{
public Keyword Keyword { get; set; }
}
}
set it up:
documentStore.Initialize();
documentStore.ExecuteIndex(new KeywordsIndex());
then call it:
using (var session = documentStore.OpenSession())
{
session.Query<KeywordsIndex.Result, KeywordsIndex>().Select(r => r.Keyword).ToList();
}
This way you can actually query the keywords and sort or filter etc.
See more here on static indexes: http://ravendb.net/docs/2.0/client-api/querying/static-indexes/defining-static-index
You may be able to return Keyword directly from the index, not sure, but it would prob be nice for you if you could.

XML LINQ query returns nothing

I'm trying to parse an xml file using LINQ, but as I understand the query returns null. (It's WP7)
Here's the code:
var resultQuery = from q in XElement.Parse(result).Elements("Question")
select new Question
{
QuestionId = q.Attribute("id").Value,
Type = q.Element("Question").Attribute("type").Value,
Subject = q.Element("Subject").Value,
Content = q.Element("Content").Value,
Date = q.Element("Date").Value,
Timestamp = q.Element("Timestamp").Value,
Link = q.Element("Link").Value,
CategoryId = q.Element("Category").Attribute("id").Value,
UserId = q.Element("UserId").Value,
UserNick = q.Element("UserNick").Value,
UserPhotoURL = q.Element("UserPhotoURL").Value,
NumAnswers = q.Element("NumAnswers").Value,
NumComments = q.Element("NumComments").Value,
};
"result" is the xml string, just like this one.
http://i48.tinypic.com/1ex5s.jpg (couldn't post properly formatted text so here's a pic : P )
Error:
http://i48.tinypic.com/2uyk2ok.jpg
Sorry, if I haven't explained it properly and if this has already been asked (tried searching but didn't help).
You have run into an XML namespace problem. When you are just querying "Question", the string is translated into an XName with the default namespace. There are no elements in the default namespace in your XML, only elements in the urn:yahoo:answers namespace (see the top level element, where it says xmlns="urn:yahoo:answers").
You need to query the correct XML namespace, like this:
var ns = new XNameSpace("urn:yahoo:answers");
var resultQuery = from q in XElement.Parse(result).Elements(ns + "Question");
When picking out the individual properties, remember to add the namespace also.
XName is a class that represents an XML name, which might have a namespace defined by XNameSpace. These two classes has an implicit conversion operator implemented that allows you to implicitly convert from string to XName. This is the reason the calls work by just specifying a string name, but only when the elements are in the default namespace.
The implicitness of this makes it very easy easier to work with XML namespaces, but when one does not know the mechanism behind, it gets confusing very quickly. The XNameclass documentation has some excellent examples.
Two ways to fix it:
Add the root element was part since Elements only search one level - XElement.Parse(result).Root.Elements("Question")
Use the Descendants method since that will search the entire xml tree.

Categories