Trying to figure out what would be the most efficient approach to use a subquery in an inline SQL statement. Inline SQL is not something I have much experience with, but I have no choice at my organization alas.
SELECT *
FROM dbo.VW_RMISPayment
WHERE ProcDate BETWEEN '7/2/2018' AND '3/8/2019'
AND Status = 'P'
AND Fund = '359'
AND Amount > 0
AND (BatchNotate = 'B' OR BatchNotate IS NULL)
ORDER BY ProcDate, Amount
How could I parameterized the (BatchNotate = 'B' OR BatchNotate IS NULL) part?
My variable passed in as a List<string>, but I could change it to be anything. I'm just not sure how I can create this subquery from my variable
if (BatchNotate.Count() > 0)
{
query += " AND BatchNotate= #BatchNotate";
}
cmd.Parameters.AddWithValue("#BatchNotate", batchNotate);
Use this:
BatchNotate = COALESCE(#inVariable,'B')
If the variable (#inVariable) is null then it will "default to B.
If it is something else it will compare against that.
Could you do something like this?
SELECT *
FROM dbo.VW_RMISPayment
WHERE ProcDate BETWEEN '7/2/2018' AND '3/8/2019'
AND Status = 'P'
AND Fund = '359'
AND Amount > 0
AND BatchNotate = COALESCE(#BatchNotate, BatchNotate)
ORDER BY ProcDate, Amount
Neither of those worked for what I'm after. Here is what I ended up doing. It's kinda hacky, but it works.
public static string AddParametersOR<T>(SqlParameterCollection parameters,
string fieldName,
string pattern,
SqlDbType parameterType,
int length,
IEnumerable<T> values)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
if (pattern == null)
throw new ArgumentNullException("pattern");
if (values == null)
throw new ArgumentNullException("values");
if (!pattern.StartsWith("#", StringComparison.CurrentCultureIgnoreCase))
throw new ArgumentException("Pattern must start with '#'");
var parameterNames = new List<string>();
foreach (var item in values)
{
var parameterName = parameterNames.Count.ToString(pattern, CultureInfo.InvariantCulture);
string parameterWithFieldName = string.Empty;
if (item.ToString().ToUpper() == "NULL")
{
parameterWithFieldName = string.Format("{0} IS NULL", fieldName);
}
else if (item.ToString().ToUpper() == "NOTNULL")
{
parameterWithFieldName = string.Format("{0} IS NOT", fieldName);
}
else
{
parameterWithFieldName = string.Format("{0}= {1}", fieldName, parameterName);
}
parameterNames.Add(parameterWithFieldName);
parameters.Add(parameterName, parameterType, length).Value = item;
}
return string.Join(" OR ", parameterNames.ToArray());
}
Usage:
if (batchNotate.Count() > 0)
{
query += " AND ({#BatchNotate})";
}
string batchNotateParamNames = SqlHelper.AddParametersOR(cmd.Parameters, "BatchNotate", "#B0", SqlDbType.VarChar, 1, batchNotate);
cmd.CommandText = query.Replace("{#BatchNotate}", batchNotateParamNames);
Depending on how many items are in your list the output will look like this:
(BatchNotate= 'B' OR BatchNotate= 'N' OR BatchNotate IS NULL)
If it finds "NULL" or "NOTNULL" it will replace these with IS NULL or IS NOT NULL
Related
I am working on a tool, that translates filter strings into linq/efcore expressions. Say following filter string is given:
{
'condition': 'OR',
'rules': [
{ 'column': 'Foo', 'value': 'Bar' },
{ 'column': 'Foo', 'value': 'Baz' },
]
}
Using an efcore extension I can filter a model like so:
_dbContext.MyModel
.Filter(filterString)
.ToList();
The issue is, that the generated query uses case instead of where statements:
SELECT
*
FROM
[MyModel] AS [c]
WHERE
(
CASE
WHEN [c].[Foo] = "Bar" THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[Foo] = "Baz" THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END
) = CAST(1 AS bit)
instead of:
SELECT
*
FROM
[MyModel] AS [c]
WHERE ([c].[Foo] = "Bar" OR [c].[Foo] = "Baz")
The last query takes much less time. Why is case used instead of where? Is it possible to instruct the parser to use where?
Extension:
public static IQueryable<T> Filter<T>(this IQueryable<T> query, string? filter)
{
if (string.IsNullOrEmpty(filter))
{
return query;
}
Expression<Func<T, bool>>? predicate;
try
{
predicate = new FilterExpressionParser().ParseExpressionOf<T>(JsonDocument.Parse(filter));
}
catch (Exception ex)
{
throw new FilterException($"Filter \"{filter}\" could not be parsed into a predicate.", ex);
}
return query.Where(predicate);
}
The extension tries to parse the filter string as a json document and create an linq expression from it. The logic for that is inside the FilterExpressionParser:
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;
public enum FilterConditionType
{
AND,
OR
}
public class FilterExpressionParser
{
public Expression<Func<T, bool>> ParseExpressionOf<T>(JsonDocument json)
{
var param = Expression.Parameter(typeof(T));
var conditions = ParseTree<T>(json.RootElement, param)!;
if (conditions.CanReduce)
{
conditions = conditions.ReduceAndCheck();
}
return Expression.Lambda<Func<T, bool>>(conditions, param);
}
private delegate Expression Binder(Expression? left, Expression? right);
private Expression? ParseTree<T>(JsonElement condition, ParameterExpression parameterExpression)
{
Expression? left = null;
var conditionString = condition.GetProperty(nameof(condition)).GetString()?.ToUpper();
if (!Enum.TryParse(conditionString, out FilterConditionType gate))
{
throw new ArgumentOutOfRangeException(nameof(condition), $"Not expected condition type: {condition}.");
}
JsonElement rules = condition.GetProperty(nameof(rules));
Binder binder = gate == FilterConditionType.AND ? Expression.And! : Expression.Or!;
Expression? bind(Expression? left, Expression? right) => left == null ? right : binder(left, right);
foreach (var rule in rules.EnumerateArray())
{
string? column = rule.GetProperty(nameof(column)).GetString();
object? toCompare = value.GetString().GetProperty(nameof(value));
Expression property = Expression.Property(parameterExpression, column);
BinaryExpression? right = Expression.Equal(property, Expression.Constant(toCompare, property.Type))
left = bind(left, right);
}
return left;
}
}
For combining predicates you have used bitwise operators Expression.And and Expression.Or Bitwise and shift operators
In C# generated result looks like
e => (e.Some > 1) & (e.Some < 10) | (e.Some == -1)
So, EF is also trying to convert bit operations to the SQL.
Instead of them use Expression.AndAlso and Expression.OrElse which are Boolean logical operators
e => (e.Some > 1) && (e.Some < 10) || (e.Some == -1)
For analysis generated expressions, I would suggest to use ReadableExpressions.Visualizers and probably you will find mistake by yourself.
I am having some issues creating a CompositeFilterDescriptor from scratch.
The scenario: We have date filters on the grid, but are showing date time fields.
When Someone filters to a specific date, we show all records for that day.
when someone filters greater than that day, we edit the filterDescriptor for that filter and add one day to it.
when someone filters to less than or equal to that day, we edit the filterdescriptor for that filter and add one day to it.
Here is the problem: when someone selects "not equal to", the solution would be to create a compositeFilterDescriptor containing two filters. One for less than that day, one for greater than that day.
I may ore may not be doing this part properly. I can not figure out how to add the new compositeFilterDescriptor to the existing dataSourceRequest.
Here is what I have:
static public DataSourceRequest FixDateFilters(DataSourceRequest request, string[] optionalFields = null)
{
if (request?.Filters != null)
{
List<string> matchingFields = new List<string> { "utc", "time" };
List<FilterDescriptor> newDescriptors = new List<FilterDescriptor>();
//if any fields specific to a page have been passed in, append them to the list.
if (optionalFields != null)
{
foreach (string field in optionalFields)
matchingFields.Append(field);
}
foreach (string matchingField in matchingFields)
{
List<FilterDescriptor> descriptors = new List<FilterDescriptor>();
FilterDescriptor descriptor1 = FindFirstFilterByMember(request.Filters, matchingField, true, null);
//there could be up to two filters on a matching field. Get the 2nd one if it exists.
FilterDescriptor descriptor2 = FindFirstFilterByMember(request.Filters, matchingField, true, descriptor1);
//turn the matches into a list for iterating
if (descriptor1 != null)
descriptors.Add(descriptor1);
if (descriptor2 != null)
descriptors.Add(descriptor2);
if (descriptors.Count != 0)
{
foreach (FilterDescriptor descriptor in descriptors)
{
DateTime? utcDate = (DateTime?)descriptor?.Value;
if (utcDate.HasValue)
{
utcDate = Core.DateHelper.ToUTC(utcDate.Value, HttpContext.Current); //make sure date ranges are one day ranges in local time
}
if (utcDate != null)
{
if (descriptor.Operator == FilterOperator.IsLessThanOrEqualTo)
{
utcDate = ((DateTime)utcDate).AddDays(1);
descriptor.Value = utcDate;
}
else if (descriptor.Operator == FilterOperator.IsGreaterThan)
{
utcDate = utcDate.Value.AddDays(1);
descriptor.Value = utcDate;
}
else if (descriptor.Operator == FilterOperator.IsEqualTo)
{
descriptor.Operator = FilterOperator.IsGreaterThanOrEqualTo;
newDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsLessThan, utcDate.Value.AddDays(1)));
}
else if (descriptor.Operator == FilterOperator.IsNotEqualTo)
{
CompositeFilterDescriptor cfd = new CompositeFilterDescriptor();
cfd.LogicalOperator = FilterCompositionLogicalOperator.Or;
cfd.FilterDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsLessThan, utcDate.Value;));
cfd.FilterDescriptors.Add(new FilterDescriptor(descriptor.Member, FilterOperator.IsGreaterThan, utcDate.Value.AddDays(1)));
newDescriptors.Add( cfd);
}
}
}
}
}
//equals matches add a new descriptor so add them after the foreach is done so they don't affect the foreach.
foreach (FilterDescriptor newDescriptor in newDescriptors)
{
if (newDescriptor.Member != "") //a blank descriptor has a emptry string for member
{
request.Filters.Add(newDescriptor);
}
}
}
return request;
}
}
static public FilterDescriptor FindFirstFilterByMember(IEnumerable<IFilterDescriptor> filters, string findMember, bool partialMatch = false, FilterDescriptor previous = null)
{
FilterDescriptor ret = null;
foreach (var filter in filters)
{
var descriptor = filter as FilterDescriptor;
if (descriptor != null
&& (descriptor.Member == findMember || (partialMatch == true && descriptor.Member.ToLower().Contains(findMember.ToLower())))
&& (previous == null || previous != descriptor))
{
ret = descriptor;
break;
}
else if (filter is CompositeFilterDescriptor)
{
ret = FindFirstFilterByMember(((CompositeFilterDescriptor)filter).FilterDescriptors, findMember, partialMatch, previous);
if (ret != null)
break;
}
}
return ret;
}
The second function is a recursive one for getting the filters by string.
The problem section is the "isNotEqualTo" comparison. I don't know how to save the composite filter into the existing request. It gives me a casting error.
Ok it turns out all i needed to do was create a new list of CompositeFilterDescriptor type and append that when necessary. The .add function took both. Sigh.
foreach (CompositeFilterDescriptor newCompositeDescriptor in newCompositeDescriptors)
{
request.Filters.Add(newCompositeDescriptor);
}
I am getting too many SOQL queries 201 error in apex class
I tried to check the no of queries within loop
Below is exact error -
11:48:43.9 (2785518121)|FATAL_ERROR|System.LimitException: Too many SOQL queries: 201
Class.GEN_CalculateActToWinScores.calcUserEligible: line 1343, column 1
Class.GEN_ActonFactsScoreUserEligibleBatch.execute: line 74, column 1
11:48:43.9 (2851114444)|CODE_UNIT_FINISHED|GEN_ActonFactsScoreUserEligibleBatch
11:48:43.9 (2852614277)|EXECUTION_FINISHED
Below is the code for method GEN_CalculateActToWinScores.calcUserEligible -
// Method for set user as ready for AoF
public static void calcUserEligible(List<User> usersList ){
List<Act_on_Facts__c> actOnFactDelete = new List<Act_on_Facts__c>();
Set<String> oppOpenStageNameSet = new Set<String>();
oppOpenStageNameSet.add(GEN_Constants.OPPORTUNITY_IDENTIFY);
oppOpenStageNameSet.add(GEN_Constants.OPPORTUNITY_QUALIFY);
oppOpenStageNameSet.add(GEN_Constants.OPPORTUNITY_PROPOSE);
oppOpenStageNameSet.add(GEN_Constants.OPPORTUNITY_NEGOTIATE);
Set<String> oppReadOnlyRecordTypeNameSet = new Set<String>();
oppReadOnlyRecordTypeNameSet.add(GEN_Constants.READONLY_SINGLE_ACCOUNT_OPP_RECORDTYPENAME);
oppReadOnlyRecordTypeNameSet.add(GEN_Constants.READONLY_WON_AND_DONE_OPP_RECORDTYPENAME);
oppReadOnlyRecordTypeNameSet.add(GEN_Constants.READONLY_CHILD_OPP_RECORDTYPENAME);
oppReadOnlyRecordTypeNameSet.add(GEN_Constants.READONLY_MULTI_ACCOUNT_OPP_RECORDTYPENAME);
if(usersList.size() > 0){
List<Id> idList = new List<Id>();
Integer countResult = 0;
List<User> newUserList = new List<User>();
Integer limitQuery; //10000 //2001
Integer limitResult; //2000
if(CSL_ActOnFactsLimits__c.getValues('QUERY_LIMIT') != null){
limitQuery = Integer.valueOf(CSL_ActOnFactsLimits__c.getValues('QUERY_LIMIT').Value__c);
}
if(CSL_ActOnFactsLimits__c.getValues('MAX_QUERY_RESULTS') != null){
limitResult = Integer.valueOf(CSL_ActOnFactsLimits__c.getValues('MAX_QUERY_RESULTS').Value__c);
}
Boolean eligible = true;
for (User userElement : usersList){
String logDetail = ' QUERY_LIMIT:limitQuery: '+limitQuery+' MAX_QUERY_RESULTS:limitResult: '+limitResult;
logDetail += ' userElement.id: '+userElement.id;
eligible = true;
CSH_ActOnFacts_UserEligible__c userEligible = null;
if(CSH_ActOnFacts_UserEligible__c.getValues(userElement.id) != null){
userEligible = CSH_ActOnFacts_UserEligible__c.getValues(userElement.id);
}
CSH_ActOnFacts_UserEligible__c profileEligible = null;
if(CSH_ActOnFacts_UserEligible__c.getValues(userElement.ProfileId) != null){
profileEligible = CSH_ActOnFacts_UserEligible__c.getValues(userElement.ProfileId);
}
List<Act_on_Facts__c> actonfacts = [select id from Act_on_Facts__c where Lookup_User__c = : userElement.id limit 1];
if(userElement.ManagerId == null){
userElement.Line_Manager_Optional__c = true;
userElement.Act_On_Facts_Manager_List__c = null;
}
if(userElement.Default_Macro_Segment__c == null){
userElement.Default_Macro_Segment__c = 'None';
}
if (userElement.IsActive == false){
eligible = false;
}else if(profileEligible != null || userEligible != null) {
eligible = false;
}else{
countResult = [select count() from Account WHERE OwnerId = :userElement.id LIMIT :limitQuery];
logDetail += ' Account.countResult: '+countResult;
if(countResult!=null && countResult > limitResult ){
eligible = false;
}
if(eligible){
if(oppOpenStageNameSet !=null && oppOpenStageNameSet.size()>0 && oppReadOnlyRecordTypeNameSet !=null && oppReadOnlyRecordTypeNameSet.size()>0){
countResult = [select count() from Opportunity WHERE OwnerId = :userElement.id AND StageName IN:oppOpenStageNameSet AND RecordType.DeveloperName NOT IN: oppReadOnlyRecordTypeNameSet LIMIT :limitQuery];
logDetail += ' Opportunity.countResult: '+countResult;
if(countResult!=null && countResult > limitResult ){
eligible = false;
}
}
}
}
if(!eligible){
userElement.Act_on_Facts_Eligible__c = false;
// remove user from Act on Facts
if(actonfacts != null && actonfacts.size()>0){
for(Act_on_Facts__c a : actonfacts){
actOnFactDelete.add(a);
}
}
}else{
userElement.Act_on_Facts_Eligible__c = true;
}
logDetail += ' userElement.Act_on_Facts_Eligible__c: '+userElement.Act_on_Facts_Eligible__c;
ApplicationLog.logEntry(ApplicationLog.SEVERITY_INFO, 'A2WBatch', 'GEN_ActonFactsScoreUserEligibleBatch:'+userElement.id+': ', logDetail);
newUserList.add(userElement);
}
try{
if(newUserList.size()>0){
update newUserList;
system.debug('HC Update- newUserList ' + newUserList);
}
if(actOnFactDelete.size()>0){
delete actOnFactDelete;
system.debug('HC Update- actOnFactDelete ' + actOnFactDelete);
}
}catch (Exception e){
system.debug('Error updating user ' + e);
}
}
}
Code for GEN_ActonFactsScoreUserEligibleBatch.execute-
global void execute(Database.BatchableContext BC, List<sObject> scope){
CSH_A2W_Settings__c a2wCS = CSH_A2W_Settings__c.getInstance();
if(scope != null){
List<User> userList = scope;
if(userList.size()>0){
if(CSL_ActOnFactsLimits__c.getValues('run_userTrigger') != null){
CSL_ActOnFactsLimits__c run_userTrigger = CSL_ActOnFactsLimits__c.getValues('run_userTrigger');
run_userTrigger.Value__c = 'false';
update run_userTrigger;
}
//GEN_CalculateActOnFactsScores.calcUserEligible(userList); //Commented as part of Decommission activity of AoF
if(a2wCS != null && a2wCS.Enabled_in_Batches__c == True){
GEN_CalculateActToWinScores.calcUserEligible(userList);
}
}
}
}
I am trying to analysis what will be the best possible ways to remove these errors or is there any alternate way to implement the same.
All these are queries in a loop:
for (User userElement : usersList){
...
List<Act_on_Facts__c> actonfacts = [select id from Act_on_Facts__c where Lookup_User__c = : userElement.id limit 1];
...
countResult = [select count() from Account WHERE OwnerId = :userElement.id LIMIT :limitQuery];
...
[select count() from Opportunity WHERE OwnerId = :userElement.id AND StageName IN:oppOpenStageNameSet AND RecordType.DeveloperName NOT IN: oppReadOnlyRecordTypeNameSet LIMIT :limitQuery];
As a very quick & dirty solution you can change the batch's size (how many records are passed to each execute). Default is 200.
Call your class with optional parameter Database.executeBatch(new GEN_ActonFactsScoreUserEligibleBatch(), 10); and see if it helps.
"Proper" fix would require some restructuring, taking queries out of the loop, maybe using some Maps where user's id is the key...
If these were custom objects a "pro" Apex developer would cheat, make these queries in one go, pulling user and related lists, something like
SELECT Id,
(SELECT Id FROM Accounts__r LIMIT 1),
(SELECT Id FROM Opportunities__r WHERE ... LIMIT 1),
(SELECT Id FROM Act_On_Facts__r)
FROM User
WHERE Id IN :scope
This won't work here because relation from Account to owner doesn't have a nice name ("Accounts" won't work). You should be still able to do it on the custom object (last subquery in my example, you can call it outside of the loop)
You might still be able to pull something like that off but it'd probably require looking at sharing-related tables... I'd say doable but if you have time to play with it. If you don't - change scope size and call it a day. Will execute bit longer but 1-liner fix is a win in my book.
I have one table which contains employees records like first , middle, last name. I have 3 textboxes for entering all these.
Now on .net side I want to write a single LINQ query to filter data based on first name middle name and last name. Any of these 3 fields can be blank.
Is these any way to write a single generic LINQ query?
public IList<Employee> GetEmployees(string first, string middle, string last)
{
var query = context.Employees.AsQueryable();
if (!string.IsNullOrWhiteSpace(first))
{
query = query.Where(x => x.FirstName == first);
}
if (!string.IsNullOrWhiteSpace(middle))
{
query = query.Where(x => x.MiddleName == middle);
}
if (!string.IsNullOrWhiteSpace(last))
{
query = query.Where(x => x.LastName == last);
}
return query.Select(x =>
new Employee
{
FullName = string.Join(" ", new string[] { x.FirstName, x.MiddleName, x.LastName}.Where(y => !string.IsNullOrWhiteSpace(y)))
})
.ToList();
}
You can write one linq query with or condition like bellow
_context.tablename.where(p=>p.firstName.contains(txtFirstName) || p.middleName.contains(txtMiddleName) || p.lastName.contains(txtLastName)).ToList();
Change tablename with you database table in above linq query
when iterating just verify if the criteria in use has value
var criteria = new
{
FirstName = default(string),
MiddleName = default(string),
LastName = "Doe",
};
var query = from record in Context()
where !string.IsNullOrEmpty(criteria.FirstName)
&& record.FirstName == criteria.FirstName
|| !string.IsNullOrEmpty(criteria.MiddleName)
&& record.MiddleName == criteria.MiddleName
|| !string.IsNullOrEmpty(criteria.LastName)
&& record.LastName == criteria.LastName
select record;
Try something like that;
public List<Employee> RetrieveEmployees(string firstName, string lastName, string middleName)
{
var query = from employees in context.Employees
where (string.IsNullOrEmpty(firstName) || employees.FirstName == firstName) &&
(string.IsNullOrEmpty(lastName) || employees.LastName == lastName) &&
(string.IsNullOrEmpty(middleName) || employees.MiddleName == middleName)
select employees;
return query.ToList();
}
How do I retrieve a collection of all the Information Flows Realized by a connector of type Dependency using Jscript please?
First option would be to use the API.
Loop the connectors and check EA.Connector.ConveyedItems
But that will be terribly slow for anything but a trivial model.
So the only sane way is to use EA.Repository.SQLQuery(string SQL) to get a list of connectorID's and then use EA.Repository.GetConnectorByID(int ID) to get the connector objects.
The SQL query you need is something in the nature of
select *
from ((((t_connector c
inner join t_xref x on (x.Client = c.ea_guid
and x.Name = 'MOFProps'
and x.Type = 'connector property'
and x.Behavior = 'abstraction'))
inner join t_connector cf on x.Description like '%' + cf.ea_guid + '%')
inner join t_xref xf on (xf.Client = cf.ea_guid
and xf.Name = 'MOFProps'
and xf.Type = 'connector property'
and xf.Behavior = 'conveyed'))
inner join t_object o on o.ea_guid like xf.Description)
where c.Connector_Type = 'Dependency'
Replace % with * in case you are working on a .eap (MS-Access) file.
I also have an implementation in C#. On class ConnectorWrapper there is this operation to get the information flows from the dependency.
/// <summary>
/// convenience method to return the information flows that realize this Relationship
/// </summary>
/// <returns>the information flows that realize this Relationship</returns>
public virtual HashSet<UML.InfomationFlows.InformationFlow> getInformationFlows()
{
HashSet<UML.InfomationFlows.InformationFlow> informationFlows = new HashSet<UML.InfomationFlows.InformationFlow>();
string sqlGetInformationFlowIDs = #"select x.description
from (t_connector c
inner join t_xref x on (x.client = c.ea_guid and x.Name = 'MOFProps'))
where c.ea_guid = '" + this.guid + "'";
var queryResult = this.model.SQLQuery(sqlGetInformationFlowIDs);
var descriptionNode = queryResult.SelectSingleNode(this.model.formatXPath("//description"));
if (descriptionNode != null)
{
foreach (string ifGUID in descriptionNode.InnerText.Split(','))
{
var informationFlow = this.model.getRelationByGUID(ifGUID) as UML.InfomationFlows.InformationFlow;
if (informationFlow != null )
{
informationFlows.Add(informationFlow);
}
}
}
return informationFlows;
}
Once you have the InformationFlow this code gets the conveyed items
public HashSet<UML.Classes.Kernel.Classifier> conveyed
{
get
{
if (_conveyed == null)
{
string getXrefDescription = #"select x.Description from t_xref x
where x.Name = 'MOFProps'
and x.Behavior = 'conveyed'
and x.client = '" + this.guid + "'";
//xrefdescription contains the GUID's of the conveyed elements comma separated
var xrefDescription = this.model.SQLQuery(getXrefDescription).SelectSingleNode(this.model.formatXPath("//Description"));
if (xrefDescription != null)
{
foreach (string conveyedGUID in xrefDescription.InnerText.Split(','))
{
var conveyedElement = this.model.getElementWrapperByGUID(conveyedGUID) as UML.Classes.Kernel.Classifier;
if (conveyedElement != null)
{
//initialize if needed
if (_conveyed == null)
{
_conveyed = new HashSet<UML.Classes.Kernel.Classifier>();
}
//add the element
_conveyed.Add(conveyedElement);
}
}
}
}
//nothing found, return empty list.
if (_conveyed == null)
{
_conveyed = new HashSet<UML.Classes.Kernel.Classifier>();
}
return _conveyed;
}
}