Find values from Xml who have same Element Name - c#-3.0

I have following xml which same child element (Filed) and I want to get value from each Child element.
Sameple XML
<root xmlns="">
<books cat="F1" ISBN="01F187597" genre="Programming">
<Field name="Title" val="XML" />
<Field name="Publish Date" val="20010424" />
<Field name="Price" val="43.00" />
</books>
</root>
Code
XDocument xdoc = XDocument.Load("c:\\test6.xml");
var booksData = from book in xdoc.Descendants("root")
//I guess create this and do something with it
// let fieldElements = book.Descendants("Field")
select new Book
{
cat = book.Element("books").Attribute("cat").Value
,ISBN = book.Element("books").Attribute("ISBN").Value
,genre = book.Element("books").Attribute("genre").Value
,Price = "?"
,PublishDate="?"
,Title="?"
};
Book Class
public class Book
{
public string cat {get;set;}
public string ISBN {get;set;}
public string genre {get;set;}
public string Title {get;set;}
public string PublishDate {get;set;}
public string Price { get; set; }
}

This will give you what you're looking for. Of course, if there is a possibility of any of these attributes not always being present, you'd want to add some null value checks to make sure each attribute exists before calling it's Value property.
Once you have the <books> element inside your query, you can do sub queries to select the specific <Field> elements that you need.
var booksData = from book in xdoc.Descendants("books")
select new Book
{
cat = book.Attribute("cat").Value,
ISBN = book.Attribute("ISBN").Value,
genre = book.Attribute("genre").Value,
Price = (from childField in book.Elements()
where childField.Attribute("name").Value == "Price"
select childField.Attribute("val").Value).SingleOrDefault(),
PublishDate = (from childField in book.Elements()
where childField.Attribute("name").Value == "Publish Date"
select childField.Attribute("val").Value).SingleOrDefault(),
Title = (from childField in book.Elements()
where childField.Attribute("name").Value == "Title"
select childField.Attribute("val").Value).SingleOrDefault()
};
One note on the code you posted in the question. The method xdoc.Descendants("root") doesn't really do anything. from book in xdoc would do the same thing. I used Descendants("books") in my answer, which is what I think you meant to do anyway.

I agree with Dennis xdoc.Descendants("root") doesn't really do anything.
I modify code so it will check attrbiute exists or not also how you can use lambda expression.
var booksData = from book in xdoc.Descendants("books")
select new Book
{
cat = book.Attribute("cat").Value,
ISBN = book.Attribute("ISBN").Value,
genre = book.Attribute("genre").Value,
//Dennis Code
Price = (from childField in book.Elements()
where childField.Attribute("name").Value == "Price"
select childField.Attribute("val").Value).SingleOrDefault(),
//same as Dennis code but using lambda expression
PublishDate = book.Elements("Field").Where(p => p.Attribute("name").Value == "Publish Date")
.Select(p => p.Attribute("val").Value).SingleOrDefault(),
// Check if element exists or not
//if not exists it will insert empty string
Title = book.Elements("Field").Any( p => p.Attribute("name").Value == "Title")
? book.Elements("Field").Where(p=> p.Attribute("name").Value == "Title")
.Select(p => p.Attribute("val")== null
? string.Empty
: p.Attribute("val").Value
).Single()
: string.Empty };

string xml = #"<root xmlns=''> <books cat='F1' ISBN='01F187597' genre='Programming'> <Field name='Title' val='XML' /> <Field name='Publish Date' val='20010424' /> <Field name='Price' val='43.00' /> </books> </root>";
XDocument xdoc = XDocument.Parse(xml);
var booksData = from book in xdoc.Descendants("root")
//I guess create this and do something with it
// let fieldElements = book.Descendants("Field")
select new Book
{
cat = book.Element("books").Attribute("cat").Value
, ISBN = book.Element("books").Attribute("ISBN").Value
, genre = book.Element("books").Attribute("genre").Value
, Price = book.Elements("books").Elements("Field").Single(b => b.Attribute("name").Value == "Price").Attribute("val").Value
, PublishDate = book.Elements("books").Elements("Field").Single(b => b.Attribute("name").Value == "Publish Date").Attribute("val").Value
, Title = book.Elements("books").Elements("Field").Single(b => b.Attribute("name").Value == "Title").Attribute("val").Value
};
}
public class Book
{
public string cat { get; set; }
public string ISBN { get; set; }
public string genre { get; set; }
public string Title { get; set; }
public string PublishDate { get; set; }
public string Price { get; set; }
}
I think this is how I would do it.

Related

Entity Framework LinqKit dynamic where predicate over related data

