Showing posts with label Beta. Show all posts
Showing posts with label Beta. Show all posts

Monday, May 14, 2012

Silverlight: Binding Resource to DataGridTextColumn.Header

In the last two days, since it’s the end of our block I have decided to do some minor work (that way a check in won’t really effect the block). One of the changes I thought of was replacing all the free text in our Silverlight application with text from a resource file.

Everything worked like a charm except for the Header of DataGridTextColumn. My original Data Binding looked like this:

  1. <slData:DataGridTextColumn Header="{Binding Path=Type, Source={StaticResource StringsRes}}" Binding="{Binding Title}"/>

But the DataGrid columns in the application looked like this:

DataGrid-header-column-bug

In the end I found the solution here:

  1. <slData:DataGridTextColumn Binding="{Binding TypeString}" >
  2.     <sdk:DataGridTextColumn.HeaderStyle>
  3.         <Style TargetType="primitives:DataGridColumnHeader" >
  4.             <Setter Property="ContentTemplate">
  5.                 <Setter.Value>
  6.                     <DataTemplate>
  7.                         <TextBlock Text="{Binding Path=Type, Source={StaticResource StringsRes}}"/>
  8.                     </DataTemplate>
  9.                 </Setter.Value>
  10.             </Setter>
  11.         </Style>
  12.     </sdk:DataGridTextColumn.HeaderStyle>
  13. </slData:DataGridTextColumn>

Though I am still searching for a generic solution and not something I have to copy paste for each DataGrid Column…

 

//TODO: Check if this was fixed in SL5. If so post the elegant solution

IceRocket Tags:

Tuesday, May 1, 2012

Polyline wrong coordinates

We have been developing for a long time on a single oracle DB Server (Development stage) and recently added a new integration server. Because of time constraints we decided to just copy the data from the Dev server using SQL (out DBA did that part), and since we moved to SDO_GEOMETRY the data can be transferred without the use of ArcDesktop utilities.

So the data was moved correctly and the registration went without a glitch. But when we tried to create a new line in the layer its coordinates moved from (35.5, 32.7) (in x,y) to (–324.4, 32.7). The first thing I checked was my code but there were no changes done and in the old environment everything worked fine.

Looking at the Layers table in the SDE schema that table looked like:

EFLAGS

LAYER_MASK

GSIZE1

GSIZE2

GSIZE3

MINX

MINY

MAXX

MAXY

LAYER_CONFIG

138674317

128

-2

0 0

-400

-400

471.3916

471.3916

SDO_GEOMETRY

My first thought was that the –400 somehow affected the data being inserted but the same values were in the old DB and they worked.

After many actions of unregister and register of that layer the problem was solved.

 

TODO: Add table border, it looks bad in the site

Friday, February 11, 2011

Silverlight Memory Leak, Part 4

Check out part 1, part 2 and part 3 (TODO).

I think it was back in 2007 that I did a presentation using CLR Profiler. I actually learned a lot from that presentation because of how difficult to understand that little program was I had to dig into CLR via C# just to understand the basic concepts. At a job interview 6 months ago I was asked a lot about CLR memory issues and I was amazed how much I actually remembered. I am kinda sorry I lost that presentation…

I wanted to see how difficult using it will be in Silverlight, since I read in David Bronman’s blog that you can now “Can target Silverlight 4 web apps”.

 

In my first try I choose target CLR Version of “V4 Core CLR”:

CLRProfiler-silverlight-clr

When the application was up I clicked on “Show Heap now” (the bottom button on the left). But the more interesting window is View->Summery:

CLRProfiler-wrong-summery

The final Heap Bytes looked just wrong, because:

CLRProfiler-task-manager-first-trial

I thought the profiler was attached to the wrong process, but…

 

I tried using target CLR Version of “V4 Desktop CLR” and used Attach Process:

clrprofiler-wrong-target-clr

Text format:

No CLR Version v4. is loaded into the target process.

And I also tried to trick the profiler and set Firefox as my default browser. But it didn’t work Internet Explorer just popped up…

(choosing “V4 Desktop CLR” and Start URL just points you to “V4 Core CLR” for Silverlight profiling)

 

