Is it possible to get populate class object from xml? - asp.net

I have an xml file like this:
<CommissionTypes>
<Type>CPA</Type>
<Lender>
<Seq>001</Seq>
<PostUrl>http://www.mysite.com</PostUrl>
</Lender>
</CommissionTypes>
</Lenders>
Having got the data like this:
var config = XDocument.Load(xml);
I need to map it to a class collection, the class is structured like this:
public class Lender
{
public string Type { get; set; }
public int Seq { get; set; }
public string PostUrl { get; set; }
public void Lender(string type, int seq, string postUrl)
{
Type = type;
Seq = seq;
PostUrl = postUrl;
}
}
I've been trying to do this for some time using linq, but without success as yet. What i want to do is retrieve all lenders with in the type "CPA" or any other type.
Any advice?
// * UPDATE * //
The update below is where I'm currently at. Its not working, getting a 'object reference not set to an instance' error wgere arrow is.
<CommissionTypes>
<Type type="CPA">
<Lender>
<Seq>001</Seq>
<PostUrl>http://www.mysite.com</PostUrl>
</Lender>
</Type>
</CommissionTypes>
public static List<Lender> GetLenders(string xml)
{
var myXml = XDocument.Load(xml);
var lenders = new List<Lender>();
lenders = (from type in myXml.Descendants("Type")
where type.Attribute("type").Value == "CPA"
===> select new Lender(
"CPA",
type.Element("Seq").Value.ConvertTo<int>(),
type.Element("PostUrl").Value)).ToList();
return lenders;
}

Your query is incorrect, because Seq is not a direct child of Lender element.
public static List<Lender> GetLenders(string xml)
{
var myXml = XDocument.Load(xml);
var lenders = new List<Lender>();
lenders = (from type in myXml.Descendants("Type")
where type.Attribute("type").Value == "CPA"
from lender in type.Elements("Lender")
select new Lender(
"CPA",
(int)lender.Element("Seq"),
(string)lender.Element("PostUrl"))).ToList();
return lenders;
}

Related

Dynamically paging EF Core results