The code is in Alpha version, don't have validations or error management ... It would be included later.
I have a very simple model with two related entities: Location and Country:
public class UNCountry
{
public int UNCountryId { get; set; }
[Required]
[MaxLength(2, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
[RegularExpression("[A-Z][A-Z]", ErrorMessage = "The field {0}, This code is formed by Two(2) capital letters")]
public string Code { get; set; }
[Required]
[MaxLength(255, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
public string Name { get; set; }
}
public class UNLocation
{
public int UNLocationId { get; set; }
[MaxLength(1, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
public string UNChangeType { get; set; }
[Required]
[MaxLength(5, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
[RegularExpression("[A-Z]{5}", ErrorMessage = "The field {0}, This code is formed by 5 capital letters")]
public string Code { get; set; }
[Required]
[MaxLength(255, ErrorMessage = "The field {0} only can contains a maximum of {1} characters lenght.")]
public string Name { get; set; }
public int UNCountryId { get; set; }
public UNCountry UNCountry { get; set; }
}
I have an app that list all the locations and the user can filter by any column, this is a generic Component View in Angular and I want to have all the Column Filters be Dynamic. The filters are passed in JsonFilters Parameter as an array
[{name:"nameOfField1", value:"valueOfField1"},...],
then I construct a predicate with LinqKit and PredicateBuilder, and use the predicate in the query .Where(predicate). It works perfectly.
Here is the initial code in the controller
var validFilter = new PaginationFilter(
pF.PageNumber, pF.PageSize, pF.JsonFilters
);
var p = validFilter.CreateDynamicSearch<UNLocation>();
//var b = PredicateBuilder.New<UNLocation>(true);
//b = b.And(c => c.UNCountry.Name.Contains("Col"));
var pagedData = await _context.UNLocations
.Include(c => c.UNCountry)
.Where(p)
//.Where(d => d.UNCountry.Name.Contains("Col"))
.OrderBy(x => x.UNLocationId)
.Skip((validFilter.PageNumber - 1) * validFilter.PageSize)
.Take(validFilter.PageSize)
.ToListAsync();
var totalRecords = await _context.UNLocations.CountAsync();
return Ok(new PagedResponse<List<UNLocation>>(pagedData, validFilter.PageNumber, validFilter.PageSize,totalRecords));
I pass the fieldname and the value in the filters parameter, and all works well, but when I have a filter over the name of the country (the field name is UNCountry.Name) the predicate fails using this name of field over UNLocations, it fails in the function that constructs the predicate, but if I use in the Where directly (The commented line //.Where(d => d.UNCountry.Name.Contains("Col")) in the previous code) It Works, I could be solve the problem like this, but it would not be dynamic for other Views with more related data fields in query.
Here is the predicate generation function:
public Expression<Func<T, bool>> CreateDynamicSearch<T>()
{
var predicate = PredicateBuilder.New<T>(true);
foreach (ReqFilter filter in this.Filters)
{
var columnFilter = PredicateBuilder.New<T>(false);
var param = Expression.Parameter(typeof(T), "a");
string[] filterParts = filter.Name.Split(".");
string filterName = filterParts[0];
if (filterParts.Length == 1)
{
var prop = Expression.Property(param, filterName);
var call = Expression.Call(prop, "Contains", new Type[0], Expression.Constant(filter.Value));
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
predicate = predicate.And(columnFilter);
}
else
{
var prop = Expression.Property(param, filter.Name);
var call = Expression.Call(prop, "Contains", new Type[0], Expression.Constant(filter.Value));
columnFilter = columnFilter.Or(Expression.Lambda<Func<T, bool>>(call, param));
predicate = predicate.And(columnFilter);
}
}
return predicate;
}
This code works well if i pass the filters with the normal fields of the UNLocation, like code or Name byExample [{code:"MIA"},name:"MI"], but the predicate constructor fails if i send the column name of the related UNCountry Name byExample [{UNCountry.Name:"United States"}], I repeat It works if I write the Where in Design Time code, but i need to have mor flexybility, and generate in predicate. It is possible or how can achieve this ? Thanks in advance, excuse my English.

How to compare the value between the one from database and the client .Net Core 2

We have this edit Razor pages (edit.cshtml) which is extended from the following page model and it's very basic only include the PopulateRolesDropDownList:
public class RoleNamePageModel : PageModel
{
public SelectList RoleNameSL { get; set; }
public void PopulateRolesDropDownList(ApplicationDbContext _context,
object selectedRole = null)
{
var rolesQuery = from d in _context.Roles
orderby d.Name // Sort by name.
select d;
RoleNameSL = new SelectList(rolesQuery,
"RoleId", "Name", selectedRole);
}
}
Also in this Edit page, we added:
<input type="hidden" asp-for="User.UserRoles.ElementAt(0).RoleId" name="User.Current.RoleId" />
We also do the [BindProperty] in the code behind
public ApplicationUser User { get; set; }
We need to find out whether there is a change on this model. What is the approach to do this?
ENVIRONMENT:
.NET Core 2.2
Razor Pages
UPDATE - 1:
On the PostAsync, we made another call to the database:
var userRoleToUpdate = await _context.UserRoles
.FirstOrDefaultAsync(m => m.UserId == id.ToString());
We just need to compare this value with the change on a drop-down list or not. We could not work how.
UPDATE - 2:
We did change as per recommend by #NevilleNazerane below:
public class AssignClubUserViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public Guid SelectedRoleID { get; set; }
}
[BindProperty]
public AssignClubUserViewModel AssignClubUser { get; set; }
and added OnGetAsync:
public async Task<IActionResult> OnGetAsync(Guid? id)
{
if (id == null)
return NotFound();
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.FirstOrDefaultAsync(m => m.Id == id.ToString());
AssignClubUser.FirstName = user.FirstName;
AssignClubUser.LastName = user.LastName;
AssignClubUser.UserName = user.UserName;
AssignClubUser.SelectedClubID =
user.ClubApplicationUsers.ElementAt(0).ClubID;
....
Is this right? I got the error: NullReferenceException: Arg_NullReferenceException on line AssignClubUser.FirstName = user.FirstName;
UPDATE - 3:
Fixed by creating a ModemView and then on the OnGetAsync() for query ensure to mapped with the ModelView:
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.Where(t => t.Id == id.ToString())
.Select(t => new AssignClubUserViewModel<ApplicationUser>
{
FirstName = t.FirstName,
LastName = t.LastName,
UserName = t.UserName,
SelectedClubID = t.ClubApplicationUsers.ElementAt(0).ClubID
}).SingleAsync();
Since you have a view model, I recommend you simplify your bindings and let your behind code handle the other functionalities. You can first make a SelectedRoleId property:
public int SelectedRoleId { get; set; }
In your view model, you can assign this property's default value to User.UserRoles.ElementAt(0).RoleId in either your constructor or your OnGet, based on how you need it set up. This way the drop down is bound to a simple property.
For binding dropdowns (HTML selects) .NET Core provides the asp-items tag helper.
<select asp-for="SelectedRoleId" asp-items="Model.RoleNameSL"></select>
In your OnPostAsync, you can use SelectedRoleId to access the selected value.

Entity Framework Core Select Outer Join

is there an easy way to include a nullable navigation inside a select expression for EF Core?
My model looks like this
public class RootVO
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Description { get; set; }
public int? RelationId { get; set; }
[ForeignKey(nameof(RelationId))]
public RelationVO Relation { get; set; }
}
public class RelationVO
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(200)]
public string Property1 { get; set; }
[Required]
[StringLength(200)]
public string Property2 { get; set; }
public ICollection<RootVO> RootRelations { get; set; }
}
When I load the data I just want to select certain kind of properties. Currently my expression looks like this:
Expression<Func<RootVO, RootVO>> selectExpr = m => new RootVO
{
Id = m.Id,
Description = m.Description,
Relation = m.Relation != null ? new RelationVO
{
Id = m.Relation.Id,
Property1 = m.Relation.Property1
} : null
};
var result = context.Roots.Select(selectExpr).ToList();
Is there an easier way to handle the relation select?
Edit
Maybe some background here will help:
I have a huge object with a lot of columns and relations, some with inner, some with outer joins. This query gets accessed by a datagrid on UI which can have dynamic columns depending on the user selection. To increase the performance I've written a class that will build the select expression dynamicly depending on the selected columns. For now it is working, but I'm having trouble when an outer join is null due to null-reference excepction.
The debug view on the expression could look like this:
.New IVA.Core.Data.Models.StockMovementLogVO(){
SequenceNo = $m.SequenceNo,
PostingPeriodId = $m.PostingPeriodId,
TransactionDate = $m.TransactionDate,
FinancialYear = $m.FinancialYear,
FinancialYearPeriod = $m.FinancialYearPeriod,
VoucherDate = $m.VoucherDate,
ItemQuantity = $m.ItemQuantity,
BuCode = $m.BuCode,
LocationStructure = .New IVA.Core.Data.Models.LocationStructureVO(){
Id = ($m.LocationStructure).Id,
Description = ($m.LocationStructure).Description
},
BookingType = .New IVA.Core.Data.Models.BookingTypeVO(){
Id = ($m.BookingType).Id,
Description = ($m.BookingType).Description
},
PartnerStockLocationType = .New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.PartnerStockLocationType).Id,
Description = ($m.PartnerStockLocationType).Description
},
StockLocationType = .New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.StockLocationType).Id,
Description = ($m.StockLocationType).Description
}
}
StockLocationType and PartnerStockLocationType are outer joins and if those are null the query fails to execute.
I've now changed my expression builder that it will take care of the outer joins by including a null reference check. The expression now looks like this:
.New IVA.Core.Data.Models.StockMovementLogVO(){
SequenceNo = $m.SequenceNo,
PostingPeriodId = $m.PostingPeriodId,
TransactionDate = $m.TransactionDate,
FinancialYear = $m.FinancialYear,
FinancialYearPeriod = $m.FinancialYearPeriod,
VoucherDate = $m.VoucherDate,
ItemQuantity = $m.ItemQuantity,
BuCode = $m.BuCode,
LocationStructure = .New IVA.Core.Data.Models.LocationStructureVO(){
Id = ($m.LocationStructure).Id,
Description = ($m.LocationStructure).Description
},
BookingType = .New IVA.Core.Data.Models.BookingTypeVO(){
Id = ($m.BookingType).Id,
Description = ($m.BookingType).Description
},
PartnerStockLocationType = .If ($m.PartnerStockLocationType != null) {
.New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.PartnerStockLocationType).Id,
Description = ($m.PartnerStockLocationType).Description
}
} .Else {
null
},
StockLocationType = .If ($m.StockLocationType != null) {
.New IVA.Core.Data.Models.StockLocationTypeVO(){
Id = ($m.StockLocationType).Id,
Description = ($m.StockLocationType).Description
}
} .Else {
null
}
}
Edit
If anyone is interessted how it looks, I've created a repository where I use the class.
https://github.com/NQuirmbach/DynamicQueryBuilder

