SqlBulkCopy Failed to obtain column collation information for the destination table - collation

I am getting this error when I try to write rows to a table via SqlBulkCopy and a DataTable object.
Before going any further, let me say that I am aware of the Microsoft KB article below. Every post out there regarding this error references that article. However, I DO NOT have dots in my table or schema name. The table exists in the default schema for the user account, so the table name alone should suffice.
http://support.microsoft.com/kb/944389
Here is the code which performs the bulk write operation:
SqlConnection cn = new SqlConnection(cs);
cn.Open();
SqlTransaction tr = cn.BeginTransaction();
try
{
using (SqlBulkCopy copy = new SqlBulkCopy(cn, SqlBulkCopyOptions.Default, tr))
{
copy.DestinationTableName = CircCountTableName;
copy.ColumnMappings.Add("CirculationRangeID", "CirculationRangeID");
copy.ColumnMappings.Add("GeographyID", "GeographyID");
copy.ColumnMappings.Add("CircCountModelID", "CircCountModelID");
copy.ColumnMappings.Add("Monday", "Monday");
copy.ColumnMappings.Add("Tuesday", "Tuesday");
copy.ColumnMappings.Add("Wednesday", "Wednesday");
copy.ColumnMappings.Add("Thursday", "Thursday");
copy.ColumnMappings.Add("Friday", "Friday");
copy.ColumnMappings.Add("Saturday", "Saturday");
copy.ColumnMappings.Add("Sunday", "Sunday");
copy.ColumnMappings.Add("DataSource", "DataSource");
copy.ColumnMappings.Add("DataSourceID", "DataSourceID");
copy.ColumnMappings.Add("CreateDate", "CreateDate");
copy.ColumnMappings.Add("LastUpdateDate", "LastUpdateDate");
copy.ColumnMappings.Add("LastUpdateUser", "LastUpdateUser");
copy.WriteToServer(circCounts);
tr.Commit();
}
}
catch (Exception ex)
{
tr.Rollback();
}
finally
{
cn.Close();
}
Has any one else encountered this problem when the cause was something other than dot notation? I suspect it's a permissions issue, but I'm not entirely convinced.
Thank you.

I have no idea why this would make a difference, but when I gave the account used to connect to the database the right to Grant the View Definition permission - under Database Properties / Permissions - the error went away.

Related

Changing a value in an Azure Cosmos DB

I've inherited a project at work that uses Azure Cosmos DB. It's completely new to me. In the CosmosDB, we have a bunch of user preferences that are saved. I've discovered a typo in the settings that I need to fix. However, I cannot figure out how to modify the value.
So far I've found the query explorer and I want to run this query:
Update c
set c.Setting = REPLACE(c.Setting, 'N*m', 'N-m')
but query explorer only supports select, not update.
I tried to use Azure Storage Explorer, but when I try to access the document I get nothing except a modal saying "Hold on! We are still working on this." Seriously Microsoft?
My current thinking is to upload a stored procedure and run that. But I'm not sure where to start. My other thinking is to write a small c# application that iterates through each user document and updates them individually. Something like this:
currId = 0;
databaseId = ...;
collectionId = ...;
collectionLink = ...;
while (currId < maxUserId) {
var response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, currId.ToString()));
if (response.Resource != null) {
var upserted = response.Resource;
upserted.SetPropertyValue("Setting", "N-m");
response = await client.UpsertDocumentAsync(collectionLink, upserted);
}
currId++;
}
But boy if that doesn't seem like a dumb idea...
What's the best way to update a single value in a CosmosDB Document?

ListShardMap. UpdateMapping throws an exception LockOwnerId Cannot be Null

