GUIDs as the value field in drop-down, Any better approaches? - asp.net-mvc-3

I have a Products table in SQL Server database, it has an ID column that contains uniqueidentifiers, and a ProductName column with VARCHAR(100). This table contains over 5000 entries.
Now on my razor cshtml page, I have a drop down that displays these pProduct names. The way I have done is by binding drop-down value field to ID column and display text to ProductName. This mean that on the client side, if someone does a view source in their browser (or use any http sniffer etc), they can see all the GUIDs associated with these Product Names. Now when user submits this information, I have setup a server side validation, where I make sure that all the submitted GUIDs are
1) Valid Guids (not strings or anything else)
2) Submitted Guid exists among the IDs in Products. This ensure that any Guid that don't exists in Products.ID will not be processed.
The fact that a user can see this GUID data, does it expose any security risks? Are there any better ways of handling this situation?

Assuming your database has foreign keys, this causes no security hole. Even if it has no foreign keys (between a product table and product orders), you can simply look for the product first. If it doesn't exist, ignore the request.
So no, it's not a security hole - unless there is a security hole in the Guid Parsing of the .NET framework.

Related

Model-driven PowerApp: Best practice to display subgrid of records with no appropriate primary column name

Background
Each Dataverse table contains a primary name column. When displayed in a subgrid, clicking on the primary name column will navigate to the form so that the user can edit that row. Most subgrids in my application work this way.
The Problem
I have a Course form with a list of participants displayed in a subgrid. The subgrid displays each student's name (as a link) and the grade received in the course. There is no appropriate primary name column for this Participant table. To edit the participant record, the user must select the row in the subgrid, then click the subgrid's Edit button. As a result, this UI is different from all other subgrids in the application and I know that user's will click the student name to try to edit the participant record and be confused when they are presented with the student record.
Am I missing something? Is there a better way to handle this?
It's a common problem I face quite often. Here is usually what I would do.
Make sure the Primary Name Column always contains relevant information to the user to be able to quickly identify a record. Sometimes it requires copying information from one or multiple other columns into the primary column.
In your case that would probably means concatenating the student's name and grade.
How to do that?
Common to all solutions below
Use one of the following solution to copy the content of one or several fields into the primary column.
Make sure the solution you select also updates the content of the primary name column when one of the copied field is updated.
Remove or hide the primary column from the form, the name of the record will be displayed at the top of the form anyway and you probably don't want users to play with it.
Display the primary name column in every subgrid.
I would recommend not adding the fields copied into the primary column in the subgrids to avoid confusion.
Solution 1 - Classic Workflow
Create a classic workflow that runs when a record is created / updated
Pros:
Very quick to put in place
Runs synchronously (users will see the name updated in real-time)
Cons:
Not very practical if you need to add business logic (using different fields as source depending on a certain condition for example)
Solution 2 - Power Automate
Create a Flow that runs when a record is created / updated
Pros:
You can implement complex business logic in your Flow
Cons:
Runs asynchronously (users will have to refresh the page after the creation of a record to see the record's name)
According to Power Automate licensing that flow would certainly be considered as an "enterprise flow" and you are supposed to pay 100$ / month. That specific point must be taken with a grain of salt. I had several discussions with Microsoft about it and they haven't given me a clear answer about what would be considered an enterprise flow.
Solution 3 - Plugin
Create a plugin that executes when a record is created / updated
Pros:
You can implement very complex business logic in your Flow
It can run synchronously
Cons:
Pro-code (I put it as a con since Model-Driven App is a low-code / no-code approach but there is nothing wrong about pro-code per say)
Developing a new plugin for each entity where you need this logic is kind of overkill in my opinion. I would consider developing something very generic that would only require some sort of configuration when the logic needs to be applied to a new table.

Loading records into Dynamics 365 through ADF

I'm using the Dynamics connector in Azure Data Factory.
TLDR
Does this connector support loading child records which need a parent record key passed in? For example if I want to create a contact and attach it to a parent account, I upsert a record with a null contactid, a valid parentcustomerid GUID and set parentcustomeridtype to 1 (or 2) but I get an error.
Long Story
I'm successfully connecting to Dynamics 365 and extracting data (for example, the lead table) into a SQL Server table
To test that I can transfer data the other way, I am simply loading the data back from the lead table into the lead entity in Dynamics.
I'm getting this error:
Failure happened on 'Sink' side. ErrorCode=DynamicsMissingTargetForMultiTargetLookupField,'Type=Microsoft.DataTransfer.Common.Shared.HybridDeliveryException,Message=,Source=,''Type=Microsoft.DataTransfer.Common.Shared.HybridDeliveryException,Message=Cannot find the target column for multi-target lookup field: 'ownerid'.
As a test I removed ownerid from the list of source columns it loads OK.
This is obviously a foreign key value.
It raises two questions for me:
Specifically with regards to the error message: If I knew which lookup it needed to use, how can I specify which lookup table it should validate against? There's no settings in the ADF connector to allow me to do this.
This is obviously a foreign key value. If I only had the name (or business key) for this row, how can I easily lookup the foreign key value?
How is this normally done through other API's, i.e. the web API?
Is there an XRMToolbox addin that would help clarify?
I've also read some posts that imply that you can send pre-connected data in an XML document so perhaps that would help also.
EDIT 1
I realised that the lead.ownertypeid field in my source dataset is NULL (that's what was exported). It's also NULL if I browse it in various Xrmtoolbox tools. I tried hard coding it to systemuser (which is what it actually is in the owner table against the actual owner record) but I still get the same error.
I also notice there's a record with the same PK value in systemuser table
So the same record is in two tables, but how do I tell the dynamics connector which one to use? and why does it even care?
EDIT 2
I was getting a similar message for msauto_testdrive for customerid.
I excluded all records with customerid=null, and got the same error.
EDIT 2
This link appears to indicate that I need to set customeridtype to 1 (Account) or 2 (Contact). I did so, but still got the same error.
Also I believe I have the same issue as this guy.
Maybe the ADF connector suffers from the same problem.
At the time of writing, #Arun Vinoth was 100% correct. However shortly afterwards there was a documentation update (in response to a GitHub I raised) that explained how to do it.
I'll document how I did it here.
To populate a contact with against a parent account, you need the parent accounts GUID. Then you prepare a dataset like this:
SELECT
-- a NULL contactid means this is a new record
CAST(NULL as uniqueidentifier) as contactid,
-- the GUID of the parent account
CAST('A7070AE2-D7A6-EA11-A812-000D3A79983B' as uniqueidentifier) parentcustomerid,
-- customer id is an account
'account' [parentcustomerid#EntityReference],
'Joe' as firstname,
'Bloggs' lastname,
Now you can apply the normal automapping approach in ADF.
Now you can select from this dataset and load into contact. You can apply the usual automapping approach, this is: create datasets without schemas. Perform a copy activity without mapping columns
This is the ADF limitation with respect to CDS polymorphic lookups like Customer and Owner. Upvote this ADF idea
Workaround is to use two temporary source lookup fields (owner team and user in case of owner, account and contact in case of customer) and with parallel branch in a MS Flow to solve this issue. Read more, also you can download the Flow sample to use.
First, create two temporary lookup fields on the entity that you wish to import Customer lookup data into it, to both the Account and Contact entities respectively
Within your ADF pipeline flow, you will then need to map the GUID values for your Account and Contact fields to the respective lookup fields created above. The simplest way of doing this is to have two separate columns within your source dataset – one containing Account GUID’s to map and the other, Contact.
Then, finally, you can put together a Microsoft Flow that then performs the appropriate mapping from the temporary fields to the Customer lookup field. First, define the trigger point for when your affected Entity record is created (in this case, Contact) and add on some parallel branches to check for values in either of these two temporary lookup fields
Then, if either of these conditions is hit, set up an Update record task to perform a single field update, as indicated below if the ADF Account Lookup field has data within it

Dealing with disassociated records in a poorly designed database

Overview
I have inherited a website that allows users to order customised products. The customisations were saved in a way that disassociates them from their record. I would like to modify the db so these records can be associated.
Example
Users can get Product #1 "stock", or customise it, changing as many as 10 different properties. Let's say color, fabric, width, height etc.
Orders can, and regularly do, contain multiple products, each of which may be customised.
Additionally, users can save their orders, so they can re-order later.
When the database was designed, the details of the order was neatly organised into individual columns. Customer name, address, payment type etc. But the list of products and more notably their customisations were saved as a JSON string in a single column. For ease, let's call this column the "cart".
Basically, the order table has a column cart and the cart column contains a JSON-formatted list of products and customisations.
Unfortunately, the JSON object has reference ids to the product table, but lacks references to the customisation table. Instead it uses a bunch of strings meant for a human to read. Fortunately those strings exist in the customisation table, but they were written as the cart was created.
The problem we face is that the list of customisations can be changed by a CMS. So far, they haven't been changed. 🙏 But they will need to be soon and that's going to cause problems:
Problems
If a customisation option is removed (say, a fabric option) and a customer re-orders from an old saved order, we need to be able to parse the cart, detect this and warn them of the change.
Customisations are currently immutable. Once a product is added to the cart, it cannot be changed. Users need to delete and re-add to make a single change. Poor UX.
If anyone changes the human-readable text on a customisation we're dead. ☠️
Questions
How would you design this if you were staring from scratch?
How might we go about converting the current implementation and legacy data to this new schema?
I don't know if stack is notable, but we're on Postgres and Django-Python.
I would implement this with the following tables:
Products {
productId // primary key
name
price
}
Customization_Types {
customizationTypeId // primary key
name // e.g. COLOR, FABRIC, LENGTH
}
Customizations {
customizationId // primary key
customizationTypeId // foreign key
value // e.g. BEIGE, VELVET, 8
}
Product_Customizations {
productCustomizationId // primary key
productId // foreign key
customizationId // foreign key
priceModifier // price markup for applying the customization
isValid // false if this record is invalid/obsolete
}
Orders {
orderId // primary key
customerId // foreign key
}
Product_Orders {
productOrderId // primary key
orderId // foreign key
productId // foreign key
quantity
}
Customization_Orders {
customizationOrderId // primary key
productOrderId // foreign key
productCustomizationId // foreign key
}
The Products table contains the data for your base products - name, price, etc
The Customization_Types table contains the type names for your different customizations - COLOR, FABRIC, LENGTH, etc
The Customizations table contains a link to a customizationTypeId as well as a legal value - I'm assuming that users can't enter arbitrary numerical values (for e.g. LENGTH or WIDTH) i.e. they're given a drop-down box instead of a text box, however if they can enter arbitrary numerical data then you'll need MIN/MAX fields that are null for named constraints (so e.g. you could have Type:COLOR/Value:BEIGE/Min:NULL/Max:NULL or Type:LENGTH/Value:NULL/Min:4/Max:8)
The Product_Customizations table links a Customization to a Product, so for example if ProductX can come in BEIGE then you would create a Product_Customization record that links ProductX to BEIGE.
The Orders table just contains an orderId and anything else relevant to the order (e.g. a link to the customerId and shippingAddressId)
Product_Orders links a product to an order
Customization_Orders links a Product_Customization to a Product_Order
Let's say a customer orders ProductX in BEIGE and LENGTH=8, then you would create an Order record, a Product_Order record with a link to ProductX, and two Customization_Order records - one linked to COLOR=BEIGE and one linked to LENGTH=8.
This should make it easy to modify a product's customizations without having to reload the entire product - the user can modify color to COLOR=RED without touching the length customization (delete the old Customization_Order:COLOR=BEIGE record and create a new COLOR=RED record), or the user can remove the length customization without touching the color customization (delete the old Customization_Order:LENGTH=8 record).
When reloading an old order/product you can quickly verify that the same productCustomizationIds still apply to the product in question, else flag the user. Additionally, you can flag the user if the customization still applies but the customization's price modifier has changed.
As far as converting the legacy data, I'm not familiar with Python but I do have experience with reading JSON via Java and I'm assuming that Python offers similar if not better libraries for this. The trick is going to be matching the existing data to pre-loaded Product_Customization data - if the data fails to match then create a new Product_Customization row corresponding to it with isValid=FALSE (this is assuming that the customization in question is no longer offered), and when you get a chance manually iterate through the invalid Product_Customization rows to ensure that these really are unmatched customizations and not just parsing errors.
Little improvement to Zim-Zam's answer.
Even better approach is to store not plain values (BEIGE, VELVET, 8) as customization parameters, but kind of schema from which code can build up correct view of a customization.
It could be just JSON/XML formatted text. And the entity that is responsible for building view and applying logic should be able to work with JSON data of different versions.
For example, if properties of a customization have changed and something new has been added, in that case you only need to change code and adjusted JSON will be saved. No need to change existing data. Also there should be possibility to read old JSON versions with old properties and work with it.
Two possible ways of what to do if you read an old entity from DB:
View builder will ignore all old properties of a customization, add new properties and set their values to default. I would go with that personally.
Old view is presented to user, but when user clicks, for example, Ok button or Finish, additional logic will check that there are old properties and notifies user that they should be removed manually or just removes them automatically.
More flexible approach that requires only code changes without touching db and allows to show user old customization properties if it is necessary.
Update:
Customizations could have two kind of properties: one that administrator can define, such as title or price, which are not frequently changed and common for all customizations and another one such as size and color which could be changed frequently, could have user defined values and are not common for all customizations.
The first kind should be stored in Customization table as separate columns. That will allow to changed such properties in administrative panel and have all previously stored data consistent.
The second kind of properties could be 1) frequently changed 2) not all customization types could have such properties. It is a bad idea to store them as separate columns because if there are huge amount of data, changing column type or adding new column could cause performance degradation and sometimes could not be possible due to incompatible types of properties.
Actually, if they are stored as separate columns, you are probably will have to change code to support new properties anyway.
My idea is that you still allow administrator to change type of such properties and add new one or remove old one through some interface. The key thing here is that you are storing JSON data like this
{
"properties": {
{
"propertyName": "height",
"propertyType": "int",
"min" : 10,
"max" : 25,
},
{
"propertyName": "color",
"propertyType": "color",
},
{
"propertyName": "anotherCustomField",
"propertyType": "text",
},
}
}
What's left to do is to implement view builders or renderers for all kinds of property type. And add a separate table with only values. You fetched a customization record from db, you found out which customization properties there are, checked which one are still valid and rendered only valid ones. If administrator changed type of customization's property or just removed one, you marked that customization's property as not valid in db and that's all the work. No code changes, no database schema changes.

Product slug not working in CRM 2013 Email templates

I have a business requirement that execs are really wanting to see. In our lead and email templates we have a few of our products listed. The products themselves are pretty stable but the prices are of course subject to change.
I'm OK with hard coding the product name into email but I was hoping the price could be a slug that get's resolved.
We only have one price list so nothing to worry about there.
I see no way to get access to the product and or price list entities through the data field values dialog.
So my thought was to create the 2-4 fields in the lead and opportunity entities. Then create a background workflow that takes the price from the price list and sets those fields.
Then when an email is generated I can access those hidden fields as they will be available through the lead or opportunity entity.
Any thoughts, concerns, better approaches?
Unfortunately CRM's email template system is not capable of what you describe out of the box. As you have discovered it will only permit you to insert placeholders from, or assocated with, the primary entity. It won't let you insert fields from other entities. What you suggest as a workaround is possible but it's not an ideal solution as you'll have these 2-4 redundant fields on each record type that contain duplicated data from the price list.
If you have any experience with creating custom workflow activities using the SDK then the best solution here is to create a custom activity that accepts either an draft Email or Email template as a input parameter, instantiates the email if required, loads the price list data, and performs your own custom placeholder replacements.
I've done this on a few projects in order to pass multiple entity records into the template, or to insert complex tables into emails by loading data from relationships. See the screenshots below for an example of how I've configured the email template and dialog process to pass both an 'Account' and 'User' record into the email template.

How to make drop-down menu within Oracle Application Express?

I would really appreciate if anybody could shed some light on this, as search engine results have been singularly unhelpful so far.
I am attempting to build a GUI for an oracle database through Application Express. There tend to be tricks of how to set up drop down menus through the Graphical User Interface of the product that one uses (for instance I would know how to do this in a product like Microsoft Access).
For instance I have this form
The foreign key for Business (FK_BUSINESS_ID) is just an integer - not terribly user friendly! If it could be a drop down list of business names (BUSINESS.NAME), it would be great. Hiding the business primary key (BUSINESS_ID) would make it look nicer, but isn't altogether relevant.
I have found the source controls for the element in question.
What sort of SQL (or even PL/SQL) could be used to both display this data from the other table, and return the selected foreign key selected by the user when the form is sent?
Solution
Open up the item, and change the type to Select List
For List of Values Definition, enter select BUSINESS.NAME, FK_BUSINESS_ID from [TABLE]
Explanation
Select list shows display values to the user and returns the corresponding ID. Your source can remain the same as before.

Resources