Project into a (list of) concrete object instead of an (list of) anonymous object

The problem simplified is as follows: in Entity Framework i am doing a join involving 3 tables, and returning the joined result set, which involves (some) fields from the 3 tables.
var query = (
from t1 in dbCtx.TB_Entity1
from t2 in dbCtx.TB_Entity2
.Where(p => p.someCol == t1.someCol && t.IsActive == true)
.DefaultIfEmpty() //LEFT JOIN
from t3 in dbCtx.TB_Entity3
.Where(q => q.someCol == t2.someCol)
where t1.IsLatest == true
&& (t1.istatus == 2
|| t1.istatus == 3
)
select new {
t1.col100,
t1.col101,
t2.col200,
t2.col201,
t3.col300,
t3.col301
}).OrderByDescending(t1 => t1.ID);
var anonObjList = query.ToList();
Thus, at the end of the query I write a projection to select the fields i want.
Finally i run the query with .ToList() and get a list of Anonymous objects.
How do i modify the query to project into a List of MyConcreteClass
i.e. i want to be able to write something similar to
List<MyConcreteClass> myObjList = query.ToList();
You may assume my concrete class looks like
public class MyConcreteClass
{
public string Col100 { get; set; }
public string Col101 { get; set; }
public string Col200 { get; set; }
public string Col201 { get; set; }
public string Col300 { get; set; }
public string Col301 { get; set; }
}
You just use the object initializer syntax:
new MyConcreteClass
{
Col100 = t1.col100,
Col101 = t1.col101,
Col200 = t2.col200,
Col201 = t2.col201,
Col300 = t3.col300,
Col301 = t3.col301
}