So I went back to my first thought and this time I actually looked into the graph of “Show Heap now”:

clrprofiler-found-mappoint

(If you can see there is a MapPoint there – ESRI Silverlight object that I use)

Then I thought it will be nice to check for ultra black light by running into a wall (I read it somewhere but can’t remember where (Scifi book?)) because I forgot: CLR Profiler => CLR => Managed memory != Task manager memory. So I opened VMMap to look at the managed memory and it was 17MB – where is a wall when you need one?!?

I am confused… Allocated bytes should have at least shown 17MB, no?

clrprofiler-before-closing-IE

Then I just decided to go home (it was around 10PM) so I closed the IE (every other time I used CLR Profiler “Kill Application” button) and this window popped up:

clrprofiler-after-closing-ie

Why me? Home looks so far away right now…

It seems like CLR Profiler found it’s lost memory but only when I terminated the application outside of the CLR Profiler scope. I posted a comment on this in “Building Development and Diagnostic Tools for .Net” forum but still have no answer…

The application show almost everything in flow charts and it hasn’t changed in the last 6 year (at least). Back when I did my presentation I said that though the other programs are more user friendly and cost accordingly, CLR Profiler has the potential to be just as good but the application is just not quite ready for a regular user.

It has the potential because it has all the relevant CLR memory data stored in relatively small files (profiling my map application took 65MB where Ants took more than 1GB) and the source code is included (I believe the EULA states you can do what ever you want with the code, but check it out (there is also a forum post on this EULA)).

The application gives almost all the functionality of Ants but the learning curve is much steeper (unlike many applications you can’t use this program as a novice to find real memory leaks – novices always show an example of running this program with a loop and one class being recreated and claim the program is easy…).

 

My purpose here is just to give you some general lines of how to start with the application. For more information you should read the Word document included in the installation of the application.

So where do I start? Not in the Heap Graph that is opened automatically:

CLR-profiler-heap-graph-unreadable

Can anyone really see what is happening here? and that is with Detail=1 in Detail=0(everything) the lines look like a cat played with them – and the cat won! With Scale=10 and Detail=0:

clr-profiler-totally-unreadable

In a demo app this would have looked like one line going from left (origin usually the main window) to the right (the actual object in memory).

 

So where should you start from? I find the summery page to be quite organized with actual numbers (no graphs):

clr-profiler-heap-statistics

What to look at?

  • Allocated bytes is a waste of time, it will give you a look of what objects the application created but most of them were freed…
  • Relocated bytes is more interesting but still not quite there, it gives you a look into objects that the GC moved –> the long lived objects
  • Final Heap bytes is the gold mine, it gives you a look at the objects that were not freed. It is especially a good place if the profile was of a closing application – those are the items that are still there (without a driver on the wheel)…
  • Object finalized (critical or regular) in my application were only Silverlight Framework objects and most of them were critical

So looking at Final Heap bytes there are three buttons:

clr-profiler-Final-Heap-bytes-buttons

Objects by address is a waste of time – look in VMMap, it is much more readable there.

Histogram by Age is useful but for a clear picture you will have to play a lot with the scales.

Histogram give you a nice clear picture from the start:

clr-profiler-histogram

Right clicking on an element in the graph and choose “Show Who Allocated”:

clr-profiler-histogram-right-click

This will open up the Allocation Graph filtered by the object you selected:

clr-profiler-filtered-allocation-graph

(it is still a lot of data though so play a bit with the detail scale)

Sometimes the filtering doesn’t work so reach the end of the graph (right most) and select the object (in my case EventHandler) right click on it and  select “Filter to callers & callees” (is that a typo?):

clr-profiler-allocation-graph-right-click

And the graph will be less problematic:

clr-profiler-less-lines

Now it is more like the novices demos, a single line where you just have to search for the root cause of the memory. In this case the root cause of the memory is ESRI’s method to GetGeometry method:

ESRI.ArcGIS.Client.Tasks.Utils.JSON.ArcGISJsonReader::GetGeometry static ESRI.ArcGIS.Client.Geometry.Geometry (<UNKNOWN>  bool):     325 kB    (90.95%)

(the application supports copying the data to the clipboard)