I have a UI grid which permits sorting by column :
Id
Organisation Name
Organisation type
Departments
1
first
some type
def
2
second
another type
abc, def
2
third
some type
xyz
See the entities below:
public class Organisation
{
public int Code { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public List<Department> Departments { get; set; }
}
public class Department
{
public int Code { get; set; }
public string Name { get; set; }
}
I want to be able to sort the table values by Departments which is a comma separated values that comes from Organization.Departments.Select(p=> p.Name);
I would like to make the sorting as an IQueryable and avoid bringing all the data in memory because After sorting I will apply the pagination and I don't want to bring all the DB records in memory.
I'm using the following extension method for sorting, but it is not working for nested collections:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
{
var type = typeof(T);
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (property == null)
throw new OperationFailedException($"Sorting by {sortProperty}");
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new Type[] { type, property.PropertyType };
var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
This method works fine for properties that are at object level.
IQueryable I'm using later for sorting looks something like this:
var iQueryableToBeSorted = _dbContext.Organization.Include(p=>p.Departments).AsQueryable();

DocumentDb CreateDocumentQuery<T> returns items not of type T

I'm trying to get a list of documents from documentdb of specific object type -
_client.CreateDocumentQuery<RuleSetGroup>(_collectionLink)
.Where(f => f.SourceSystemId == sourceSystemId).AsEnumerable().ToList();
This returns objects of types other than RuleSetGroup, as long as they have a property SourceSystemId matching what I pass in. I understand this is how documentdb works, is there a way to enforce the type T so only those objects are returned?
I am using Auto Type Handling:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Auto
};
You will get different document types unless you implement a Type Pattern (adding a Type attribute to each Class) and use it as extra filter.
The reason is because you are storing NoSQL documents, which can obviously have different schema. DocumentDB treats them all equally, they are all documents; when you query, it's your responsability (because only you know the difference) to separate the different document types.
If you document Types all have an attribute "Client" (for example, Orders and Invoices) and you create a query with that attribute but mapped to one Type (Orders), you will get both Orders and Invoices that match the filter because they are documents that match the query. The deserialization logic is on your end, not within DocDB.
Here is an article regarding that Type Pattern when storing different document Types on DocDB (check the Base Type Pattern section).
Something like this might solve it:
public abstract class Entity
{
public Entity(string type)
{
this.Type = type;
}
/// <summary>
/// Object unique identifier
/// </summary>
[Key]
[JsonProperty("id")]
public string Id { get; set; }
/// <summary>
/// Object type
/// </summary>
public string Type { get; private set; }
}
public class RuleSetGroup : Entity
{
public RuleSetGroup():base("rulesetgroup")
}
public class OtherType : Entity
{
public OtherType():base("othertype")
}
_client.CreateDocumentQuery<RuleSetGroup>(_collectionLink).Where(f => f.Type == "rulesetgroup" && f.SourceSystemId == sourceSystemId).AsEnumerable().ToList();
You can wrap queries on helpers that set the type as a Where clause before applying your other filters (in LINQ you can chain Wheres without problem).
My repository might be a little too much for you, the short answer is that you can return .AsDocumentQuery() instead of .ToList()
public async Task<IEnumerable<T>> GetDocumentsAsync<T>(Expression<Func<T, bool>> predicate, int maxReturnedDocuments = -1,
bool enableCrossPartitionQuery = true, int maxDegreeOfParallellism = -1, int maxBufferedItemCount = -1)
{
//MaxDegreeofParallelism default = 0, add -1 to let SDK handle it instead of a fixed 1 network connection
var feedOptions = new FeedOptions
{
MaxItemCount = maxReturnedDocuments,
EnableCrossPartitionQuery = enableCrossPartitionQuery,
MaxDegreeOfParallelism = maxDegreeOfParallellism,
MaxBufferedItemCount = maxBufferedItemCount
};
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(_databaseName, _collectionName), feedOptions)
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
var res = await query.ExecuteNextAsync<T>();
results.AddRange(res);
}
return results;
}
You can call the above method like this:
var ecsterConfigs = await repoBO.GetDocumentsAsync<EcsterPaymentConfig>(c => c.ValidTo == null && c.Type == type);
And then I have a wrapper around it sometimes when I "might" do an update of document, to keep track of the _Etag which will change if there is another update on the document before I write it down again.
public class DocumentWrapper<DocumentType>
{
public DocumentWrapper(Document document)
{
Value = (DocumentType)(dynamic)document;
ETag = document.ETag;
TimeStamp = document.Timestamp;
}
public DocumentType Value { get; set; }
public string ETag { get; set; }
public DateTime TimeStamp { get; set; }
}
#Granlund how do you make GetDocumentsAsync return DocumentWrapper instances while still allowing the predicate to query on the properties of the Value?
Here is what I came up with but maybe you have a better way:
[TestMethod]
[TestCategory("CosmosDB.IntegrationTest")]
public async Task AddAndReadDocumentWrapperViaQueryAsync()
{
var document = new Foo { Count = 1, Name = "David" };
var response = await client.CreateDocumentAsync(documentCollectionUri, document);
var id = response.Resource.Id;
var queryResult = await GetWrappedDocumentsAsync<Foo>(f => f.Where(a => a.Name == "David"));
foreach (var doc in queryResult)
{
Assert.AreEqual("David", doc.Value.Name);
}
}
public class Foo
{
public int Count { get; set; }
public string Name { get; set; }
}
public class DocumentWrapper<DocumentType>
{
public DocumentWrapper(Document document)
{
Value = (DocumentType)(dynamic)document;
ETag = document.ETag;
TimeStamp = document.Timestamp;
}
public DocumentType Value { get; set; }
public string ETag { get; set; }
public DateTime TimeStamp { get; set; }
}
public async Task<IEnumerable<DocumentWrapper<T>>> GetWrappedDocumentsAsync<T>(
Func<IQueryable<T>, IQueryable<T>> query,
int maxReturnedDocuments = -1,
bool enableCrossPartitionQuery = true,
int maxDegreeOfParallellism = -1,
int maxBufferedItemCount = -1)
{
//MaxDegreeofParallelism default = 0, add -1 to let SDK handle it instead of a fixed 1 network connection
var feedOptions = new FeedOptions
{
MaxItemCount = maxReturnedDocuments,
EnableCrossPartitionQuery = enableCrossPartitionQuery,
MaxDegreeOfParallelism = maxDegreeOfParallellism,
MaxBufferedItemCount = maxBufferedItemCount
};
IDocumentQuery<T> documentQuery =
query(client.CreateDocumentQuery<T>(documentCollectionUri, feedOptions)).AsDocumentQuery();
var results = new List<DocumentWrapper<T>>();
while (documentQuery.HasMoreResults)
{
var res = await documentQuery.ExecuteNextAsync<Document>();
results.AddRange(res.Select(d => new DocumentWrapper<T>(d)));
}
return results;
}

handle this object datasource on reporting services

