Related entities not loading when using Include with Linq to Entities - vb.net

In my schema, I have the following relevant entities:
Basic schema for Element/Folder/User/Role
I'm trying to load a list of the Folders that each User has access to - either directly or by virtue of the User having a Role that has access to the Folder. I've split the requirement into two pieces (direct and indirect) and used a Union to pull the records back in a single collection, with only one call to the database.
I've got the L2E statement below running - as long as a User has a (direct or indirect) record in Access, the Folder entity will be returned in the list (Note: sUserElmKey is the key of the current User).
Dim fldList = (From fld As Folder In ctxClient.Elements.OfType(Of Folder)() _
Join acs As Access In ctxClient.Accesses
On acs.ElementKey Equals fld.ElementKey
Where acs.UserRoleElementKey = sUserElmKey _
Select fld
).Union _
(From fld As Folder In ctxClient.Elements.OfType(Of Folder)() _
Join acs As Access In ctxClient.Accesses
On acs.ElementKey Equals fld.ElementKey
Join rol As Role In ctxClient.Elements.OfType(Of Role)()
On rol.ElementKey Equals acs.UserRoleElementKey
Where (From usr As User In rol.Users
Where usr.ElementKey = sUserElmKey).Any _
Select fld
)
This worked fine when I just needed to know if any access to each Folder was available to the User. Now I need to define other logic based on which AccessTypeCode the User has for each Folder.
Given that I need to iterate over the resulting entities in the collection in order to load a different collection of objects that represents nodes in the tree UI, I figured I'd just add an Include("Accesses") to each sub-statement above. As I iterated through the resulting collection, I could write the logic to set the needed flags for each of the new node objects (e.g. if the User has AccessTypeCode "VIEW", then they have read-only access; "OWN" or "NEW" and they have read/write access).
Unfortunately, I haven't been able to find where to add the Include to do this properly - I've put it after the "ctxClient.Elements" as well as after the ".OfType(Of Folder()", but while the code compiles and runs, the debugger shows 0 records for the Accesses collection under each Folder when I'm iterating.
Rewriting this as a lambda expression is an option, but I think I might just find myself in the same place.
Any ideas on how to resolve this?

Looking at you schema I think you can rewrite the query as follows:
(From fld In ctxClient.Elements.OfType(Of Folder)()
Where fld.Accesses.Any(Function(acs) acs.UserRoleElementKey = sUserElmKey)
Select fld)
.Union
(From fld In ctxClient.Elements.OfType(Of Folder)()
Where fld.Accesses.Any(Function(acs) acs.UserRoleElement.Users
.Any(Function(usr) usr.ElementKey = sUserElmKey))
Select fld)
This checks whether there is any access, directly or through roles, without having to Include accesses.

Related

Outlook-Addin: How to retrive folder tree fast when Exchange-Online is used

I'm using Outlook Redemption library.
I am trying to retrieve the folder list of an Outlook store. I only need folders which meet a some criterias and only need their name and there DefaultMessageClass.
Iteration the RDOFolder objects is quite slow when an Exchange Account without a cached is used.
So I played around with the GetAllChildFolders() and combined it the the RODFolderItems findmethod.
http://www.dimastr.com/redemption/RDOFolders.htm
var allFolder = ((RDOFolder2)rootFolder).GetAllChildFolders();
string folderSelect = "SELECT Name, EntryId, DefaultMessageClass FROM FOLDER WHERE \"http://schemas.microsoft.com/mapi/proptag/0x3613001F\" like '%Note%'";
try
{
RDOFolder vFolder = vFolders.Find(folderSelect);
while (vFolder != null)
{
System.Diagnostics.Debug.WriteLine(vFolder.Name);
System.Diagnostics.Debug.WriteLine(vFolder.EntryID);
System.Diagnostics.Debug.WriteLine(vFolder.DefaultMessageClass);
vFolder = vFolders.FindNext();
}
}
catch (Exception ex)
{
//log error here!
}
Now I got two questions!
1. SQL Syntax
From the docmentation of find method
The properties specified in the SQL query must either use the Outlook Object Model (or RDO)
property name (e.g. Subject, Email1Address)
I can't make Outlook or Redemption properties work within the SQL statement. Has somebody a working example?
2. Performance access properties
The online documentation states the following.
Including the SELECT clause allows Redemption to pre-fetch the
properties from the folder hierarchy table without opening the item
resulting in a significant performance gain (see example "I wish there was one :-)"). If you
later access a property not specified in the SELECT clause, Redemption
will open the item.
How do I access the properties prefetch from the folder hierarchy?
If I use RDOFolder.Name, the whole folder object is opened and there is not much of an performance gain.
Which properties do not work? Are you getting a particular error?
What is your full SELECT statement? To avoid the chance of opening a folder, use RDOFolders.MAPITable.ExecSQL - it will
return an ADODB.Recordset object.