This is not necessarily an example of a memory leak, I just choose this in random… (though it is curious the memory was still in the final heap when I closed the IE (but maybe it was just on the way to be cleared, from the Word guide on Final Heap bytes: “This may include some objects that are no longer referenced, but that have not yet been cleaned up by the garbage collector.”))

 

And some things will still seem off:

clr-profiler-unknown-double-click

(seems to be something of the Silverlight Framework (you can just see the Xaml on the right))

 

So to conclude, the CLR Profiler is a free profiler with open source (though the code is not very nice). I think the Profiler should be separated into a CLR Profiler and a CLR Profile Viewer. The Profiler should just create log files and the Viewer will read that log file and give a more simple, user friendly view of the data. That way the Viewer could still work with existing CLR Profiler logs. I also believe it should be a free community product (open source) with the support of Microsoft.

 

TODO: might be a good idea to move this post before part 3 (maybe use another program for part 3?)

 

Resources:

CLRProfiler V4 Released

 

Keywords: Silverlight, memory, CLRProfiler

Monday, January 10, 2011

ArcSDE–Adding domains from code

(The images for this post were taken from the ESRI site because I no longer use the ArcDesktop applications)

In our project we are using Domains to display text instead of the codes written in our tables.

Well first what are Domains?

I always found Domains and Subtypes very similar. They are both used to bring description to a field in a feature. The difference I guess is that Subtypes also separate the features to groups whereas Domains only give text value.

According to ESRI domains are used as validators for the data inserted using ArcMap/ArcCatalog. The trouble with that is that in all my systems so far we didn’t insert the feature data by those programs – we did it all by code! And at least in 9.3.1 there were no validations on the field…

 

(from ESRI site using domain coded values)

Whereas according to ESRI subtypes allow much more. They increase performance, they create all kinds of rules and they allow the applying of domains on the fields.

How do I create domains regularly (in ArcCatalog)?

In ArcCatalog right click on the Geodatabase –> Properties –> Domains tab:

 

(Taken from the ESRI site)

On the first empty line in the top table add your domain name and description.

On the next table enter the Domain Type: Coded Values, Range

If you choose Range you will have to enter a minimum and maximum values

If you choose Coded Values you will have to enter in the bottom table the codes of the domain and their description. We use Coded Values domains.

How do I associate a field with a domain(in ArcCatalog)?

In ArcCatalog right click on a table/layer –> Properties –> Subtypes:

(Taken from the ESRI site)

On the bottom table click on the field you want to add the domain to and select the domain from the combo box.

How do I create domains by C# code?

You can read more about it here.

  1. #region Create/Delete Domains
  2.  
  3. public void CreateDomain(string name, esriFieldType type, List<KeyValuePair<object ,string >> domains)
  4. {
  5.     ICodedValueDomain codedValueDomain = new CodedValueDomainClass();
  6.  
  7.     foreach (var pair in domains)
  8.     {
  9.         codedValueDomain.AddCode(pair.Key, pair.Value);
  10.     }
  11.  
  12.     // The code to set the common properties for the new coded value domain.
  13.     var domain = (IDomain)codedValueDomain;
  14.     domain.Name = name;
  15.     domain.FieldType = type;
  16.     domain.SplitPolicy = esriSplitPolicyType.esriSPTDuplicate;
  17.     domain.MergePolicy = esriMergePolicyType.esriMPTDefaultValue;
  18.  
  19.     ((IWorkspaceDomains) _workspace).AddDomain(domain);
  20. }
  21.  
  22. public void DeleteDomain(string name)
  23. {
  24.     ((IWorkspaceDomains)_workspace).DeleteDomain(name);
  25. }
  26.  
  27. //For tests
  28. internal IDomain GetDomain(string name)
  29. {
  30.     return ((IWorkspaceDomains)_workspace).DomainByName[name];
  31. }
  32.  
  33. #endregion

The code uses an inner variable _workspace which is the current IWorkspace.

Weird things (found while unit testing):

1. You can create a domain with a name of “” or null but they will have the same name (later in my code I added a validation for the name field so that it has to have a value).

2. You can create a domain without any coded values (empty list not null list!)

3. You cannot create two domains with the same name, an exception will be thrown: COMException: Domain name already in use.

