I have a program that loads a shapefile into memory, groups some of the features based on business logic, creates a shapefile out of each group of features, and then saves the files to a cloud location for use in other applications.
The one sticking point in this process has been the attribute table. I had wanted to be able to set custom attributes for the features in the new shapefiles. With the code below I am able to update the datatable with the desired information, but I can't make it persist once I save and dispose the shapefile object.
var table = shapefile.DataTable;
var i = 0;
foreach(var branchObject in branches)
{
shapefile.AddFeature(branchObject.Feature);
var row = table.Rows[i];
row.BeginEdit();
row["BranchName"] = branchObject.Name;
row.EndEdit();
i++;
}
table.AcceptChanges();
This gives me a properly-populated DataTable in the shapefile, but when I open the shapefile in MapWindow5, the only field in the attribute table is the auto-generated Id.
I'm clearly missing some sort of "Save Changes" step that I thought was encompassed in "AcceptChanges()" or "Being/EndEdit()"...what else needs to be called on the table to make it update?
I have a feeling this was covered in one of the tutorials that I can't find since Codeplex sunsetted, but as it is Google hasn't been very helpful.
As it turns out, my DataTable and DataRows were fine. One has to explicitly tell the shapefile to update it's attribute table after changes are made.
shapefile.Filename = $"{filePathWithName}.shp";
shapefile.UpdateAttributes();
These two lines of code just before saving the shapefile, and I can now see the attribute table of my dreams in MapWindow5.
Note:
Calling
shapefile.UpdateAttributes();
without first setting the shapefile.Filename property will throw an exception.
Updating the attributes evidently requires saving to the .dbf file of the shapefile package, and it can't do that without knowing where that .dbf file is supposed to go. This called for some refactoring for me as the output shapefile didn't exist outside memory till the end of the process.
Related
I have an application that reads quite a few domino databases. These databases are split between a main database, and some cabinets (or sub-databases) linked to the main database.
But those databases don't have the same number of cabinets attached to them. It goes from 1 to 15.
As I need to read all of them (93 databases currently), I used to list all of the paths to the nsf files and read them one by one. However, the number of cabinets can change with time, meaning that I need to regularly check if I need to add new files. And I don't want to do that...
So I figured another way: I list only a template of the names of nsf files (all the cabinets are named like database00.nsf, database01.nsf, etc, so it's easy to generate those file names at runtime, and when I try to open a database and get an COMException, I assume that the database doesn't exist, and remove it from my list.
BUT! This is awfully slow and definitely not a good practice!
This is what I currently have. _listDBs holds a list of the paths to the databases' nsf file, and tmplist is temporary list, used later to clean _listDbs
foreach (var pair in _listDBs)
{
try
{
NotesDatabase notedb = _notesSession.GetDatabase(_lotusRoot, pair.Value);
//other stuff here, not important
}
catch (System.Runtime.InteropServices.COMException ex)
{
tmplist.Add(pair);
}
}
I have to wait for the COMException to be raised to identify that the database doesn't exist.
I would prefer to check first if the nsf file exists. But I can't find a way to do that. I couldn't find a way to navigate the Domino server differently.
Any leads?
Thanks a lot!
There is no 100% way to determine if a database exists without trying to open it. you could go through the documents in catalog.nsf and check if there is one for your path, but this database is updated only nightly and might be out of date at the time you check it.
BUT: you can go the other way around: use a NotesDbDirectory, loop through it and check the filepath of the databases if they match your list.
The NotesDbDicrectory contains all databases on the server and looping through it is very fast.
As I am not familiar with c#, here a COM Example for Visual Basic taken from Designer help
Dim s As New NotesSession
s.Initialize
Dim dir As NotesDbDirectory
Dim db As NotesDatabase
Set dir = s.GetDbDirectory("Snapper")
Set db = dir.GetFirstDatabase(NOTES_DATABASE)
While Not (db Is Nothing)
Ë‹Check db.Filename or db.Filepath here
Set db = dir.GetNextDatabase
Wend
we recently had a migration project that went badly wrong and we now have 1000's of duplicate records. The business has been working with them which has made the issue worse as we now have records that have the same name and address but could have different contact information. A small number are exact duplicates. we have started the panful process of manually merging the records but this is very slow. Can anyone suggest another way of tackling the problem please?
You can write a console app quickly to merge them & refer the MSDN sample code for the same.
Sample: Merge two records
// Create the target for the request.
EntityReference target = new EntityReference();
// Id is the GUID of the account that is being merged into.
// LogicalName is the type of the entity being merged to, as a string
target.Id = _account1Id;
target.LogicalName = Account.EntityLogicalName;
// Create the request.
MergeRequest merge = new MergeRequest();
// SubordinateId is the GUID of the account merging.
merge.SubordinateId = _account2Id;
merge.Target = target;
merge.PerformParentingChecks = false;
// Execute the request.
MergeResponse merged = (MergeResponse)_serviceProxy.Execute(merge);
When merging two records, you specify one record as the master record, and Microsoft Dynamics CRM treats the other record as the child record or subordinate record. It will deactivate the child record and copies all of the related records (such as activities, contacts, addresses, cases, notes, and opportunities) to the master record.
Read more
Building on #Arun Vinoth's answer, you might want to see what you can leverage with out-of-box duplicate detection to get sets of duplicates to apply the merge automation to.
Alternatively you can build your own dupe detection to match records on the various fields where you know dupes exist. I've done similar things to compare records across systems, including creating match codes to mimic how Microsoft does their dupe detection in CRM.
For example, a contact's match codes might be
1. the email address
2. the first name, last name, and company concatenated together without spaces.
If you need to match Companies, you can implement the an algorithm like Scribe's stripcompany to generate matchcodes based on company names.
Since this seems like a huge problem you may want to consider drastic solutions like deactivating the entire polluted data set and redoing the data import clean, then finding any of the deactivated records that got touched in the interim to merge them, then deleting the entire polluted (deactivated) data set.
Bottom line, all paths seem to lead to major headaches and the only consolation is that you get to choose which path to follow.
What is the best approach to store information gathered locally in .csv-files with a C#.net sql-database? My reasons for asking is
1: The data i am to handle is massive (millions of rows in each csv). 2: The data is extremely precise since it describes measurements on a nanoscopic scale, and is therefor delicate.
My first though was to store each row of the csv in a correspondant row in the database. I did this using The DataTable.cs-class. When done, i feelt that if something goes wrong when parsing the .csv-file, i would never notice.
My second though is to upload the .csvfiles to a database in it's .csv-format and later parse the file from the database to the local enviroment when the user asks for it. If even possible in c#.net with visual stuido 2013, how could this be done in a efficient and secure manner?
I used .Net DataStreams library from csv reader in my project. It uses the SqlBulkCopy class, though it is not free.
Example:
using (CsvDataReader csvData = new CsvDataReader(path, ',', Encoding.UTF8))
{
// will read in first record as a header row and
// name columns based on the values in the header row
csvData.Settings.HasHeaders = true;
csvData.Columns.Add("nvarchar");
csvData.Columns.Add("float"); // etc.
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "DestinationTable";
bulkCopy.BulkCopyTimeout = 3600;
// Optionally, you can declare columnmappings using the bulkCopy.ColumnMappings property
bulkCopy.WriteToServer(csvData);
}
}
It sounds like you are simply asking whether you should store a copy of the source CSV in the database, so if there was an import error you can check to see what happened after the fact.
In my opinion, this is probably not a great idea. It immediately makes me ask, how would you know that an error had occurred? You certainly shouldn't rely on humans noticing the mistake so you must develop a way to programmatically check for errors. If you have an automated error checking method you should apply that method when the import occurs and avoid the error in the first place. Do you see the circular logic here?
Maybe I'm missing something but I don't see the benefit of storing the CSV.
You should probably use Bulk Insert. With your csv-file as a source.
But this will only work if the file is accessible from the PC that is running your SQL Server.
Here you can find a nice solution as well. To be short it looks like this:
StreamReader file = new StreamReader(bulk_data_filename);
CsvReader csv = new CsvReader(file, true,',');
SqlBulkCopy copy = new SqlBulkCopy(conn);
copy.DestinationTableName = tablename;
copy.WriteToServer(csv);
I've created a new project in .Net (2010 4.0) and added an SDF data file. I've generated a dataset and created a table in it (and I believe generated the Fill and other methods).
In code, I'm trying to add a row to the database.
eBureauScrubber.App_Data.matchingtempDataSet ds = new App_Data.matchingtempDataSet();
eBureauScrubber.App_Data.matchingtempDataSet.ctfFileRow row = ds.ctfFile.NewctfFileRow();
row.Address = "123 Main St.";
row.City = "Overland Park";
row.FirstName = "Matt";
row.LastName = "Dawdy";
row.rownum = 1;
EDIT: Added the next bit of code.
ds.ctfFile.Rows.Add(row);
ds.ctfFile.AcceptChanges();
ds.AcceptChanges();
eBureauScrubber.App_Data.matchingtempDataSetTableAdapters.ctfFileTableAdapter ctfa = new App_Data.matchingtempDataSetTableAdapters.ctfFileTableAdapter();
ctfa.Update(ds.ctfFile);
This runs fine. However, after the program completes, the data is not persisted in the database. What am I missing?
EDIT: I've tried all different combinations of AcceptChanges() on the datatable, the dataset, running update() before, after, etc. I'm missing something huge here. I'm not even sure it is connecting to the "right" database. Maybe that's my problem.
EDIT 2: Here's what I did to get this to work (it's still funky, though).
Change the properties of my DB file in App_Data to "Do Not Copy"
Manually copy that db file to bin\debug\app_data
Use the data adapter's fill method to fill the ds.ctfFile data table.
Create a row (.NewctfFileRow())
Set values on that row.
ds.ctfFile.Rows.Add(row)
ds.ctfFile.AcceptChanges();
ds.AcceptChanges();
Call the adapater's update method.
Now, the data is in my database file (in bin\debug\app_data), but I can't see it because the Data Sources connection. I'm still trying to find out how to do that.
It should have generated a TableAdapter class with a .Update() method that you have to call to save data in your database. See MSDN for some examples.
I'm a total newbie at the .net c# business and dove in this last week creating a form application to shuffle data around for SSIS configurations. The geniuses at MS decided not to apply a key to the table I'm working with - and generating a composite key of the two candidate fields will not work due to their combined length being too long. I don't particularly want to mess with the [ssis configurations] table schema by adding an autoincrement field.
So I've been having alot of trouble getting an update from a DataGridView control to work with a TableAdapter.
I need the update statement to be update table set a = x where b = y and c = z.
Can I set the update method of the TableAdapter, and if so, how. If not, what to do?
I see this autogenerated code:
this._adapter.InsertCommand = new global::System.Data.SqlClient.SqlCommand();
this._adapter.InsertCommand.Connection = this.Connection;
this._adapter.InsertCommand.CommandText = "INSERT INTO [dbo].[SSIS Configurations Staging] ([ConfigurationFilter], [Configur" +
"edValue], [PackagePath], [ConfiguredValueType]) VALUES (#ConfigurationFilter, #C" +
"onfiguredValue, #PackagePath, #ConfiguredValueType)";
But in my form code, the UpdateCommand is not available. I'm assuming this is because the above code is a class definition which I cannot change after creating the object. I see this code has a recommendation not to be changed since it is autogenerated by VS.
Thanks for your most excellent advice.
From your code i assume you are using a typed Dataset with the designer.
Not having a primary key is one of the many reasons the designer will not generate Insert, Update or Delete commands. This is a limitation of the CommandBuilder.
You could use the properties window to add an Update Command to the Apdapter but I would advice against that, if you later configure your main query again it will happily throw away all your work. The same argument holds against modifying the code in any *.Designer.cs file.
Instead, doubleclick on the caption with the Adaptername. The designer will create (if necessary) the accompanying non-designer source file and put the outside of a partial class in it. Unfortunately that is how far the code-generation of the designer goes in C#, you'll have to take it from there. (Aside: The VB designer knows a few more tricks).
Edit:
You will have to provide your own Update(...) method and setup an UpdateCommand etc.
var updateCommand = new SqlCommand();
...