LDAP_MATCHING_RULE_IN_CHAIN not working with default AD groups - Domain Users

In my program, I need to fetch all the AD groups for a user.
The current version of my program uses System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups.
This works good until we had a new customer with a much largeer AD. There it is really slow. (Up to 60 seconds)
Now I've been looking around, and saw the posts that the AccountManagement is easy to use, but slow.
I also found that LDAP_MATCHING_RULE_IN_CHAIN should also fetch all the nested groups that a user is member of. And is more performant.
From this link.
But I'm having an issue with the default groups that in AD exists.
For example the group "Domain Users" is not returned by the function.
They also have a group "BDOC" that as member have "Domain Users". That group is also not returned.
Trough the GetAuthorizationGroups it is returned correct.
I'm using following code to fetch the groups by user.
VB.NET:
Dim strFilter As String = String.Format("(member:1.2.840.113556.1.4.1941:={0})", oUserPrincipal.DistinguishedName)
Dim objSearcher As New DirectoryServices.DirectorySearcher("LDAP://" & oLDAPAuthenticationDetail.Domain & If(Not String.IsNullOrWhiteSpace(oLDAPAuthenticationDetail.Container), oLDAPAuthenticationDetail.Container, String.Empty))
objSearcher.PageSize = 1000
objSearcher.Filter = strFilter
objSearcher.SearchScope = DirectoryServices.SearchScope.Subtree
objSearcher.PropertiesToLoad.Add(sPropGuid)
objSearcher.PropertiesToLoad.Add(sPropDisplayName)
Dim colResults As DirectoryServices.SearchResultCollection = objSearcher.FindAll()
Afterwards I was testing with the script from the link, if it was possible to fetch all the users from the Domain Users group, by changing the "member" to "memberOf" in the filter.
When I put the Domain Admins group in the filter, it shows the admins correct.
When I put the Domain Users group in the filter, it returns nothing.
Powershell:
$userdn = 'CN=Domain Users,CN=Users,DC=acbenelux,DC=local'
$strFilter = "(memberOf:1.2.840.113556.1.4.1941:=$userdn)"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://rootDSE")
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = "LDAP://$($objDomain.rootDomainNamingContext)"
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Base"
$colProplist = "name"
foreach ($i in $colPropList)
{
$objSearcher.PropertiesToLoad.Add($i) > $nul
}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{
$objItem = $objResult.Properties
$objItem.name
}
I don't know what I'm doing wrong. Or is it maybe just not possible to fetch the "default groups" with that filter?
What is a good alternative then?
The default group is odd. It is not stored in memberOf, or even in the member attribute of the group. That's why your search won't find it. The default group is determined by the primaryGroupId of the user. That attribute stores the RID (the last part of the SID) of the group. It's kind of dumb, I know :)
I actually wrote an article about the 3 (yes 3) different ways someone can be a member of a group: What makes a member a member?
I also wrote an article about getting all of the groups a single user belongs to, and how to account for all 3 ways: Finding all of a user’s groups
For example, here is the C# code I put in that article about how to find the name of the primary group for a user (given a DirectoryEntry). It shouldn't be too hard to translate that to VB.NET:
private static string GetUserPrimaryGroup(DirectoryEntry de) {
de.RefreshCache(new[] {"primaryGroupID", "objectSid"});
//Get the user's SID as a string
var sid = new SecurityIdentifier((byte[])de.Properties["objectSid"].Value, 0).ToString();
//Replace the RID portion of the user's SID with the primaryGroupId
//so we're left with the group's SID
sid = sid.Remove(sid.LastIndexOf("-", StringComparison.Ordinal) + 1);
sid = sid + de.Properties["primaryGroupId"].Value;
//Find the group by its SID
var group = new DirectoryEntry($"LDAP://<SID={sid}>");
group.RefreshCache(new [] {"cn"});
return group.Properties["cn"].Value as string;
}
You are right that the AccountManagement namespace makes things easy, but it really does have terrible performance sometimes. I never use it anymore. I find that DirectoryEntry/DirectorySearcher gives you much more control of how often your code makes calls out to AD.
I have been meaning to write an article about writing high performance code with DirectoryEntry, but I haven't gotten around to it yet.
Update: So if you need the nested groups for the user, including membership through the primary group, then you can find the primary group first, then do an LDAP_MATCHING_RULE_IN_CHAIN search for groups that have both the user and the primary group as members:
(|(member:1.2.840.113556.1.4.1941:={userDN})(member:1.2.840.113556.1.4.1941:={primaryGroupDN}))
Update: If you want to include Authenticated Users in your search (edit the DC portion of the distinguishedName):
(|(member:1.2.840.113556.1.4.1941:=CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=domain,DC=com)(member:1.2.840.113556.1.4.1941:={userDN})(member:1.2.840.113556.1.4.1941:={primaryGroupDN})
Note that you can also use the tokenGroups attribute of a user to find out all of the authentication groups for a user. The tokenGroups attribute is a constructed attribute, so you only get it if you specifically ask for it (with DirectoryEntry.RefreshCache() or, in a search, add it to DirectorySearcher.PropertiesToLoad.
The caveat is that tokenGroups is a list of SIDs, not distinguishedName, but you can bind directly to an object using the SID with the LDAP://<SID={sid}> syntax.

SQL, Link to Element Feature

I have two classes. One Attribut of class1 is connected to another attribut of class2. This was done with the help of the context menue of the connector in the proximity of one class, it is called Link to element feature. The same is done on the other side of the connector to select the other attribut of the other class. So the connector directly connects two attributes and not the classes itselves. I haven´t found the tables, where this infomation is stored, so I dont find the appropiate SQL to find connected (or not connected) attributes.
Here's the way for notes using Link to..
PDATA1 = 'Attribute'
PDATA2 = t_attribute.ID of the attribute
PDATA3 = name of the attribute
PDATA4 has 'Yes' (I don't recall what that's used for so you can probably ignore it
First SQL:
SELECT PDATA2 FROM t_object WHERE Object_Type='Note' AND PDATA1 = 'Attribute'
will give you the ID. Just put that in another SQL:
SELECT * FROM t_attribute WHERE ID = (above SQL)
and you have the attribute details. Or if you want to find the unmapped one just build a dissection with the found IDs from the first with the existing attribute IDs.
For associations using Link to... it's a bit more tricky. First off, any such connectors have the relevant information stored in t_connector.StyleEx like e.g.
LFEP={69A30E17-23AB-4641-9573-9BDBAA988D52}L;
LF<dir>P=<guid><pos>; connector is attached to attribute/operation
<dir> = S or E meaning Start (source) or End (target) <guid> = ea_guid of t_attribute or t_operation
<pos> is the edge (L or R) where the connector had been attached to in the moment when the link has been created. This is a superfluous information since the renderer will attach the link to whatever place is relevant.
There can be one LFSP, one LFEP or both be present in one StyleEx property
(from my Inside book)
Now you can filter that information with a SQL or (what I prefer) with a little script, since doing complex SQL string operations are not my expertise.

Entity Framework 5 foreign key values not updating (VB.NET)

I realise many similar questions have been asked but after attempting tens of examples I still cannot get this to work. The problem I am having involves the updating of foreign key values in the Users table.
I am creating a website using ASP WebForms, .net 4.5, Entity Framework 5, SQL server 2012.
I created the EF using database first, here you can see a sample of the model:
I am using a 3 tier architecture - my data access layer queries the database using LINQ and returns a User object (as defined by T4).
This means the object is disconnected from the context when it is modified. (right?)
I modify the object in the page's code behind, updating the UserRole by doing this:
(in page load)
Dim _userAcces As UserLayer = UserLayer.getInstance
mUser = _userAcces.getUserWithCustomerAndRole(_ID)
Dim _rolesAccess As UserRolesLayer = UserRolesLayer.getInstance
mRoles = _rolesAccess.listUserRoles
(on button click)
mUser.UserRole = mRoles.Item(dropDownRole.SelectedIndex)
At this point the mUser object holds the correct (updated) UserRole object. I then pass the mUser object back into my data layer to update the database. The simnplest examples I have seen do this:
Using _context As New SaasPortalContext
_context.Entry(updatedUser).State = EntityState.Modified
_context.SaveChanges()
End Using
I have also tried using:
_context.users.attach
getting the original from the context using _context.users.find(id) and updating the original from the updated User object
getting the dbentityentry object from _context.entry(updatedUser) and setting the property("UserRole").isModified property to true
Using any of these methods I expect the foreign key value for UserRole in the Users table to update to be the ID of a different role in the UserRole table, reflecting the changes in the mUser object, but this does not happen and the value does not change.
What am I doing wrong? Why will the ID not update in the Users table?
Any help would be greatly appreciated
I finally worked out what I was doing wrong, I did not check the box for "include foreign key relationships" when setting up the model, meaning the relationships were not properly defined (I assume).

How to check the user's CRUD permissions for an object in Salesforce?

According to a requirement, i have to change the owner of an account if the user does not have read access to a third object.
I need a functionality similar to the isAccessible() method of Describe Field Result, but it is only available for the current logged in user.
Is there any other way to check the user's CRUD permissions for an object in Apex code?
I wrote an article about this on my blog. There is a feature that was just released in version 24.0 of the API (Spring Release) that will let you do just this on a record by record basis for the current user.
Here is the link to that blog entry that goes into details: How to tell if a user has access to a record
Don't confuse record level access with CRUD - the latter is the ability for a user to Create, Read, Update or Delete an object in general, regardless of sharing rules etc. that might affect the user's access to a particular record.
To check whether a user can create (e.g. Contacts) in general, just use
Schema.sObjectType.Contact.isCreateable()
(returns true or false)
From the documentation. it sounds like you want to use execute anonymously.
Apex generally runs in system context; that is, the current user's permissions, field-level security, and sharing rules aren’t taken into account during code execution.​ The only exceptions to this rule are Apex code that is executed with the executeAnonymous call. executeAnonymous always executes using the full permissions of the current user. For more information on executeAnonymous, see Anonymous Blocks.
Although Apex doesn't enforce object-level and field-level permissions by default, you can enforce these permissions in your code by explicitly calling the sObject describe result methods (of Schema.DescribeSObjectResult) and the field describe result methods (of Schema.DescribeFieldResult) that check the current user's access permission levels. In this way, you can verify if the current user has the necessary permissions, and only if he or she has sufficient permissions, you can then perform a specific DML operation or a query.
For example, you can call the isAccessible, isCreateable, or isUpdateable methods of Schema.DescribeSObjectResult to verify whether the current user has read, create, or update access to an sObject, respectively. Similarly, Schema.DescribeFieldResult exposes these access control methods that you can call to check the current user's read, create, or update access for a field. In addition, you can call the isDeletable method provided by Schema.DescribeSObjectResult to check if the current user has permission to delete a specific sObject.
http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_classes_perms_enforcing.htm#kanchor431
Have you tried the runAs() method?
Something like (not verified):
User u = [SELECT Id FROM User WHERE Name='John Doe'];
System.runAs(u) {
if (Schema.sObjectType.Contact.fields.Email.isAccessible()) {
// do something
}
}
The DescribeSObjectResult class has methods for checking CRUD.
E.g. this allows you to test whether or not the current user can update the account object in general.
Schema.DescribeSObjectResult drSObj = Schema.sObjectType.Account;
Boolean thisUserMayUpdate = drSObj.isUpdateable();
#John De Santiago: your article covers record level access rather than object CRUD (= object level access)
Very old post. Since then SF add option to query object permission:
Select SobjectType ,ParentId, PermissionsEdit, PermissionsRead
From ObjectPermissions
Order by ParentID, SobjectType ASC
Basically you will need to get the profile and permissionset of the user that you want to check and the relevant object. So it will be something like:
Select SobjectType ,ParentId, PermissionsEdit, PermissionsRead
From ObjectPermissions
where parentId IN :UserProfileIdAndPermission
AND sObjectType=:objectType
Order by ParentID, SobjectType ASC