I am having an odd problem with data loading which I don't understand, and I am hoping someone can explain to me what is going on, and perhaps how to accomplish my task more directly.
I am building a website using the technologies listed in the subject of this question.
I have a set of objects - each object has several properties (Name, ID, etc.) and a collection (ICollection<>) of other objects. So just looking at the tree of objects and their collections, it looks like this:
Tab
-TabRows
--Sections
---SectionRow
----Article
(So each tab has one or more tabrows, each tabrow has one or more sections, and so on. Each sub-object has a link back the parent, so each sectionrow has a SectionID, each Section has a TabRowID, etc.)
OK, so given that structure, consider this code:
// GET api/Tab/5
public Tab GetTab(int id)
{
var tab = db.Tabs.FirstOrDefault(t => t.TabId == id);
var tabrows = db.TabRows.ToList();
var sections = db.Sections.ToList(); // This makes the tabRow.Sections populate
var sectionrows = db.SectionRows.ToList();
var articles = db.Articles.ToList();
return tab;
}
So here is what happens. When the first line (var tab =...) executes, I get a tab object, but the TabRows collection is empty. (It is not null because the constructor instantiates it).
When the second line (var tabrows =...) executes, tab.TabRows suddenly populates. tab.TabRows.Sections is empty.
When the third line executes, tab.TabRows.Sections suddenly populates.
And so on.
I am assuming this is some sort of "lazy loading" on behalf of Linq, or perhaps one of the other technologies. But I don't know them well enough to figure it out.
Is there a way to re-write this so that I can just call line 1 and basically have everything auto-populate without having to individually reference every single object in every single collection?
Lazy loading is enabled by default and eager loading disabled. Entity framework allows you to hint at eager loading using the include statements. Your statement will become something like this.
var tab = db.Tabs.FirstOrDefault(t => t.TabId == id).Include("TabRows");
or as Include(t => t.TabRows);
Take a look at this link for more information.
In your case you would need to handle nested includes as well. Which means you would be better off taking another Model (your class) structured as follows
Tabs -> Containing a List<TabRows> -> containing a List<Sections> etc.
You would then need to re-write the linq so it populates the entire Model including the nested entities using nested includes.
As a side note, too many of these inner joins might slow down your querying and so consider indexed views on your DB side if and when possible
Related
I have a UI macro that fetches a list of records and displays them into a table above some form fields. I'm not using an embedded list because I want the table to read-only and so far, haven't found a way to make an embedded list read.
In my macro I have :
<g2:evaluate var="jvar_records" object="true" >
var gr = new GlideRecord(Tables.PTC);
gr.query();
gr;
</g2:evaluate>
<j2:while test="$[jvar_records.next()]">
<tr class="$[jvar_class]">
<td>$[jvar_records.getValue('arrival_date')]</td>
<td>$[jvar_records.getValue('departure_date')]</td>
<td>$[jvar_records.getDisplayValue('certifier')]</td>
<td>$[jvar_records.getDispalayValue('trip.depart_reason_code')]</td>
</tr>
</j2:while>
The field certifier and trip are Reference fields, that I want to get the dispaly value of. But they keep coming back as empty in the macro. It works in a background-script just fine.
If I just get the value jvar_records.getValue('certifier') it correctly gives me the sys_id.
What am I missing?
Am relatively certain that, outside of the <g2: evaluate> tag, Jelly is constrained to client side API. Client side GlideRecord doesn't have a getDisplayValue function.
What I would do is have your g2:evaluate actually loop through the records and build an array of normal JavaScript objects with just the values you will need, then return that array of objects as opposed to returning the GlideRecord object with query results.
I am trying to limit entities that the user can select from on "Regarding" field on Appointment form and set some default views used by these lookup fields.
The big idea is that when user is entering data in the field itself, the results should be provided from default view for this lookup, e.g. "Contacts I follow", but when user enters "Lookup more records" pop-up the default view should be swapped to "All contacts".
This field can look up in many entities, e.g. Account, Contact, Billing, etc., and I want to limit it strictly to Accounts only.
So far I've found two ways of doing it:
1) Add filters to lookup fields, so that any records not fulfilling given criteria will not be shown. This is done using addPreSearch JS function from Xrm lib, like this:
var addEventHandler = function (defaultView) {
Xrm.Page.getControl("contactid").addPreSearch(function () {
addFilter("contactid", defaultView)
});
}
var addFilter = function (entityName, defaultView) {
var filterXml = "<filter type = 'and'>" +
"<condition attribute='contactid' operator='null'/>" +
"</filter>";
Xrm.Page.getControl("contactid").addCustomFilter(filterXml, "contact");
Xrm.Page.getControl("contactid").setDefaultView(defaultView);
}
(This is just sample code, not actually applied to the Regarding field)
2) Second option is to use undocumented solution by using setLookupTypes function on the field itself, like this:
var limitLookupEntities = function () {
Xrm.Page.getAttribute("contactid").setLookupTypes(["contact"]);
}
var changeDefaultViewForLookup = function(viewGuid){
Xrm.Page.getControl("contactid").setDefaultView(viewGuid);
}
The issue:
The problem is that as mentioned earlier, "Regarding" field can look up in many entities. Limiting them using the solution 1) is tedious, having to create as many filters as there are entities and applying them one by one to the field doesn't seem the right way to go. However, the way I implement this, the default view behaviour works as intended - the look-in value is only changed for the pop-up window.
Solution 2) is way easier to implement, and won't require any changes in the future if any new entities would be added to relationship with "Regarding" field, but the default view is applied to both "in-field" and "pop-up" default view.
The solution I'm looking for needs to do the following:
It should limit the search to specified entity only
It shouldn't require any maintaining after being implemented, e.g.
in case new entities are introduced to "Regarding" field.
It should be generic - I might want to be able to parametrize it and use it on other forms
It should apply the default view change only when user opens the pop-up search window
Is it even possible?
The CRM version I'm implementing this on is 8.2 on-prem.
I'm brand new to .NET 4.5, bootstrap, and LINQ and I've come across an issue I don't know how to handle elegantly. I was hoping somebody could confirm or correct my thought process, and if you're feeling particularly charitable, point me to a resource that could help me achieve my aim.
I'm developing a page on a web app that is going to link to multiple outside applications. The way I've decided to group the apps is by using a bootstrap Tab control, and making each tab a logical group. I've written the LINQ query that returns all of the information about the apps I need (Title, Description, Link, etc.), but now I need to group the results by another field (which we call ContainerID) and put the groups in their proper Repeater control inside the correct Bootstrap tab.
As of now I'm putting one Repeater inside of each tab, which feels a little clunky, but I can't think of a better way to do it. My idea was to do the following:
1) do a foreach loop on the objects returned by the LINQ query
2) inside the foreach loop, write an if statement like "if ContainerID=" then add the result to a list or array and use that as the datasource for the corresponding Repeater and bind it
3) rinse and repeat for however many categories I need.
As I started down this path it looks to me like there's no such thing as a foreach with LINQ, so I'm dead at step 1. Any thoughts on the best way to accomplish my aim? Thanks in advance...
EDIT: I should mention I'm not tied to any of this. If there's a better UI design to accomplish my purpose, I'm all for it. I'm just looking for the cleanest way to implement it.
The results of a linq query is just the query itself. There are no results until you iterate over the result. You should then be able to iterate over the results like this:
var items = Your LINQ Query
foreach(var item in items)
{
if(ContainerID == item.id)
{
//do work
}
}
Or you can do a items.ToList() which will iterate the query and turn it into a list of objects that you can work with. Hope this helps.
I see articles on using SPMetal to generate the .cs file that allows LINQ to work properly. The file I'm talking about inherits from the Microsoft.SharePoint.Linq.DataContext class. How can I use LINQ without recompiling on my production environment, since I would need to regenerate this file using SPMetal on my production environment? I suspect the answer is going to be "can't do it".
I guess I'll use a CAML query instead unless there is some easier way to use LINQ that I am missing.
If the objective is just to query lists using LINQ and you want to avoid such recompilations, do not use SPMetal.
LINQ can be directly used on SPListItemCollection
e.g.
var FindCustomer = from SPListItem Item in Customers.Items
where Item["Orders"] as int == 5
select Item;
//or select new{Title = Item["Title"]}
This does not have hard coded entities but is more flexible. And as long as your list column names remain same, code can be deployed on any environment even if other lists are changing.
Also you can choose to retrieve few chosen field's data instead of retrieving data of all the fields every time.
There is no problem I guess. Personally I have been using Linq for good amount of time. I never generated the cs specifically for production. Is your site different across environments?
Im not sure if I'm missing the point or not, but the DataContext object takes the URL as apart of the constructor, so you should retrieve the URL from config somewhere E.g. database
DataContext teamSite = new DataContext("http://MarketingServer/SalesTeam");
OR use the SPContext object, if your code has a SharePoint context. E.g. in a web part
DataContext teamSite = new DataContext(SPContext.Current.Web.Url);
Okay, so I'm doing my first foray into using the ADO.NET Entity Framework.
My test case right now includes a SQL Server 2008 database with 2 tables, Member and Profile, with a 1:1 relationship.
I then used the Entity Data Model wizard to auto-generate the EDM from the database. It generated a model with the correct association. Now I want to do this:
ObjectQuery<Member> members = entities.Member;
IQueryable<Member> membersQuery = from m in members select m;
foreach (Member m in membersQuery)
{
Profile p = m.Profile;
...
}
Which halfway works. I am able to iterate through all of the Members. But the problem I'm having is that m.Profile is always null. The examples for LINQ to Entities on the MSDN library seem to suggest that I will be able to seamlessly follow the navigation relationships like that, but it doesn't seem to work that way. I found that if I first load the profiles in a separate call somehow, such as using entities.Profile.ToList, then m.Profile will point to a valid Profile.
So my question is, is there an elegant way to force the framework to automatically load the data along the navigation relationships, or do I need to do that explicitly with a join or something else?
Thanks
Okay I managed to find the answer I needed here http://msdn.microsoft.com/en-us/magazine/cc507640.aspx. The following query will make sure that the Profile entity is loaded:
IQueryable<Member> membersQuery = from m in members.Include("Profile") select m;
I used this technique on a 1 to many relationship and works well. I have a Survey class and many questions as part of that from a different db table and using this technique managed to extract the related questions ...
context.Survey.Include("SurveyQuestion").Where(x => x.Id == id).First()
(context being the generated ObjectContext).
context.Survey.Include<T>().Where(x => x.Id == id).First()
I just spend 10mins trying to put together an extention method to do this, the closest I could come up with is ...
public static ObjectQuery<T> Include<T,U>(this ObjectQuery<T> context)
{
string path = typeof(U).ToString();
string[] split = path.Split('.');
return context.Include(split[split.Length - 1]);
}
Any pointers for the improvements would be most welcome :-)
On doing a bit more research found this ... StackOverflow link which has a post to Func link which is a lot better than my extension method attempt :-)