Lookup data Table Name for Contacts and Contact.People

Discussions related to custom development with Select.
Post Reply
maxschilling
Posts: 2
Joined: Mon Apr 24, 2017 3:23 pm

Lookup data Table Name for Contacts and Contact.People

Post by maxschilling »

Hi all,

I have a requirement to load the lookup table data for order contacts and order contact people.

Once I determine the Lookup Table name, I have been able to successfully load the lookup table data that I need given the tablename and the lookupcode, however I have a question about figuring out the correct lookup table name in the first place.


I have been able to iterate through the collection of contacts (and contact persons) in IOrder and execute the following code for each:

Code: Select all

		
                string lookupTableName;
				
                Type contactType = spsContact.GetType();

                ILookupMap map = lookups.GetLookupMap(profileInfo);

                const string overlayPath = ".LookupCode";

                if (map.Contains(contactType, overlayPath))
                {
                    ILookupMapEntry mapEntry = map[contactType, overlayPath];

					lookupTableName = mapEntry.Name;
                }


This *will* successfully load the lookuptable name, however in my use case I am loading the lookup table data "out of band" and do not have the object availabe to call .GetType() on.


I only have the string representation of ContactType (spsContact.ContactType.ToString()) ie "Government"

ILookupMap will only accept "Type" for OverlayContext

Code: Select all

  public interface ILookupMap
  {
    ILookupMapEntry this[Type overlayContext, string overlayPath] { get; }

    bool Contains(Type overlayContext, string overlayPath);

    bool TryGet(Type overlayContext, string overlayPath, out ILookupMapEntry entry);
  }
I found the following code in an example on these forums:

Code: Select all

            List<ILookupTableInfo> tables = lookups.Tables
                .Where(t =>
                    t.OverlayContext == "Government" &&
                    t.OverlayPath == ".LookupCode")
                .HavingProfile(profileInfo)
                .ToList();


However, in my testing, including any version of Having Profile:

.HavingProfile(Guid)
.HavingProfile(IProfileInfo)
.HavingProfiles(IList<IProfileInfo>)

will result in zero results for the query.

Removing .HavingProfile returns a resultset that seems to span all profiles.

When I pass the same IProfileInfo object in the code above to .GetLookupMap() it will result in the correct data being loaded.

Are the .HavingProfile(s) extension methods supposed to work in the above example?

Is there a different interface available where I can can pass in the "OverlayContext" as a string, rather than the object Type?

Thanks in advance,
Max Schilling
FNF FSI Team
BobRichards
Posts: 1377
Joined: Wed Jan 15, 2014 3:50 pm
Location: Raleigh, NC
Contact:

Re: Lookup data Table Name for Contacts and Contact.People

Post by BobRichards »

There is no direct way to get that information without access to the users in the order since the query process will automatically insert some additional information into the search criteria. But since you don't know the table name or ID, you can't use that method since it will throw an exception. The next best thing might be to continue on with your code and after you have the applicable tables, get their extended information and test the profile paths yourself.

Code: Select all

// Get all of the tables with the desired overlay requirements.
ILookups lookups = ss.GetService<ILookups>();
IList<ILookupTableInfo> tables = lookups.Tables
.Where(t =>
	t.OverlayContext == "Government" &&
	t.OverlayPath == ".LookupCode")
.ToList();

// Get the full information for each table.  Check the profile root path(s)
//  to see if there is a match.  If one or more matches is found, add the profile
//  to the matchingTables collection.
IList<ILookupTableInfo> matchingTables = new List<ILookupTableInfo>();
string orderOwnershipProfile = @"Default\aa\bb\cc";
foreach (var table in tables)
{
	LookupQuerySpec spec = new LookupQuerySpec();
	spec.Table = table.Name;

	// Get full profile information.
	ILookupTable qtable = lookups.QueryTable(spec);

	// Tables can be attached to multiple profiles.  Check them all.
	bool isMatch = qtable.Profiles.Any(t => orderOwnershipProfile.StartsWith(t.Path));
	if (isMatch)
	{
		// This table has a profile that matches the path.
		matchingTables.Add(table);
	}                   
}
Bob Richards, Senior Software Developer, SoftPro
maxschilling
Posts: 2
Joined: Mon Apr 24, 2017 3:23 pm

Re: Lookup data Table Name for Contacts and Contact.People

Post by maxschilling »

Hi Bob,

I need to run this operation for MANY contacts and contact persons to reconcile data each evening, so what you described seems like it would be a VERY expensive operation as it is not referencing the cached dataset...

What if I, instead, had an operation that pre-loaded the Lookup tables for all contact types in all non-test profiles and then used that pre-loaded data in the subsequent operation?