I tried different ways and googled a lot for the error but no luck so far.
I am trying to make a function which can update an existing shard mapping but I get the following exception.
Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement.ShardManagementException: Store Error: Error 515, Level 16, State 2, Procedure __ShardManagement.spBulkOperationShardMappingsLocal, Line 98, Message: Cannot insert the value NULL into column 'LockOwnerId', table 'TEST-POS.__ShardManagement.ShardMappingsLocal'; column does not allow nulls. INSERT fails.
Though I created Create Shard and Delete Shard functions and they are working fine. But I get the above error while updating or creating a mapping.
Following is my code:
PointMapping<int> pointMapping;
bool mappingExists = _listShardMap.TryGetMappingForKey(9, out pointMapping);
if (mappingExists)
{
var shardLocation = new ShardLocation(NewServerName, NewDatabaseName);
Shard _shard;
bool shardExists =
_listShardMap.TryGetShard(shardLocation, out _shard);
if (shardExists)
{
var token = _listShardMap.GetMappingLockOwner(pointMapping);
var mappingUpdate = new PointMappingUpdate { Shard = _shard, Status = MappingStatus.Online };
var newMapping = _listShardMap.UpdateMapping(_listShardMap.MarkMappingOffline(pointMapping), mappingUpdate, token);
}
}
I get the same error either I supply the token or not. Then I also tried to supply token in this way MappingLockToken.Create(), but then I get different error that correct token was not provided. It is also obvious because token is different.
_listShardMap.UpdateMapping(offlineMapping, mappingUpdate, MappingLockToken.Create());
Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement.ShardManagementException: Mapping referencing shard '[DataSource=cps-pos-test-1.database.windows.net Database=Live_MSA_Test_Cloud]' belonging to shard map 'ClientIDShardMap' is locked and correct lock token is not provided. Error occurred while executing procedure
I also checked the LockOwnerId in the [__ShardManagement].[ShardMappingsGlobal] table in the database and this is the ID = 00000000-0000-0000-0000-000000000000
I though I am getting null insertion error because token Id is zero, so I updated it manually to 451a4da0-e3d4-42ac-bdc3-5b57022693d0 in database by executing an update query. But it did not work and I get the same Cannot insert the value NULL into column 'LockOwnerId' error.
I am also facing the same Null error while creating a new mapping and I do not see in the code where to provide a token while creating a mapping. Following is code.
PointMappingCreationInfo<int> newMappingInfo = new PointMappingCreationInfo<int>(10, newShard, MappingStatus.Online);
var newMapping = _listShardMap.CreatePointMapping(newMappingInfo);
I searched it a lot on google and downloaded some sample applications as well, but I am not able to find the solution. I will highly appreciate any kind of help.

Control Report Permission Based on Parameters in Reporting Services

Assume we have a report called SalesSummary for a large department. This department has many smaller teams for each product. People should be able to see information about their own product, not other teams' products. We also have one domain group for each of these teams.
Copying SalesSummary report for each team and setting the permission is not the best option since we have many products. I was thinking to use a code similar to below on RS, but it doesn't work. Apparently, System.Security.Principal.WindowsPrincipal is disabled by default on RS.
Public Function isPermitted() As Boolean
Dim Principal As New System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent())
If (Principal.IsInRole("group_prod")) Then
Return true
Else
Return false
End If
End Function
I also thought I can send the userID from RS to SQL server, and inside my SP I can use a code similar to below to query active directory. This also doesn't work due to security restriction.
SELECT
*
FROM OPENQUERY(ADSI,'SELECT cn, ADsPath FROM ''LDAP://DC=Fabricam,DC=com'' WHERE objectCategory=''group''')
Is there any easier way to achieve this goal?
Thanks for the help!
The first option you suggested (using embedded code to identify the executing user) will not be reliable. SSRS code is not necessarily executed as the user accessing the report, and may not have access to that users credentials, such as when running a subscription.
Your second approach will work, but requires the appropriate permissions for your SQL server service account to query Active Directory.
Another approach is to maintain a copy of the group membership or user permissions in a SQL table. This table can be updated by hand or with an automated process. Then you can easily incorporate this into both available parameters and core data queries.
So I ended up with this code:
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "AD");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, identityName);
//// if found - grab its groups
if (user != null)
{
PrincipalSearchResult<Principal> _groups = null;
int tries = 0;
//We have this while because GetGroups sometimes fails! Specially if you don't
// mention the domain in PrincipalContext
while (true)
{
try
{
_groups = user.GetGroups();
break;
}
catch (Exception ex)
{
logger.Debug("get groups failed", ex);
if (tries > 5) throw;
tries++;
}
}
// iterate over all groups, just gets groups related to this app
foreach (Principal p in _groups)
{
// make sure to add only group principals
if (p is GroupPrincipal)
{
if (p.Name.StartsWith(GROUP_IDENTIFIER))
{
this.groups.Add((GroupPrincipal)p);
this.groupNames.Add(p.Name);
}
}
}
}
Now, that you have a list of related group you can check the list to authorize the user!

Add or replace entity in Azure Table Storage