How to call object from Controller to display in the View of ASP.net MVC

I have one action in my controller :
public ActionResult GiftVochure()
{
if (Request.QueryString["gc"] != "")
{
var obj = from b in context.GiftCards
join cus in context.Customers on b.CustomerID equals cus.ID
where b.ID == int.Parse(Request.QueryString["gc"])
select new
{
b.ID,
b.Date,
b.CardNo,
b.Price,
CustomerName = cus.FirstName + " " + cus.LastName
};
return View(obj.ToList());
}
return View();
}
And I want to take "obj" to loop and display in the GiftVochure Views, Does any one know, how to do this?
Thanks.
You should start by defining a model type to replace the anonymous type projected by the query.
public class CardInfo
{
int ID { get; set; }
DateTime Date { get; set; }
int CardNo { get; set; }
double Price { get; set; }
string CustomerName { get; set; }
}
Modifying your action method:
var obj = from b in context.GiftCards
join cus in context.Customers on b.CustomerID equals cus.ID
where b.ID == int.Parse(Request.QueryString["gc"])
select new CardInfo
{
ID = b.ID,
Date = b.Date,
CardNo = b.CardNo,
Price = b.Price,
CustomerName = cus.FirstName + " " + cus.LastName
};
return View(obj);
Then you should strongly type your GiftVochure view to this type's sequence.
#model IEnumerable<CardInfo>
In the end you can iterate the items in your view.
#foreach(CardInfo current in Model) {
//display info
}
You should probably move your markup for displaying a single object to a partial view. Then render it in your loop.