The following code seems to return the data I need and seems to run reasonably quickly... it did take a while to complete on the first iteration (I did not time the first run), but now, against FutureCA, it runs in about 7-10 seconds and loads approximately 9000 results on subsequent loads... I'd assume in prod many of these profiles would already be in memory cache and the performance would be relatively efficient?

Because the darned lookup Map only accepts a type as an input, the only way I could figure out to make this work was to take as input an order that we know has at least one contact on it. I then grab the type of that first contact, get the assembly name out and then re-generate a new type string and subsequently a new type for for each of the contact types in the ContactType enum.

It seems necessary as the .NET Type for the contacts are as follows:

Code: Select all

Government,                 SoftPro.OrderTracking.Order_68a7b0ff1de20f4196d39e60e610509c, Version=4.0.30302.58, Culture=neutral, PublicKeyToken=8ae96314b6bae08d
HazardInsuranceAgent, 		SoftPro.OrderTracking.Order_68a7b0ff1de20f4196d39e60e610509c, Version=4.0.30302.58, Culture=neutral, PublicKeyToken=8ae96314b6bae08d
I am assuming these libraries (which I found on disk at C:\ProgramData\SoftPro\EntityModels\SoftPro.OrderTracking.Order), are only loaded while processing a particular order... Therefore it seems I would need to be in the process of opening an order to be able to guarantee that the particular library is in memory...

Is that an accurate assumption, or would there be a means of pre-loading a SoftPro.OrderTracking.Order library for a particular softpro version and accomplishing this without that order load mechanism?

Do you see anything wrong with this approach, we would be running this once a day in the evening when the load on the server would presumably be low.

Code: Select all


    public class LookupTableData
    {
        public Guid ProfileId { get; set; }
        public string ContactType { get; set; }
        public string LookupTableName { get; set; }
        public Guid LookupTableGuid { get; set; }
    }

        public IList<LookupTableData> GetAllLookupTableData(Guid orderId)
        {
            var orderIdentifier = new OrderIdentifier(orderId);

            var results = new List<LookupTableData>();

            using (var order = softProSession.OrderStore.OpenOrder(orderIdentifier, OrderEditMode.ReadOnly))
            {
                dynamic spsOrder = order;

                var contacts = spsOrder?.Contacts;

                var firstContact = contacts?[0];

                if (firstContact == null)
                {
                    return null;
                }

                Type firstContactType = firstContact.GetType();

                var lookups = softProSession.Lookups;

                var allProfiles = softProSession.ProfileManager.Profiles.ToList();

                var contactTypes = Enum.GetValues(typeof(ContactType)).Cast<ContactType>().ToList();

                foreach (var profile in allProfiles)
                {
                    if (IsTestOrder(profile.Path))
                    {
                        continue;
                    }

                    var map = lookups.GetLookupMap(profile);

                    foreach (var contactTypeEnum in contactTypes)
                    {
                        var contactType = contactTypeEnum.ToString();

                        var foo = string.Join(", ", contactType, firstContactType.Assembly.FullName);

                        var newType = Type.GetType(foo);

                        const string overlayPath = ".LookupCode";

                        if (!map.Contains(newType, overlayPath))
                        {
                            continue;
                        }

                        var mapEntry = map[newType, overlayPath];

                        var item = new LookupTableData
                                     {
                                         ContactType = contactType,
                                         ProfileId = profile.ID,
                                         LookupTableGuid = mapEntry.ID,
                                         LookupTableName = mapEntry.Name
                                     };

                        results.Add(item);
                    }
                }

            }

            return results;
        }
        
        public bool IsTestOrder(string profilePath)
        {
            if (configuration.AllowTestOrders)
            {
                return false;
            }

            if (string.IsNullOrWhiteSpace(profilePath))
            {
                return false;
            }

            profilePath = profilePath.ToUpper();

            if (profilePath.StartsWith(@"\\DEFAULT\T\") ||
                profilePath.StartsWith(@"DEFAULT\T\") ||
                profilePath == @"\\DEFAULT\T" ||
                profilePath == @"DEFAULT\T")
            {
                return true;
            }

            if (profilePath.Contains(@"\CBA"))
            {
                return true;
            }

            return false;
        }
BobRichards
Posts: 1377
Joined: Wed Jan 15, 2014 3:50 pm
Location: Raleigh, NC
Contact:

Re: Lookup data Table Name for Contacts and Contact.People

Post by BobRichards »

Sorry but it is a little beyond scope for this forum to validate that much code. However the idea of building your own cache for the table data sounds fine as long as no one adds lookups while your application runs.

Also, there is no way to "preload" the order models. Select automatically loads the order version on demand so you don't need to. But once it is loaded and JIT'ed it is reused and not deleted between orders.
Bob Richards, Senior Software Developer, SoftPro
Post Reply