Seleccionar columnas usando QueryExpression
Importante
Le desaconsejamos encarecidamente devolver todas las columnas de una tabla con su consulta. Devolver todas las columnas hará que sus aplicaciones se ejecuten más lentamente y puede provocar errores de tiempo de espera. Debe especificar el número mínimo de columnas para recuperar con sus datos. Si establece la propiedad ColumnSet.AllColumns en verdadero, se devuelven datos para todas las columnas. Si no establece ninguna columna, solo se devuelve el valor de la clave principal del registro. Esto es lo opuesto al comportamiento que se produce al utilizar FetchXml, donde se devuelven todas las columnas si no se especifica ninguna.
Utilice la clase ColumnSet para especificar los nombres de las columnas que se devolverán con su consulta. Utilice el valor AttributeMetadata.LogicalName para cada columna. Los valores de nombres lógicos siempre están en minúsculas.
Puede especificar las columnas con el constructor ColumnSet(String[]) cuando inicializa QueryExpression
:
QueryExpression query = new("account")
{
ColumnSet = new ColumnSet("name", "accountclassificationcode", "createdby", "createdon")
};
Y puede utilizar los métodos ColumnSet.AddColumn o ColumnSet.AddColumns para agregar columnas adicionales a la propiedad QueryExpression.ColumnSet después de inicializar QueryExpression
.
QueryExpression query = new("account");
query.ColumnSet.AddColumn("name");
query.ColumnSet.AddColumns("accountclassificationcode", "createdby", "createdon");
Nota
Algunas columnas no son válidas para la lectura. La propiedad AttributeMetadata.IsValidForRead indica si una columna es válida para lectura. Si incluye los nombres de estas columnas, no se devuelve ningún valor.
La propiedad ColumnSet.Columns es Microsoft.Xrm.Sdk.DataCollection<cadena> que extiende System.Collections.ObjectModel.Collection<T>clase, por lo que también puede usar los métodos de esas clases de base para interactuar con las cadenas de la colección.
Seleccionar columnas para tablas unidas
Cuando Une tablas usando QueryExpression, usa la clase LinkEntity. La propiedad LinkEntity.Columns es un ColumnSet, por lo que definirá las columnas que se devolverán para las tablas unidas del mismo modo.
Clases de campo enlazadas anticipadamente
Si está utilizando clases de campo de enlace temprano generadas usando el comando pac modelbuilder con el interruptor emitfieldsclasses habilitado, puede usar las constantes generadas para todos los nombres de campos en lugar de usar los nombres lógicos directamente como cadenas.
QueryExpression query = new(Account.EntityLogicalName)
{
ColumnSet = new ColumnSet(
Account.Fields.Name,
Account.Fields.AccountClassificationCode,
Account.Fields.CreatedBy,
Account.Fields.CreatedOn)
};
Esto ayuda a evitar errores de ejecución debidos a escribir el nombre incorrecto. Más información sobre:
- Generación de clases enlazadas anticipadamente para el SDK para .NET
- Programación en tiempo de ejecución y en tiempo de compilación con SDK para .NET
Alias de columna
Los alias de columna se utilizan normalmente para operaciones agregadas, pero también funcionan para recuperar filas, por lo que podemos presentarlos aquí.
Agregue instancias XrmAttributeExpression a la colección ColumnSet.AttributeExpressions para especificar un nombre de columna único para los resultados devueltos. Para cada instancia, establezca estas propiedades:
Property | Description |
---|---|
AttributeName | El nombre lógico de la columna |
Alias | Un nombre único para que la columna aparezca en los resultados |
AggregateType | Cuando no agregue datos, utilice el miembro XrmAggregateType.None . Este es el valor predeterminado, por lo que no es necesario configurarlo si no utiliza la agregación. Obtenga más información sobre cómo agregar datos con QueryExpression |
Cada columna devuelta debe tener un nombre único. De forma predeterminada, los nombres de las columnas devueltas para la tabla de su consulta son los valores de la columna LogicalName
. Todos los nombres lógicos de las columnas son únicos para cada tabla, por lo que no puede haber nombres duplicados dentro de ese conjunto.
Cuando usa LinkEntity para unir tablas, puede configurar la propiedad EntityAlias para el LinkEntity
que representa la tabla unida. Los nombres de las columnas en la propiedad LinkEntity.Columns siga esta convención de nomenclatura: {Linked table LogicalName or alias}.{Column LogicalName}
. Esto evita que se dupliquen los nombres de las columnas.
Sin embargo, cuando especifica un alias de columna usando la propiedad XrmAttributeExpression.Alias, el LinkEntity.EntityAlias
o el valor del nombre lógico de la tabla no se antepone al valor del alias. Debe asegurarse de que el valor del alias sea único. Si el valor no es único, puede esperar este error:
Nombre:
QueryBuilderDuplicateAlias
Código:0x80041130
Número:-2147217104
Mensaje:< alias value > is not a unique alias. It clashes with an autogenerated alias or user provided alias
Ejemplo de alias de columna
Este método de ejemplo SimpleAliasOutput
utiliza alias y nombres lógicos de las columnas. Debido a esto, los resultados que utilizan alias se devuelven como AliasedValue. Para obtener acceso al valor de tipos como OptionSetValue o EntityReference, tiene que emitir el valor.
En este ejemplo, solo se especifican alias para las columnas accountclassificationcode
, createdby
y createdon
. La columna name
no utiliza un alias. Este método depende del paquete ConsoleTables NuGet para representar la tabla.
/// <summary>
/// Output the entity attribute values with aliases
/// </summary>
/// <param name="service">The authenticated IOrganizationService instance</param>
static void SimpleAliasOutput(IOrganizationService service)
{
QueryExpression query = new("account")
{
TopCount = 3,
ColumnSet = new ColumnSet("name")
{
AttributeExpressions = {
new XrmAttributeExpression{
AttributeName = "accountclassificationcode",
Alias = "classificationcode"
},
new XrmAttributeExpression{
AttributeName = "createdby",
Alias = "whocreated"
},
new XrmAttributeExpression{
AttributeName = "createdon",
Alias = "whencreated"
}
}
}
};
//Retrieve the data
EntityCollection entityCollection = service.RetrieveMultiple(query: query);
var table = new ConsoleTables.ConsoleTable("classificationcode", "whocreated", "whencreated", "name");
foreach (var entity in entityCollection.Entities)
{
var code = ((OptionSetValue)entity.GetAttributeValue<AliasedValue>("classificationcode").Value).Value;
var whocreated = ((EntityReference)entity.GetAttributeValue<AliasedValue>("whocreated").Value).Name;
var whencreated = entity.GetAttributeValue<AliasedValue>("whencreated").Value;
var companyname = entity.GetAttributeValue<string>("name");
table.AddRow(code, whocreated, whencreated, companyname);
}
table.Write();
}
Salida:
----------------------------------------------------------------------------------
| code | whocreated | whencreated | companyname |
----------------------------------------------------------------------------------
| 1 | FirstName LastName | 8/13/2023 10:30:08 PM | Fourth Coffee (sample) |
----------------------------------------------------------------------------------
| 1 | FirstName LastName | 8/13/2023 10:30:10 PM | Litware, Inc. (sample) |
----------------------------------------------------------------------------------
| 1 | FirstName LastName | 8/13/2023 10:30:10 PM | Adventure Works (sample) |
----------------------------------------------------------------------------------
La clase AliasedValue tiene dos propiedades que indican los EntityLogicalName y AttributeLogicalName originales si los necesita.
Ejemplo de valores formateados y con alias
Las columnas que utilizan un alias devuelven un AliasedValue. Como se explica en Acceder a valores formateados, para algunos tipos de columnas, los valores de cadena formateados también se devuelven utilizando la colección Entity.FormattedValues para proporcionar valores de cadena adecuados para mostrar en una aplicación.
El siguiente método de ejemplo estático OutputQueryExpression
muestra cómo extraer valores de cadena para cada fila de datos. Esta función utiliza los datos QueryExpression.ColumnSet
para saber qué columnas se solicitan y luego procesa los resultados para encontrar la mejor manera de mostrar los datos del registro en una aplicación, en este caso, una aplicación de consola que utiliza el paquete ConsoleTables NuGet para representar una tabla.
/// <summary>
/// Renders the output of a query in a table for a console application
/// </summary>
/// <param name="service">The authenticated IOrganizationService instance to use.</param>
/// <param name="query">The query to use</param>
/// <exception cref="Exception">
/// OutputQueryExpression requires all LinkEntity instances that contain columns specify an EntityAlias property.
/// </exception>
static void OutputQueryExpression(IOrganizationService service, QueryExpression query)
{
//Retrieve the data
EntityCollection entityCollection = service.RetrieveMultiple(query: query);
var columns = GetQueryExpressionColumns(query);
// Create the table using https://www.nuget.org/packages/ConsoleTables/2.5.0
var table = new ConsoleTables.ConsoleTable(columns.ToArray());
// Add the rows of the table
entityCollection.Entities.ToList().ForEach(entity =>
{
table.Rows.Add(GetRowValues(columns, entity).ToArray());
});
// Write the table to the console
table.Write();
List<string> GetQueryExpressionColumns(QueryExpression query)
{
List<string> columns = new();
columns.AddRange(GetColumns(query.ColumnSet));
foreach (LinkEntity linkEntity in query.LinkEntities)
{
columns.AddRange(GetLinkEntityColumns(linkEntity));
}
return columns;
}
List<string> GetLinkEntityColumns(LinkEntity linkEntity)
{
if (string.IsNullOrWhiteSpace(linkEntity.EntityAlias))
{
if (linkEntity.Columns.Columns.Count != 0)
{
string message = "OutputQueryExpression method requires all ";
message += "LinkEntity instances that contain columns ";
message += "specify an EntityAlias property.";
throw new Exception(message);
}
}
List<string> columns = new();
columns.AddRange(GetColumns(linkEntity.Columns, linkEntity.EntityAlias));
foreach (LinkEntity le in linkEntity.LinkEntities)
{
columns.AddRange(GetColumns(le.Columns, le.EntityAlias));
}
return columns;
}
List<string> GetColumns(ColumnSet columnset, string alias = null)
{
List<string> columns = new();
foreach (string column in columnset.Columns)
{
columns.Add(string.IsNullOrWhiteSpace(alias) ? column : $"{alias}.{column}");
}
foreach (XrmAttributeExpression item in columnset.AttributeExpressions)
{
columns.Add(item.Alias ?? item.AttributeName);
}
return columns;
}
List<string> GetRowValues(List<string> columns, Entity entity)
{
List<string> values = new();
columns.ForEach(column =>
{
if (entity.Attributes.ContainsKey(column))
{
// Use the formatted value if it available
if (entity.FormattedValues.ContainsKey(column))
{
values.Add($"{entity.FormattedValues[column]}");
}
else
{
// When an alias is used, the Aliased value must be converted
if (entity.Attributes[column] is AliasedValue aliasedValue)
{
values.Add($"{aliasedValue.Value}");
}
else
{
// Use the simple attribute value
values.Add($"{entity.Attributes[column]}");
}
}
}
// Null values are not in the Attributes collection
else
{
values.Add("NULL");
}
});
return values;
}
}
Puede utilizar esta función para mostrar el resultado de cualquier consulta de QueryExpression
con el único requisito de que cualquier LinkEntity utilizada para unir tablas especifique un alias. Por ejemplo, la siguiente consulta incluye valores formateados y con alias con una tabla unida:
static void OutputQueryExpressionExample(IOrganizationService service)
{
// Specify a query:
QueryExpression query = new("account")
{
TopCount = 3,
ColumnSet = new ColumnSet("name")
{
AttributeExpressions = {
new XrmAttributeExpression{
AttributeName = "accountclassificationcode",
Alias = "classificationcode"
}
}
},
LinkEntities = {
new LinkEntity()
{
LinkFromEntityName = "account",
LinkToEntityName = "contact",
LinkFromAttributeName = "primarycontactid",
LinkToAttributeName = "contactid",
JoinOperator = JoinOperator.Inner,
EntityAlias = "person",
Columns = new ColumnSet("fullname"){
AttributeExpressions = {
new XrmAttributeExpression{
AttributeName = "accountrolecode",
Alias = "role"
}
}
}
}
}
};
// Use OutputQueryExpression
OutputQueryExpression(service, query);
}
Los resultados de esta consulta podrían verse así:
----------------------------------------------------------------------------
| name | classificationcode | person.fullname | role |
----------------------------------------------------------------------------
| Fourth Coffee | Large | Susie Curtis | Influencer |
----------------------------------------------------------------------------
| Litware, Inc. | Medium | Adele Vance | Decision Maker |
----------------------------------------------------------------------------
| Adventure Works | Small | Rafel Shillo | Employee |
----------------------------------------------------------------------------
Pasos siguientes
Más información sobre cómo unir tablas.