Introducción
Si se quiere trabajar con mapas es imprescindible contar con la correcta resolución del posicionamiento de las entidades, la idea es actualizar los campos latitud y longitud de la entidad cuenta
Nota:Esta vista que estas observando la cree para poder tener la info de la latitud y longitud listado a simple vista
En este caso la actualización será masiva a un grupo de cuentas, la ejecución se realizara desde un test haciendo uso de una consulta fetchxml para resolver las cuentas que se quieren actualizar
Esta misma técnica se podría aplicar para crear un plug-in que se adjunte a los campos de dirección de la cuenta, ante el cambio de este campo se lanzaría la operación de actualización del posicionamiento global.
Obtener información de las cuentas
Haremos uso de las librerías de CRM SDK para poder recuperar las cuentas según la query definida en el fetchxml proporcionado
El primer paso será agregar la referencia a las librerías del SDK
En el archivo de configuración se debe definir la url al sitio del CRM que se este utilizando
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="CRMServer" connectionString="Url=http://localhost:5555/ContosoHQ;"/> </connectionStrings> <system.diagnostics> <trace autoflush="true" indentsize="4"> <listeners> <remove name="Default" /> <add name="myListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="TraceLog.log" /> </listeners> </trace> </system.diagnostics> </configuration>
Aquí se define tanto la conexión como la línea que permite definir el trace
En la siguiente imagen se puede observar la utilización la definición de la conexión en la clase que proporciona el SDK
Nota: en este caso desarrolle dentro del propio equipo donde tenia instalado el servidor de CRM por eso utilice localhost, pero es lógico que esto deba cambiarse si se accede de forma remota
El siguiente código permite recuperar la información de las cuentas:
public static List<AccountCRM> GetEntityMap(string fetchxml) { List<AccountCRM> entityList = new List<AccountCRM>(); try { using (var service = new OrganizationService("CRMServer")) { EntityCollection entityCol = service.RetrieveMultiple(new FetchExpression(fetchxml)); foreach (Entity entity in entityCol.Entities) { AccountCRM account = new AccountCRM() { id = (Guid)entity.Attributes["accountid"], razonsocial = entity.Attributes["name"].ToString(), address = entity.Attributes["address1_line1"].ToString(), stateorprovince = entity.Attributes["address1_stateorprovince"].ToString(), country = entity.Attributes["address1_country"].ToString(), }; if (entity.Attributes.ContainsKey("address1_latitude") && entity.Attributes.ContainsKey("address1_longitude")) { account.Position = new GeoPosition() { Latitude = Convert.ToDouble(entity.Attributes["address1_latitude"]), Longitude = Convert.ToDouble(entity.Attributes["address1_longitude"]), }; } entityList.Add(account); } return entityList; } } catch (Exception ex) { Trace.WriteLine(string.Format("[{0:dd-MM-yyyy HH:mm}] Message:{1}, StackTrace: {2}", DateTime.Now, ex.Message, ex.StackTrace)); throw; } }
Se utilizo el siguiente fetchxml para recuperar las cuentas que nos interesa actualizar
string fetch = @"<fetch version='1.0' count='50' output-format='xml-platform' mapping='logical' distinct='false'> <entity name='account'> <attribute name='accountid' /> <attribute name='name' /> <attribute name='address1_city' /> <attribute name='address1_stateorprovince' /> <attribute name='address1_line1' /> <attribute name='address1_country' /> <attribute name='address1_longitude' /> <attribute name='address1_latitude' /> <filter type='and'> <condition attribute='statecode' operator='eq' value='0' /> <condition attribute='address1_city' value='Buenos Aires' operator='eq'/> </filter> </entity> </fetch>";
Por supuesto esto es completamente re-definible solo se usa la opción:
La búsqueda avanzada abre el dialogo que permite definir los filtros que crean la consulta fetchxml
Resolver Geo Localización de las cuentas
Para obtener el posicionamiento global de las cuentas según su dirección haremos uso de google maps
public static GeoPosition GetGeoPosition(AccountCRM account) { const string _googleUri = "http://maps.googleapis.com/maps/api/geocode/xml?"; try { if (string.IsNullOrEmpty(account.address) || string.IsNullOrEmpty(account.stateorprovince) || string.IsNullOrEmpty(account.country)) return null; string url = string.Format("{0}address={1}&components=locality:{2}|country:{3}&sensor=false" , _googleUri , HttpUtility.UrlEncode(account.address) , account.stateorprovince , account.country); WebClient client = new WebClient(); Uri uri = new Uri(url); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); XmlDocument doc = new XmlDocument(); doc.Load(request.GetResponse().GetResponseStream()); XmlNode root = doc.DocumentElement; if (root.SelectSingleNode("/GeocodeResponse/status").InnerText == "OK") { return new GeoPosition() { Latitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText), Longitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lng").InnerText) }; } else return null; } catch (Exception ex) { Trace.WriteLine(string.Format("[{0:dd-MM-yyyy HH:mm}] Message:{1}, StackTrace: {2}", DateTime.Now, ex.Message, ex.StackTrace)); throw; } }
Solo es cuestión de definir la url de google maps con los valores de dirección, localidad y país, lanzar la ejecución mediante WebClient y procesar la respuesta.
El ultimo paso consiste en actualizar la entidad en CRM
public static void UpdatePosition(string fetchxml) { System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); //se obtiene la lista de cuentas List<AccountCRM> accountList = GetEntityMap(fetchxml); using (var service = new OrganizationService("CRMServer")) { //se recorre cada cuenta para actualizar el posicionamiento foreach (var item in accountList) { //se recupera las coordenadas de posicionamiento GeoPosition position = MapHelper.GetGeoPosition(item); //se crea la entidad account //a la cual se le actualizara la info de posicionamiento var entity = new Entity("account"); entity["accountid"] = item.id; if (position == null) { entity["address1_latitude"] = null; entity["address1_longitude"] = null; } else { entity["address1_latitude"] = position.Latitude; entity["address1_longitude"] = position.Longitude; } service.Update(entity); } } }
En la primer línea se define la cultura en en-US para evitar problemas de conversión de tipos cuando se recupera las posiciones de geo localización
Una vez que se recuperan las cuentas se recorren para resolver la posición según al dirección que tengan asignada, según la respuesta de google maps se asignara las coordenadas de localización o en caso de no poder resolver se asigna null.
Código
Leandro, excelente artículo. Muchas gracias.
ResponderEliminarMe encanta ver contenidos de Dynamics CRM en blogs no específicos de CRM!
Un abrazo!
Hola. Estoy creando en C# un html para enviar al correo de hotmail p gmail. ENtonces estoy creando una simpre tabla de html :
ResponderEliminarimg src=\"" + "http://salud_2013.salud.gob.mx/images/mpr.png"
algo asi como esto, el problema es que cuando lleha el correo no llega la propidad src del img. SAludos
hola Josengan
ResponderEliminarcuando defines el mail asignas la propiedad
IsBodyHtml
porque sino no interpretara el body como html
saludos
Hola, soy nuevo en esto de visual stufioy c#, me gustaria saber como se consume este ejemplo, desde un proyecto web, desde consola, etc.
ResponderEliminarhola Hermes HR
ResponderEliminarse consume que cosa ?
no entendi el planteo que realizas en el contexto del articulo del blog en que lo haces
saludos
Lo que pasa es que tengo una base de datos con latitud, longitud y mas datos. y no tengo ni la menor idea de como mostrar los puntos en un mapa de google, espero tu ayuda por favor.
ResponderEliminarde antemano, gracias.
hola
ResponderEliminarpero si la idea es mostrar en el mapa deberias analiza el otro articulo:
[Dynamic CRM] Integrar Google Maps – PopUp Mapa Web (1/2)
alli ve al javascript que utilizo, donde defino:
google.maps.Marker()
se espcifica el posicionamiento de cada punto
saludos
Hola Leandro, primero que todo muchas gracias por toda la ayuda que tus publicaciones entregan. Ahora a lo que vinimos. Tengo un problema al intentar crear una solucion web con MVC y EF6, la cosa es que tengo una entidad con una propiedad definida como DbGeography y por el lado en la vista no logro asignar correctamente los valores de longitud y latitud, para entregarlo al controlador y poder hacer la grabacion en mi base de datos.
ResponderEliminar¿Cual seria la forma correcta de asignar en mi vista los datos de ubicacion obtenidos en la geolocalizacion?
Muchas gracias por la ayuda
hola
EliminarComo defines en la View los @Html.TextBoxFor() para mapear la propiedad del model. Esa parte es clave para que al realizar el post al action reciba los valores.
Si necesitas publicar codigo sugiero que plantes el problema en el foro de msdn
ASP.NET MVC foro
En este es mas simple publicar codigo
saludos
Leandro, muchas gracias por la respuesta finalmente lo que hice fue crear propiedades distintas para la longitud y la latitud que las muestro en la vista, excluyendo las propiedades dbgeography, ya en el controlador convierto las coordenadas al formato geography y las grabo sin necesidad de tener que presentar en la vista todas las caracteristicas de este tipo de dato.
EliminarSalu2
Hola Leandro la verdad muchas gracias por tus publicaciones tan claras y profesionales de este tema.
ResponderEliminarMi pregunta es: hay alguna conexión entre visual studio 2010 los SDK Y CRM Dynamic
Ocea podemos ver datos de las tablas del crm desde visual studio ?
desde ya muchas gracias