4. You cannot delete a domain with a name of “” or null – an exception is thrown that is NOT COMException - System.ArgumentException: Invalid function arguments – from the DeleteDomain method(my DB is doomed…). I manually deleted it from the database it was the only line in GDB_ITEMS where Name,PhysicalName,Path and Url were empty string (and just because its good to know – the type for domains is ‘8C368B12-A12E-4C7E-9638-C9C64E69E98F’).

How do I use a domain in a feature field by C# code?

You can read more about it here.

  1. #region Assign Domains
  2.  
  3. public void AssignDomain(string layerName, string fieldName, string domainName)
  4. {
  5.     var featureClass = GetFeatureClass(layerName);
  6.     var domain = ((IWorkspaceDomains) _workspace).DomainByName[domainName];
  7.     var field = featureClass.GetField(fieldName);
  8.  
  9.     if (field.Type != domain.FieldType)
  10.         throw new ApplicationException(
  11.             "The field and the domain are not of the same type. Cannot assign domain to field.");
  12.  
  13.     var schemaLock = (ISchemaLock)featureClass;
  14.     try
  15.     {
  16.         schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
  17.         ((IClassSchemaEdit)featureClass).AlterDomain(fieldName, domain);
  18.  
  19.     }
  20.     finally
  21.     {
  22.         schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
  23.     }
  24. }
  25.  
  26. #endregion

Weird things (found while unit testing):

1. If you try to delete a domain that is in use it will throw a COMException: The domain is used as a default domain

2. To remove a domain simply do it with null -  AlterDomain(fieldName, null) . I added a method called RemoveDomain that does that.

In GDB_ITEMS a field in a feature class that uses domain looks like:

  1. <GPFieldInfoEx xsi:type="typens:GPFieldInfoEx">
  2.   <Name>Type</Name>
  3.   <AliasName>Type</AliasName>
  4.   <ModelName>Type</ModelName>
  5.   <DomainName>DefaultDomain</DomainName>
  6.   <FieldType>esriFieldTypeSmallInteger</FieldType>
  7.   <IsNullable>false</IsNullable>
  8. </GPFieldInfoEx>

And a field in a feature class that doesn’t use domain looks like:

  1. <GPFieldInfoEx xsi:type="typens:GPFieldInfoEx">
  2.   <Name>Type</Name>
  3.   <AliasName>Type</AliasName>
  4.   <ModelName>Type</ModelName>
  5.   <FieldType>esriFieldTypeSmallInteger</FieldType>
  6.   <IsNullable>false</IsNullable>
  7. </GPFieldInfoEx>

 