I'm working with Windows Azure Table Storage and have a simple requirement: add a new row, overwriting any existing row with that PartitionKey/RowKey. However, saving the changes always throws an exception, even if I pass in the ReplaceOnUpdate option:
tableServiceContext.AddObject(TableName, entity);
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
If the entity already exists it throws:
System.Data.Services.Client.DataServiceRequestException: An error occurred while processing this request. ---> System.Data.Services.Client.DataServiceClientException: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>EntityAlreadyExists</code>
<message xml:lang="en-AU">The specified entity already exists.</message>
</error>
Do I really have to manually query for the existing row first and call DeleteObject on it? That seems very slow. Surely there is a better way?
As you've found, you can't just add another item that has the same row key and partition key, so you will need to run a query to check to see if the item already exists. In situations like this I find it helpful to look at the Azure REST API documentation to see what is available to the storage client library. You'll see that there are separate methods for inserting and updating. The ReplaceOnUpdate only has an effect when you're updating, not inserting.
While you could delete the existing item and then add the new one, you could just update the existing one (saving you one round trip to storage). Your code might look something like this:
var existsQuery = from e
in tableServiceContext.CreateQuery<MyEntity>(TableName)
where
e.PartitionKey == objectToUpsert.PartitionKey
&& e.RowKey == objectToUpsert.RowKey
select e;
MyEntity existingObject = existsQuery.FirstOrDefault();
if (existingObject == null)
{
tableServiceContext.AddObject(TableName, objectToUpsert);
}
else
{
existingObject.Property1 = objectToUpsert.Property1;
existingObject.Property2 = objectToUpsert.Property2;
tableServiceContext.UpdateObject(existingObject);
}
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
EDIT: While correct at the time of writing, with the September 2011 update Microsoft have updated the Azure table API to include two upsert commands, Insert or Replace Entity and Insert or Merge Entity
In order to operate on an existing object NOT managed by the TableContext with either Delete or SaveChanges with ReplaceOnUpdate options, you need to call AttachTo and attach the object to the TableContext, instead of calling AddObject which instructs TableContext to attempt to insert it.
http://msdn.microsoft.com/en-us/library/system.data.services.client.dataservicecontext.attachto.aspx
in my case it was not allowed to remove it first, thus I do it like this, this will result in one transaction to server which will first remove existing object and than add new one, removing need to copy property values
var existing = from e in _ServiceContext.AgentTable
where e.PartitionKey == item.PartitionKey
&& e.RowKey == item.RowKey
select e;
_ServiceContext.IgnoreResourceNotFoundException = true;
var existingObject = existing.FirstOrDefault();
if (existingObject != null)
{
_ServiceContext.DeleteObject(existingObject);
}
_ServiceContext.AddObject(AgentConfigTableServiceContext.AgetnConfigTableName, item);
_ServiceContext.SaveChangesWithRetries();
_ServiceContext.IgnoreResourceNotFoundException = false;
Insert/Merge or Update was added to the API in September 2011. Here is an example using the Storage API 2.0 which is easier to understand then the way it is done in the 1.7 api and earlier.
public void InsertOrReplace(ITableEntity entity)
{
retryPolicy.ExecuteAction(
() =>
{
try
{
TableOperation operation = TableOperation.InsertOrReplace(entity);
cloudTable.Execute(operation);
}
catch (StorageException e)
{
string message = "InsertOrReplace entity failed.";
if (e.RequestInformation.HttpStatusCode == 404)
{
message += " Make sure the table is created.";
}
// do something with message
}
});
}
The Storage API does not allow more than one operation per entity (delete+insert) in a group transaction:
An entity can appear only once in the transaction, and only one operation may be performed against it.
see MSDN: Performing Entity Group Transactions
So in fact you need to read first and decide on insert or update.
You may use UpsertEntity and UpsertEntityAsync methods in the official Microsoft Azure.Data.Tables TableClient.
The fully working example is available at https://github.com/Azure-Samples/msdocs-azure-data-tables-sdk-dotnet/blob/main/2-completed-app/AzureTablesDemoApplicaton/Services/TablesService.cs --
public void UpsertTableEntity(WeatherInputModel model)
{
TableEntity entity = new TableEntity();
entity.PartitionKey = model.StationName;
entity.RowKey = $"{model.ObservationDate} {model.ObservationTime}";
// The other values are added like a items to a dictionary
entity["Temperature"] = model.Temperature;
entity["Humidity"] = model.Humidity;
entity["Barometer"] = model.Barometer;
entity["WindDirection"] = model.WindDirection;
entity["WindSpeed"] = model.WindSpeed;
entity["Precipitation"] = model.Precipitation;
_tableClient.UpsertEntity(entity);
}

Get all deleted record

I'm looking for a way to get all records where deleted is set to true on a particular table. How might I accomplish this?
Note: Using auto-generated class by SubSonic. Not T-SQL.
The auto-generated SubSonic classes don't support querying logical deletes. But you can do this (version 2.1/2.2 syntax):
public partial class TableClassCollection
{
public TableClassCollection LoadAll(bool suppressLogicalDeletes)
{
SubSonic.SqlQuery q = new SubSonic.Select(TableClass.Schema)
.From(TableClass.Schema);
if (suppressLogicalDeletes)
{
q.Where(TableClass.DeletedColumn).IsEqualTo(false);
}
return q.ExecuteAsCollection<TableClassCollection>();
}
}
More examples at subsonicproject.com
I've never heard of SubSonic before, but a quick Google search turned up: Select Queries in SubSonic.
So, using that page as a guide, it sounds like you'd be able to write your query as:
FooCollection deletedFoos = // use the generated collection class
DB.Select().From("FooTable") // table name goes here
.Where("deleted").IsEqualTo(true) // might need 1, depends on database?
.ExecuteAsCollection<FooCollection>(); // should match the type above
not a lot of detail in your question, but assuming there's a column named "deleted," it would look something like this:
select * from tableName where deleted = true

Resources