I perform a query on my XML file with Linq and when I parse and obtain data from the XML document, I need to go against a DB to populate 2 properties of my object. I can perform 2 calls as my snipet shows, but I would like to make just one call and obtain the result to populate the 2 properties
XDocument recentOrdersXDoc = GetResults(...);
var q = from c in recentOrdersXDoc.Descendants("prop")
let handle = c.Element("handle")
select new ReturnResult()
{
ClientTemplateID = (string)c.Element("TemplateID"),
Handle = resultref != null ? (string)resultref.Attribute("handle") : null,
ClientID = DataContext.GetClientID((string)c.Element("TemplateID")),
ClientName = DataContext.GetClientName((string)c.Element("TemplateID")),
};
To populate ClientID and ModifiedDate I need to make 2 calls. There is a table called Clients which has these 2 columns, ClientID and ClientName. Also can i access ClientTemplateID property when I need to pass it as a param in GetClientID and GetClientName, as in my code above I have to obbtain the result from the XDocument.
How about something like:
var q = from c in recentOrdersXDoc.Descendants("prop")
let handle = c.Element("handle")
let clientTemplateID = (string)c.Element("TemplateID")
let client = DataContext.Clients
.Where(x=>x.ClientTemplateID == clientTemplateID)
.Select(x=>new {x.ClientID, x.ClientName}).Single()
select new ReturnResult()
{
ClientTemplateID = clientTemplateID,
Handle = resultref != null ?
(string)resultref.Attribute("handle") : null,
ClientID = client.ClientID,
ClientName = client.ClientName
};
This still only reads the two columns you need (I had to make some assumptions on names, though).
Related
I need to get that single date which is underlined in red in the image, and store it into a variable, so I can create a conditional and use it as a param.
Through front end it comes dynamically thousands of fields and filters which this date is included, or it also can bring only the date. Therefore, DataFechamento will always come no matter how many filters are coming..
Any way, the thing is that for me to create the conditional that I need, only that date in DataFechamento matters. Therefore, I need to find out how I can pick that '31/08/2022' alone and store it inside a variable.
public virtual IQueryable<Object> MontaQueryRecuperar(RecuperarIdDTO dto)
{
QueryParams queryParams = new QueryParams()
{
includes = dto.includes,
limit = 1,
page = 1,
start = 0,
fields = dto.fields,
filter = "[{'property':'Id','operator':'equal','value':" + dto.Id + "}]"
};
var query = ExecutaConsulta(queryParams);
IQueryable<object> queryFinal = query;
var fields = queryParams.GetFields();
if (fields != null && fields.Count > 0)
queryFinal = (query.SelectDynamic<TEntidade>(fields) as IQueryable<object>);
return queryFinal;
}
just for analysis:
I will attempt to be as specific as possible. So we are using Sitefinity 8.1.5800, I have a couple dynamic content modules named ReleaseNotes and ReleaseNoteItems. ReleaseNotes has some fields but no reference to ReleaseNoteItems.
Release Note Items has fields and a related data field to ReleaseNotes.
So I can query all ReleaseNoteItems as dynamic content pretty quickly less than a second.
I then use these objects provided by sitefinity and map them to a C# object so I can use strong type. This mapping process is taking almost a minute and using over 600 queries for only 322 items (N+1).
In Short: I need to get all sitefinity objects and Map them to a usable c# object quicker than I currently am.
The method for fetching the dynamic content items (takes milliseconds):
private IList<DynamicContent> GetAllLiveReleaseNoteItemsByReleaseNoteParentId(Guid releaseNoteParentId)
{
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(String.Empty);
Type releasenoteitemType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ReleaseNoteItems.Releasenoteitem");
string releaseNoteParentTypeString = "Telerik.Sitefinity.DynamicTypes.Model.ReleaseNotes.Releasenote";
var provider = dynamicModuleManager.Provider as OpenAccessDynamicModuleProvider;
int? totalCount = 0;
var cultureName = "en";
Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
Type releasenoteType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ReleaseNotes.Releasenote");
// This is how we get the releasenote items through filtering
DynamicContent myCurrentItem = dynamicModuleManager.GetDataItem(releasenoteType, releaseNoteParentId);
var myMasterParent =
dynamicModuleManager.Lifecycle.GetMaster(myCurrentItem) as DynamicContent;
var relatingItems = provider.GetRelatedItems(
releaseNoteParentTypeString,
"OpenAccessProvider",
myMasterParent.Id,
string.Empty,
releasenoteitemType,
ContentLifecycleStatus.Live,
string.Empty,
string.Empty,
null,
null,
ref totalCount,
RelationDirection.Parent).OfType<DynamicContent>();
IList<DynamicContent> allReleaseNoteItems = relatingItems.ToList();
return allReleaseNoteItems;
}
This is the method that takes almost a minute that is mapping sitefinity object to C# object:
public IList<ReleaseNoteItemModel> GetReleaseNoteItemsByReleaseNoteParent(ReleaseNoteModel releaseNoteItemParent)
{
return GetAllLiveReleaseNoteItemsByReleaseNoteParentId(releaseNoteItemParent.Id).Select(rn => new ReleaseNoteItemModel
{
Id = rn.Id,
Added = rn.GetValue("Added") is bool ? (bool)rn.GetValue("Added") : false,
BugId = rn.GetValue<string>("bug_id"),
BugStatus = rn.GetValue<Lstring>("bugStatus"),
Category = rn.GetValue<Lstring>("category"),
Component = rn.GetValue<Lstring>("component"),
#Content = rn.GetValue<Lstring>("content"),
Criticality = rn.GetValue<Lstring>("criticality"),
Customer = rn.GetValue<string>("customer"),
Date = rn.GetValue<DateTime?>("date"),
Grouped = rn.GetValue<string>("grouped"),
Override = rn.GetValue<string>("override"),
Patch_Num = rn.GetValue<string>("patch_num"),
PublishedDate = rn.PublicationDate,
Risk = rn.GetValue<Lstring>("risk"),
Title = rn.GetValue<string>("Title"),
Summary = rn.GetValue<Lstring>("summary"),
Prod_Name = rn.GetValue<Lstring>("prod_name"),
ReleaseNoteParent = releaseNoteItemParent,
McProductId = GetMcProductId(rn.GetRelatedItems("McProducts").Cast<DynamicContent>()),
}).ToList();
}
Is there any way to optimize this all into one query or a better way of doing this? Taking almost a minute to map this objects is too long for what we need to do with them.
If there is no way we will have to cache the items or make a SQL query. I would rather not do caching or SQL query if I do not have to.
Thank you in advance for any and all help you can provide, I am new to posting questions on stackoverflow so if you need any additional data please let me know.
Is there a reason why you are doing a .ToList() for the items? Is it possible for you to avoid that. In my opinion, most of the time(of the 1 minute) is taken to convert all your items into a list. Conversion from Sitefinity object to C# object is not the culprit here.
Look Arno's answer here: https://plus.google.com/u/0/112295105425490148444/posts/QrsVtxj1sCB?cfem=1
You can use the "Content links manager" to query dynamic modules relationships (both by parent -ParentItemId- or by child -ChildItemId-) much faster:
var providerName = String.Empty;
var parentTitle = "Parent";
var relatedTitle = "RelatedItem3";
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName);
Type parentType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.ParentModules.ParentModule");
Type relatedType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.RelatedModules.RelatedModule");
ContentLinksManager contentLinksManager = ContentLinksManager.GetManager();
// get the live version of all parent items
var parentItems = dynamicModuleManager.GetDataItems(parentType).Where(i => i.GetValue<string>("Title").Contains(parentTitle) && i.Status == ContentLifecycleStatus.Live && i.Visible);
// get the ids of the related items.
// We use the OriginalContentId property since we work with the live vesrions of the dynamic modules
var parentItemIds = parentItems.Select(i => i.OriginalContentId).ToList();
// get the live versions of all the schedules items
var relatedItems = dynamicModuleManager.GetDataItems(relatedType).Where(i => i.Status == ContentLifecycleStatus.Live && i.Visible && i.GetValue<string>("Title").Contains(relatedTitle));
// get the content links
var contentLinks = contentLinksManager.GetContentLinks().Where(cl => cl.ParentItemType == parentType.FullName && cl.ComponentPropertyName == "RelatedField" && parentItemIds.Contains(cl.ParentItemId) && cl.AvailableForLive);
// get the IDs of the desired parent items
var filteredParentItemIds = contentLinks.Join<ContentLink, DynamicContent, Guid, Guid>(relatedItems, (cl) => cl.ChildItemId, (i) => i.OriginalContentId, (cl, i) => cl.ParentItemId).Distinct();
// get the desired parent items by the filtered IDs
var filteredParentItems = parentItems.Where(i => filteredParentItemIds.Contains(i.OriginalContentId)).ToList();
I would imagine that every release note item under a single release note would be related to the same product wouldn't it?
If so, do you need to do the GetMcProductId method for every item?
var FileProducts = from ProductsRow in ProductRangesDt.AsEnumerable()
join Filee in FileTb.AsEnumerable() on ProductsRow["GEN_CODE"].ToString() equals Filee["GEN_CODE"].ToString()
select new
{
PRODUCT_ID = ProductsRow["PRODUCT_ID"],
PRODUCT_NAME = ProductsRow["PRODUCT_NAME"],
PROVIDER_ID = ProductsRow["PROVIDER_ID"],
PROVIDER_NAME = ProductsRow["PROVIDER_NAME"],
GEN_CODE = ProductsRow["GEN_CODE"],
MIN_QUANTITY = Filee["MIN_QUANTITY"],
MAX_QUANTITY = Filee["MAX_QUANTITY"],
DISCOUNT_VALUE = Filee["DISCOUNT_VALUE"]
};
var s = (from b in FileProducts
select b.PRODUCT_ID).Distinct(); // count=285
var Products = (from ProductsRow in ProductRangesDt.AsEnumerable()
select ProductsRow["PRODUCT_ID"]).Distinct(); // count=7159
var result = Products.Except(s); // it's count should be 7159-285
I want to get all the products ID that are in Products and don't exist in FileProducts how can i do this ? result always return 0 as count
From the MSDN documentation about Except extension method:
This method is implemented by using deferred execution. The immediate
return value is an object that stores all the information that is
required to perform the action. The query represented by this method
is not executed until the object is enumerated either by calling its
GetEnumerator method directly or by using foreach in Visual C# or For
Each in Visual Basic.
So in order to get the real value form your Set differentiation, you need to enumerate your result either by a call to the Count()-Method (result.Count()) on using foreach (foreach (var r in result) { ... }).
I can't test with your data, but with test data at my disposition, the Except-extension did delivered the correct results.
So basically I have this field in XML that I want to update. It is parsed out through XML and I am not sure how to update this single record from this single instance.
var dataContext =
new RequestFormsDBDataContext(ConfigManager.Data.ConnectionString);
var userForm = (
from i in dataContext.RequestFormItems
join c in dataContext.RequestFormInstances on i.TypeGuid equals c.TypeGuid
where i.Id == FormID
select new {
i.Id,
XmlData = XElement.Parse(i.XML)
}
).ToList();
// Parsing out XML Data
var userFormParced = (
from i in userForm
select new FormItem {
FormId = i.Id,
DateTimeCompleted = i.XmlData.Element("DateTimeCompleted").Value
}
).FirstOrDefault();
RFDateTimeCompleted = userFormParced.DateTimeCompleted;
// Code that isnt working
userFormParced.DateTimeCompleted = "New Data";
dataContext.SubmitChanges();
This won't work because you aren't changing the instance that you retrieve from the database. You are creating a new object using the values from the original - twice - and then changing it. The LINQ-to-SQL context has no knowledge that you're changing database rows, just some unrelated XML that you've constructed.
Firstly, the value you retrieve from the database is being put into an XElement
XmlData = XElement.Parse(i.XML)
and then you retrieve the 'value' from a node and put it into another new object
select new FormItem
{
FormId = i.Id,
DateTimeCompleted = i.XmlData.Element("DateTimeCompleted").Value
}
It's lost any reference to the LINQ-to-SQL context by this stage. Instead you need to change it in place.
If you're using XML in your database, you should be using XML columns which map to XML properties in your LINQ-to-SQL objects. You're not, so we'll implement a workaround.
Try something like this.
// just get the item
var userForm = (
from i in dataContext.RequestFormItems
join c in dataContext.RequestFormInstances on i.TypeGuid equals c.TypeGuid
where i.Id == FormID
select i).FirstOrDefault();
// parse the XML
var xml = XElement.Parse(userForm.XML);
RFDateTimeCompleted = xml.Element("DateTimeCompleted").Value;
xml.Element("DateTimeCompleted").Value = "New Data";
// and finally, because you're again just changing XML
// unrelated to the context, update the original object
userForm.XML = xml.ToString();
context.SubmitChanges();
I am developing asp.net mobile application. I am using LINQ to XML to query XML file. I am using the following query to retrieve the name & value of the query dynamically as follows
var TotalManifolds = from MF in FieldRoot.Element("FIELD-DEFINITION").Element("MANIFOLDS").Elements("MANIFOLD")
join SLT in FieldRoot.Element("FIELD-DEFINITION").Element("SLOTS").Elements("SLOT")
on (string)MF.Attribute("MID") equals (string)SLT.Attribute("PARENT")
select new
{
SlotName = (string)SLT.Attribute("NAME").Value,
SlotValue = (string)SLT.Attribute("NAME").Value
};
In the following statement of above query I want to retrieve the name of the attribute dynamically without explicitly specifying the name of the attribute
SlotName = (string)SLT.Attribute("NAME").Value
Here I am explicitly specifying the name. I want to code which can dynamically retrieve the name of the attribute. I am new to Linq to xml. Can you please tell how this can be done programatically ? or can you provide me the link through which I can resolve the above issue ?
It seems you are looking for something like:
// ...
select new
{
SlotName = SLT.Attributes().First().Name,
SlotValue = SLT.Attributes().First().Value
};
If I understand you correctly, you could always pass a variable in to the LINQ query:
var string attrName = "NAME"; // specify whatever value you need ...
// wrap the query below in a function, if it will be reused...
var TotalManifolds = from MF in FieldRoot.Element("FIELD-DEFINITION").Element("MANIFOLDS").Elements("MANIFOLD")
join SLT in FieldRoot.Element("FIELD-DEFINITION").Element("SLOTS").Elements("SLOT")
on (string)MF.Attribute("MID") equals (string)SLT.Attribute("PARENT")
select new
{
SlotName = (string)SLT.Attribute(attrName).Value,
SlotValue = (string)SLT.Attribute(attrName).Value
};