The end result was this:

  1. #region Create/Delete Domains
  2.  
  3. /// <summary>
  4. /// Create a domain in the DB
  5. /// </summary>
  6. /// <param name="name"></param>
  7. /// <param name="type"></param>
  8. /// <param name="domains"></param>
  9. public void CreateDomain(string name, esriFieldType type, List<KeyValuePair<object ,string >> domains)
  10. {
  11.     if (String.IsNullOrEmpty(name))
  12.         throw new ArgumentException("Parameter name cannot be null or empty.", "name");
  13.  
  14.     ICodedValueDomain codedValueDomain = new CodedValueDomainClass();
  15.  
  16.     foreach (var pair in domains)
  17.     {
  18.         codedValueDomain.AddCode(pair.Key, pair.Value);
  19.     }
  20.  
  21.     // The code to set the common properties for the new coded value domain.
  22.     var domain = (IDomain)codedValueDomain;
  23.     domain.Name = name;
  24.     domain.FieldType = type;
  25.     domain.SplitPolicy = esriSplitPolicyType.esriSPTDuplicate;
  26.     domain.MergePolicy = esriMergePolicyType.esriMPTDefaultValue;
  27.  
  28.     ((IWorkspaceDomains) _workspace).AddDomain(domain);
  29. }
  30.  
  31.  
  32. /// <summary>
  33. /// Delete a domain from the DB
  34. /// </summary>
  35. /// <param name="name"></param>
  36. public void DeleteDomain(string name)
  37. {
  38.     if (String.IsNullOrEmpty(name))
  39.         throw new ArgumentException("Parameter name cannot be null or empty.", "name");
  40.  
  41.     ((IWorkspaceDomains)_workspace).DeleteDomain(name);
  42. }
  43.  
  44. //For tests
  45. internal IDomain GetDomain(string name)
  46. {
  47.     return ((IWorkspaceDomains)_workspace).DomainByName[name];
  48. }
  49.  
  50. #endregion
  51.  
  52. #region Assign Domains
  53.  
  54. /// <summary>
  55. /// Assign the domain to the field in layerName
  56. /// </summary>
  57. /// <param name="layerName"></param>
  58. /// <param name="fieldName"></param>
  59. /// <param name="domainName"></param>
  60. public void AssignDomain(string layerName, string fieldName, string domainName)
  61. {
  62.     if (String.IsNullOrEmpty(domainName))
  63.         throw new ArgumentException("Parameter name cannot be null or empty.", domainName);
  64.  
  65.     var domain = ((IWorkspaceDomains)_workspace).DomainByName[domainName];
  66.     AssignDomain(layerName, fieldName, domain);
  67. }
  68.  
  69. /// <summary>
  70. /// Remove the domain usage from the field
  71. /// </summary>
  72. /// <param name="layerName"></param>
  73. /// <param name="fieldName"></param>
  74. public void RemoveDomain(string layerName, string fieldName)
  75. {
  76.     AssignDomain(layerName, fieldName, (IDomain)null);
  77. }
  78.  
  79. private void AssignDomain(string layerName, string fieldName, IDomain domain)
  80. {
  81.     if (String.IsNullOrEmpty(layerName) || String.IsNullOrEmpty(fieldName))
  82.         throw new ArgumentException("Parameter name cannot be null or empty.");
  83.  
  84.     var featureClass = GetFeatureClass(layerName);
  85.  
  86.     if(domain != null)
  87.     {
  88.         var field = featureClass.GetField(fieldName);
  89.  
  90.         if (field.Type != domain.FieldType)
  91.             throw new ApplicationException(
  92.                 "The field and the domain are not of the same type. Cannot assign domain to field.");
  93.     }
  94.  
  95.     var schemaLock = (ISchemaLock)featureClass;
  96.     try
  97.     {
  98.         schemaLock.ChangeSchemaLock(esriSchemaLock.esriExclusiveSchemaLock);
  99.         ((IClassSchemaEdit)featureClass).AlterDomain(fieldName, domain);
  100.  
  101.     }
  102.     finally
  103.     {
  104.         schemaLock.ChangeSchemaLock(esriSchemaLock.esriSharedSchemaLock);
  105.     }
  106. }
  107.  
  108. //For Tests
  109. internal IField GetLayerField(string layerName, string fieldName)
  110. {
  111.     return GetFeatureClass(layerName).GetField(fieldName);
  112. }
  113. #endregion

//TODO: post this only after writing about extension methods and WorkspaceUtils

//TODO: Refactor the code…

Resources:

ESRI: A quick tour of attribute domains

ESRI: A quick tour of subtypes

ESRI: About maintaining attribute integrity while editing (using domains)

ESRI: Exercise 3: Creating subtypes and attribute domains

ESRI: Creating and modifying domains

ESRI: Assigning domains to fields

 

Keywords: ESRI, ArcSDE, SDE, domain, subtype, ArcCatalog, ArcMap, ArcDesktop,IWorkspace, C#, Add, Delete, ArcObjects

 

Calling ArcGIS Server service model

Our application manages the process of installing a site that has CAD drawings that usually don't fit in the place that it is supposed to be. For the longest time someone in our organization used to go through the process of uploading the file to a file GeoDatabase => moving, rotating the lines till they fit in the map => dissolve the line to polyline => save the polyline in the DB. She doesn't want to do that anymore because of all the trouble in the location of the CAD.

For that reason we had to make the process a part of our application.

Uploading the CAD data to lines was relatively easy and is in the post - Working with CAD files in ArcObjects.

The next step was dissolving the polylines to one polyline, and that’s where the troubles started – there are no dissolve methods in ArcObjects (I even posted a question about that in their forums but got no answers).