If I had a class defined with this attributes
public class GestionesDataSet
{
public DateTime GestionInicio { get; set; }
public DateTime GestionFin { get; set; }
public Nullable<DateTime> LlamadaInicio { get; set; }
public Nullable<DateTime> LlamadaFin { get; set; }
public string Login { get; set; }
public string Tipificacion { get; set; }
public List<CamposGestion> campoValor { get; set; }
}
And the class called CamposGestion is defined like this
public class CamposGestion
{
public string Nombre { get; set; }
public string Valor { get; set; }
}
How can I Defined a report where I can use the field that refers to the list of the other elements?
I tried to used one dataset where I can set this linq as object data source
var gestiones = (from G in db.Gestion
where
G.IDTipificacion == idTipificacion
&& (from T in db.Tipificacion where T.IdTipificacion == G.IDTipificacion select T.Servicio.IDServicio).AsEnumerable().Contains(idServicio)
select G).AsEnumerable().Select(xx => new GestionesDataSet()
{
GestionInicio = xx.HoraInicio,
GestionFin = xx.HoraFin,
#Tipificacion = ((from T in db.Tipificacion select T).Where(x => x.IdTipificacion == xx.IDTipificacion).Count() > 0 ?
(from T in db.Tipificacion where T.IdTipificacion == xx.IDTipificacion select T.Nombre).FirstOrDefault() : ""),
LlamadaInicio = xx.Llamada.HoraInicio,
LlamadaFin = xx.Llamada.HoraFin,
Login = xx.Llamada.Sesion.Usuario.Nombre,
campoValor = xx.CampoValor.Select(aux => new CamposGestion() {
Nombre = aux.ConfiguracionCampo.Campo.Nombre,
Valor = aux.Valor
}).ToList()
}).ToList();
But what I want to see the report the field that contains the List show's an error like this
Any help would be appreciate.
I would rewrite the query like this:
var gestiones =
from xx in db.Gestion
where
xx.IDTipificacion == idTipificacion
&& (from T in db.Tipificacion
where T.IdTipificacion == xx.IDTipificacion select T.Servicio.IDServicio).AsEnumerable().Contains(idServicio)
select new GestionesDataSet()
{
GestionInicio = xx.HoraInicio,
GestionFin = xx.HoraFin,
#Tipificacion = (from T in db.Tipificacion where T.IdTipificacion == xx.IDTipificacion select T.Nombre).FirstOrDefault() ?? "",
LlamadaInicio = xx.Llamada.HoraInicio,
LlamadaFin = xx.Llamada.HoraFin,
Login = xx.Llamada.Sesion.Usuario.Nombre,
campoValor = xx.CampoValor.Select(aux => new CamposGestion()
{
Nombre = aux.ConfiguracionCampo.Campo.Nombre,
Valor = aux.Valor
}).ToList()
}).ToList();
When you call a projection (Select) after the AsEnumerable was called, LINQ will try to get the navigation objects first from the already loaded ones. If no object is loaded, then will execute a select SQL command for each navigation property used in the projection. If the [DeferredLoadingEnabled][1] property is set to false it won't execute any query and if no object is loaded already (they can be loaded "apriori" with [LoadWith][2]) it will give a NullReferenceException. So, in some situations, calling AsEnumerable might hurt performance. All these things are not valid when AsEnumerable is used in the where parts.
For giving a default value, when no Tipificacion doesn't exist, it can be used the null-coalescing operator, from C#, instead of using the Count method, which creates an extra lookup on the the table.
Now.. to your problem.
SSRS doesn't support binding to a list of items. The column campoValor tries to bind to a list of objects, which is not allowed. So either you create a subreport (there is a section which describes this) or you flatten your data (having the all the properties on one single object) and then use the HideDuplicates property

How to consume a RSS feed and read the dc:creator element