So after some discussion in our team we decided to go with a Geoprocessor task. We added a model to the ArcGIS server that dissolves one table to another table.

The code is fairly simple (the base for it was taken from the interactive samples for ESRI's Silverlight API):

  1. private void LoadRawCadCompleted()
  2. {
  3.     var dissolveTask = new Geoprocessor(MapApplicationConfigWrapper.Instance.DissolveCadServiceUrl);
  4.     dissolveTask.CancelAsync();
  5.     dissolveTask.JobCompleted += DissolveTask_ExecuteCompleted;
  6.     dissolveTask.Failed += DissolveTask_Failed;
  7.     dissolveTask.SubmitJobAsync(new List<GPParameter>());
  8. }

 

But I got this error back:

{ESRI.ArcGIS.Client.Tasks.ServiceException: Execute operation is not allowed on this service.}

I have tried making the service synchronic – like this guy:

image

I even called did the clear cache thing – see "Clearing ArcGIS Server REST API Cache" Post.

This (of course) didn't work!

 

The problem was:

image

The model didn't run…

(It didn’t work because it used a connection file that was on the Model’s Designer computer but not on the ArcGis Server)

 

 

Well after the problem was solved (and I changed the Execution type back to asynchronous):

image

 

I used that code and got back:

-        jobInfoEventArgs    {ESRI.ArcGIS.Client.Tasks.JobInfoEventArgs}    ESRI.ArcGIS.Client.Tasks.JobInfoEventArgs
-        base    {ESRI.ArcGIS.Client.Tasks.JobInfoEventArgs}    ESRI.ArcGIS.Client.Tasks.TaskEventArgs {ESRI.ArcGIS.Client.Tasks.JobInfoEventArgs}
+        base    {ESRI.ArcGIS.Client.Tasks.JobInfoEventArgs}    System.EventArgs {ESRI.ArcGIS.Client.Tasks.JobInfoEventArgs}
        UserState    "j5fac49ec349241759b248eb20d9a716d"    object {string}
-        JobInfo    {ESRI.ArcGIS.Client.Tasks.JobInfo}    ESRI.ArcGIS.Client.Tasks.JobInfo
        JobId    "j5fac49ec349241759b248eb20d9a716d"    string
       JobStatus    esriJobSucceeded    ESRI.ArcGIS.Client.Tasks.esriJobStatus
-        Messages    Count = 3    System.Collections.Generic.List<ESRI.ArcGIS.Client.Tasks.GPMessage>
        Capacity    4    int
        Count    3    int
-        Static members       
+        Non-Public members       
+        Non-Public members   

               

Resources:

Interactive samples for ESRI's Silverlight API

ESRI forum - Error executing Geoprocessing Service in Silverlight

 

Keywords: ESRI, ArcGIS Server, ArcGIS Manager, Job, Dissolve, Silverlight

Tuesday, January 4, 2011

ArcGIS Server layer problem (DB is down)

 

So I got a task from my team leader to fix something in our implementation of FeatureLayer (in the Silverlight map).

I started last week and this morning I opened the map and saw white – No layers and the background layer took a few minutes to load.

So I fired up the ArcGIS Server Management site at: http://SERVER_NAME/ArcGIS/Manager/Default.aspx

Opened the log:

image

And saw this:

image

(Just imagine 8 more rows of the same text)

WARNING |  The Layer:'SchemaName.LayerName' in Map:'Layers' is invalid. Failure to access the DBMS server

So the first thing I tried was googling it and couldn't find any relevant data.

So since the error was DBMS I tried opening the SQL Server Management and got this:

image

When the server returned the application (of course) didn't start working…

I looked at ArcGIS server:

image

WARNING | The context has been automatically released because the Context Usage Timeout Interval has elapsed.

ERROR | Server Context creation failed on machine SERVER_NAME.

And then this:

image

ERROR | Container process 3592 has crashed on machine SERVER_NAME.

The problem was solved by deleting the cache (see Clearing ArcGIS Server REST API Cache) and restarting the ArcGis Service (see Restart ArcGIS Server service).

It seems ArcGIS Server saves in it's cache the error and doesn't let go until you manually refresh the cache – fun isn't it?