I am trying to get this to work
I am using this code and it works fine by itself, but now I am trying to get the following element to be read: dc:creator and are not able to.
class file:
public class RSSItem
{
XNamespace dc="http://purl.org/dc/elements/1.1/";
public string Title { get; set; }
public string Description { get; set; }
public string PubDate { get; set; }
public string Link { get; set; }
public string strGuid { get; set; }
public string Author { get; set; }
public string Dc_creator { get; set; }
// Next we’ll modify the constructor. First add a parameter list to the constructor:
public RSSItem(string title, string description, string link, string guid,
string pubDate, string (dc:creator) //<-- not working but what to use instead)
{
// This constructor will be used to parse the RSS xml. Add the following code to the constructor:
Title = title;
Description = description;
Link = link;
strGuid = guid;
PubDate = pubDate;
Dc_creator = dc:creator; <-- not working but what to use instead
Obviously this does not work:
string (dc:creator)) in the constructor
page.cshtml:
<div>
#{
XNamespace dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
XDocument rss = XDocument.Load("http://rss.xml");
var items = from elem in rss.Elements("rss").Elements("channel").Elements("item")
select elem;
foreach (var item in items)
{
RSSItem rssItem = new RSSItem(
item.Element("title").Value,
item.Element("link").Value,
item.Element("guid").Value,
item.Element("description").Value,
//item.Element("author").Value,
item.Element("pubDate").Value,
item.Element(dc + "creator").Value
);
<span class="rssStyle">
<div>
<b>#rssItem.Title</b>
</div>
etc etc
So how do I manage to have the class constructor read the dc:creator element?
Not the exact solution, but I had done this using XmlDocument. You will have to modify this according to your RssItem, I had similar object
XmlDocument doc = new XmlDocument();
doc.LoadXml(rss);
//Get Channel Node
XmlNode channelNode = doc.SelectSingleNode("rss/channel");
if (channelNode != null) {
//Add NameSpace
XmlNamespaceManager nameSpace = new XmlNamespaceManager(doc.NameTable);
nameSpace.AddNamespace("content", "http://purl.org/rss/1.0/modules/content/");
nameSpace.AddNamespace("slash", "http://purl.org/rss/1.0/modules/slash/");
nameSpace.AddNamespace("dc", "http://purl.org/dc/elements/1.1/");
//Parse each item
foreach (XmlNode itemNode in channelNode.SelectNodes("item")) {
RssFeed rssItem = new RssFeed();
rssItem.Guid = itemNode.SelectSingleNode("guid").InnerText;
rssItem.Title = itemNode.SelectSingleNode("title").InnerText;
rssItem.CreatedBy = itemNode.SelectSingleNode("dc:creator", nameSpace).InnerText;
rssItem.Url = itemNode.SelectSingleNode("link").InnerText;
rssItem.PubDate = DateTime.Parse(itemNode.SelectSingleNode("pubDate").InnerText);
rssItem.CommentCount = itemNode.SelectSingleNode("slash:comments", nameSpace).InnerText;
rssItem.Description = itemNode.SelectSingleNode("content:encoded", nameSpace).InnerText;
}
}

Reflection C# question

I am having problem getting values of a class whose one property is another class.
Here is an example:
public class Person
{
private int age;
private string name;
public Person()
{
Address = new Address();
}
public int Age
{
get { return age; }
set { age = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public Address Address { get; set; }
}
public class Address
{
public string street { get; set; }
public string houseno { get; set; }
}
public class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Age = 27;
person.Name = "Fernando Vezzali";
person.Address.houseno = "123";
person.Address.street = "albert street";
Type type = typeof(Person);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(person, null));
}
}
}
But with this I dont get values of address.
Can someone help here?
Here is the possible ToString, taking into account the Jason's answer...
You can also cast your returned reflected objet into an Address to access the full object and properties
public class Address
{
public string street { get; set; }
public string houseno { get; set; }
public override ToString() {
return string.Format("street: {0}, house: {1}", street, houseno);
}
}
type.GetProperties() only gets the properties for that type, one of which is an object Address. street and houseno are not properties on Person.
Console.Write... implicitly calls ToString() on each parameter. So you probably see "Address - Namespace.Address" as an output, because someAddressObject.ToString() will return the type name.
The easiest way to get what you want for this specific situation is to override ToString() on your Address object to output some meaningful string representation of the object:
public override ToString()
{
return string.Format("#{0} {1}",
this.houseno,
this.street); //returns #123 StreetName
}
If you actually need to write every property of every sub-object on your object, that can get fairly complex - you're essentially talking about serialization, which recurses down an object tree and into each object.
Either you need to implement ToString() in Address, if you're happy with returning a formatted string as the value of Address, or your iteration code needs to inspect each property to determine whether that property's type also exposes properties, and enqueue it for further inspection.
Your foreach is iterating through all properties properly, and I beleive it is implicitely calling ToString on it to get the value, so override the ToString method of your Address class, and return the properties as a string.
Or, in the foreach, test to see if your property is a value type or a class type by getting the property type and checking IsValueType or IsClass. If IsValueType is false, then iterate through the properties of that properties' class type just as you did for the properties of Person.
Something like this (You may need to tweek to get this to compile, but it gives you the idea):
Person person = new Person();
person.Age = 27;
person.Name = "Fernando Vezzali";
person.Address.houseno = "123";
person.Address.street = "albert street";
Type type = person.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
//get the type of this property
Type tyProperty = property.PropertyType;
object oValue = property.GetValue(person, null));
//if the property is a value
if (tyProperty.IsValueType)
{
Console.WriteLine("{0} = {1}", property.Name, oValue);
}
else //else if property type is a class
{
oSubValue = property.GetValue(oValue, null));
//loop through the classes properties
PropertyInfo[] lstSubProperties = tyProperty.GetProperties();
foreach (PropertyInfo propSub in lstSubProperties)
{
Console.WriteLine("{0} = {1}", propSub .Name, oSubValue);
}
}
}

Resources