WQL query returns multiple instances in collection - object

I'm running a WQL query in VBScript to pull data from our SCCM database. I can do other queries that all work as expected. They usually return an object collection that I can loop through and access using the standard method:
For Each objGroup in colGroups
wscript.echo objgroup.name
Next
when using the the objgroup.GetObjectText_ method to display the data within one of the collection objects in a working query, I typically see something like:
instance of SMS_R_UserGroup
{
Name = "whatevername";
UsergroupName = "whatever";
WindowsNTDomain = "whatever";
};
There is essentially a single instance (please correct me if my terminology is wrong) within each object with properties that I can easily access.
With the problematic query, I'm seeing multiple instances within each object:
instance of __GENERIC
{
SMS_G_System_NETWORK_ADAPTER_CONFIGURATION =
instance of SMS_G_System_NETWORK_ADAPTER_CONFIGURATION
{
DefaultIPGateway = "xxxx";
DHCPEnabled = 1;
DHCPServer = "xxxx";
DNSDomain = "xxxx";
DNSHostName = "xxxx";
GroupID = 4;
Index = 9;
IPAddress = "xxxxxx";
IPEnabled = 1;
IPSubnet = "xxxx";
MACAddress = "xxxx";
ResourceID = 74762;
RevisionID = 11;
ServiceName = "xxxxxx";
TimeStamp = "xxxxx";
};
SMS_R_System =
instance of SMS_R_System
{
Active = 1;
ADSiteName = "xxxxxx";
AgentName = {"xxxxxx"};
AgentSite = {"xxxxx"};
AgentTime = {"xxxxxx"};
AlwaysInternet = 0;
Client = 1;
ClientType = 1;
ClientVersion = "xxxx";
};
How do I access the properties in an object with multiple instances? Why is it returning multiple instances?
By the way, here is the query I'm running:
SELECT *
FROM SMS_R_System
JOIN SMS_G_System_NETWORK_ADAPTER_CONFIGURATION ON
SMS_G_System_NETWORK_ADAPTER_CONFIGURATION.ResourceID = SMS_R_System.ResourceID
WHERE SMS_R_System.Name = 'xxxxxx' AND
SMS_G_System_NETWORK_ADAPTER_CONFIGURATION.IPAddress IS NOT NULL

You're JOIN'ing SMS_R_System and SMS_G_System_NETWORK_ADAPTER_CONFIGURATION. This will yield one record for each NIC config. If the device has multiple NICs (including virtual ones) with non-null IP addresses (which could include private networks), you will get one JOIN'ed record for each. You should be able to see differences in NIC data for each "duplicate" record.

Related

ServiceStack Bug serializing GUIDs, numbers, etc. when value is default and SerializeFn is specified

When you try and serialize a Guid that is empty (not null, but empty) the result will be omitted if you set ExcludeDefaultValues = true.
But, if you then set ExcludeDefaultValues = false it will generate the string ""
JsConfig.IncludeNullValues = false;
JsConfig.ExcludeDefaultValues = false;
var tt = new { Name="Fred", Value=Guid.Empty, Value2=Guid.NewGuid() };
var test = JSON.stringify(tt);
Console.WriteLine(test);
Gives
{"Name":"Fred","Value":"00000000000000000000000000000000","Value2":"598a6e08af224db9a08c2d0e2f6cff11"}
But we want the Guid's formatted as a Microsoft format Guid at the client end, so we add a serializer:
JsConfig.IncludeNullValues = false;
JsConfig.ExcludeDefaultValues = false;
JsConfig<Guid>.SerializeFn = guid => guid.ToString();
var tt = new { Name="Fred", Value=Guid.Empty, Value2=Guid.NewGuid() };
var test = JSON.stringify(tt);
Console.WriteLine(test);
Gives
{"Name":"Fred","Value2":"07a2d8c0-48ad-4e72-b6f3-4fec81c36a1d"}
So the presence of a SerializeFn seems to make it ignore the config settings so it's impossible to generate the empty Guid.
The same bug applies to numbers, so if (like us) you reformat all Double to three decimal places they are omitted if zero, which is wrong.
Has anyone found a workaround for this?
Stepping through the source, it appears you need to explicitly call out what Types you want to include the default value for if there is a SerializeFn for that Type. Source reference. Note the JsConfig<Guid>.IncludeDefaultValue = true; line below.
Example Source
JsConfig.Reset();
JsConfig.IncludeNullValues = false;
JsConfig.ExcludeDefaultValues = false;
JsConfig<Guid>.SerializeFn = guid => guid.ToString();
JsConfig<Guid>.IncludeDefaultValue = true;
var tt = new { Name = "Fred", Value = Guid.Empty, Value2 = Guid.NewGuid() };
var test = tt.ToJson();
Console.WriteLine(test);
Output
{"Name":"Fred","Value":"00000000-0000-0000-0000-000000000000","Value2":"b86c4a18-db07-42f8-b618-6263148219ad"}
Your problem statement: Gistlyn: Note how it does not return the default GUID in the console.
The answer proposed above: Gistlyn: Notice how it does return the default GUID in the console.

Upload CSV to BigQuery in C#

Basically what I want to do is to submit a job to BigQuery (Asynchronously), check job status after and print out corresponding status info or error info. I created a frame as below. But I need help on:
GoogleApiException: Not Found Job exception when "BigQueryService.Jobs.Get(jobReference.ProjectId, jobReference.JobId).Execute()" was called. My gut is that the job wasn't submit correctly, but I don't know how to do it correctly.
How should I handle GoogleApiExceptions?
Firt step: create a Job (uploading CSV file into BigQuery), return the JobReference
TableReference DestTable = new TableReference();
DestTable.ProjectId = project;
DestTable.DatasetId = dataset;
DestTable.TableId = tableId;
Job Job = new Job();
JobConfiguration Config = new JobConfiguration();
JobConfigurationLoad ConfigLoad = new JobConfigurationLoad();
ConfigLoad.Schema = schema;
ConfigLoad.DestinationTable = DestTable;
ConfigLoad.Encoding = "ISO-8859-1";
ConfigLoad.CreateDisposition = "CREATE_IF_NEEDED";
ConfigLoad.WriteDisposition = createDisposition;
ConfigLoad.FieldDelimiter = delimiter.ToString();
ConfigLoad.AllowJaggedRows = true;
Config.Load = ConfigLoad;
Job.Configuration = Config;
//set job reference (mainly job id)
JobReference JobRef = new JobReference();
JobRef.JobId = GenerateJobID("Upload");
JobRef.ProjectId = project;
Job.JobReference = JobRef;
using(FileStream fileStream = new FileStream(filePath,FileMode.Open)){
var JobInfo = BigQueryService.Jobs.Insert(Job,project,fileStream,"text/csv");//application/octet-stream
JobInfo.UploadAsync();
Console.WriteLine(JobInfo.GetProgress().Status.ToString());
}
return JobRef;
Then, Pull Job status using projectId and jobId in the returned JobReference from the first step:
while (true)
{
pollJob = BigQueryService.Jobs.Get(jobReference.ProjectId, jobReference.JobId).Execute();
i = 0;
Console.WriteLine("Job status" + jobReference.JobId + ": " + pollJob.Status.State);
if (pollJob.Status.State.Equals("DONE"))
{
return pollJob;
}
// Pause execution for pauseSeconds before polling job status again,
// to reduce unnecessary calls to the BigQuery API and lower overall
// application bandwidth.
Thread.Sleep(pauseSeconds * 1000);
}
There's hardly any useful sample code out there showing how to upload a local CSV file to Bigquery table. I eventually get something to work. It might not be the best solution, but it at least works. It's open to any improvement.
private JobReference JobUpload(string project, string dataset, string tableId, string filePath, TableSchema schema, string createDisposition, char delimiter)
{
TableReference DestTable = new TableReference();
DestTable.ProjectId = project;
DestTable.DatasetId = dataset;
DestTable.TableId = tableId;
Job Job = new Job();
JobConfiguration Config = new JobConfiguration();
JobConfigurationLoad ConfigLoad = new JobConfigurationLoad();
ConfigLoad.Schema = schema;
ConfigLoad.DestinationTable = DestTable;
ConfigLoad.Encoding = "ISO-8859-1";
ConfigLoad.CreateDisposition = "CREATE_IF_NEEDED";
ConfigLoad.WriteDisposition = createDisposition;
ConfigLoad.FieldDelimiter = delimiter.ToString();
ConfigLoad.AllowJaggedRows = true;
ConfigLoad.SourceFormat = "CSV";
Config.Load = ConfigLoad;
Job.Configuration = Config;
//set job reference (mainly job id)
JobReference JobRef = new JobReference();
JobRef.JobId = GenerateJobID("Upload");
JobRef.ProjectId = project;
Job.JobReference = JobRef;
using(FileStream fileStream = new FileStream(filePath,FileMode.Open)){
JobsResource.InsertMediaUpload InsertMediaUpload = new JobsResource.InsertMediaUpload(BigQueryService,Job,Job.JobReference.ProjectId,fileStream,"application/octet-stream");
var JobInfo = InsertMediaUpload.UploadAsync();
Console.WriteLine(JobInfo.Status);
while (!JobInfo.IsCompleted)
{
//wait for the job to be activated and run
Console.WriteLine(JobInfo.Status);
}
}
return JobRef;
}
After this, you can actually use the returned JobRef to pull job status, almost the same as we do with Java API:
while(true)
{
PollJob = BigQueryService.Jobs.Get(jobReference.ProjectId, jobReference.JobId).Execute();
Console.WriteLine("Job status" + jobReference.JobId + ": " + PollJob.Status.State);
if (PollJob.Status.State.Equals("DONE"))
{
return PollJob;
}
}

Azure Table Storage: How can I create a dynamic where clause?

Ok, so I am using Azure Table Storage for the first time in a ASP.NET MVC 3 application.
I have a table entity that has a user ID as its RowKey. I have a list of user IDs and need to get all of the entities that have one of the User IDs.
In traditional SQL it would be a simple OR statement in the where clause that you can dynamically add to:
select * from blah
where userID = '123' or userID = '456' or userID = '789'
but I haven't found the equivalent in the Azure SDK.
Is this possible with Azure Table Storage?
Thanks,
David
The .Net client for Azure Table Storage has features to generate and combined filters.
So that you can write your filter expression like that
string[] split = IDs.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string mainFilter = null;
foreach (var id in split)
{
var filter = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, id);
mainFilter = mainFilter != null ? TableQuery.CombineFilters(mainFilter, TableOperators.And, filter) : filter;
}
var rangeQuery = new TableQuery<Blah>().Where(mainFilter);
var result = table.ExecuteQuery(rangeQuery);
I am using Windows Azure Storage 7.0.0 and you can use Linq query to filter.
Unfortunately Contains method is not supported by the Table Service but you can write a simple method to build dynamically your linq query:
public static class ContainsExtension
{
public static Expression<Func<TEntity, bool>> Contains<TEntity,
TProperty>(this IEnumerable<object> values,
Expression<Func<TEntity, TProperty>> expression)
{
// Get the property name
var propertyName = ((PropertyInfo)((MemberExpression)expression.Body).Member).Name;
// Create the parameter expression
var parameterExpression = Expression.Parameter(typeof (TEntity), "e");
// Init the body
Expression mainBody = Expression.Constant(false);
foreach (var value in values)
{
// Create the equality expression
var equalityExpression = Expression.Equal(
Expression.PropertyOrField(parameterExpression, propertyName),
Expression.Constant(value));
// Add to the main body
mainBody = Expression.OrElse(mainBody, equalityExpression);
}
return Expression.Lambda<Func<TEntity, bool>>(mainBody, parameterExpression);
}
}
So that you can build dynamic queries easily :
var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["TableStorageConnectionString"]);
var tableClient = storageAccount.CreateCloudTableClient();
var table = tableClient.GetTableReference("Blah");
var split = IDs.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
// Create a query: in this example I use the DynamicTableEntity class
var query = table.CreateQuery<DynamicTableEntity>()
.Where(split.Contains((DynamicTableEntity d) => d.RowKey));
// Execute the query
var result = query.ToList();
Alrighty, with a bit more digging I found the answer.
You can construct a where filter using the syntax found here: http://msdn.microsoft.com/en-us/library/windowsazure/ff683669.aspx
So for my little example it ended up looking like this:
I have a comma delimited string of IDs sent to this method
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["TableStorageConnectionString"]);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("Blah");
string[] split = IDs.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string filter = null;
for (int i = 0; i < split.Length; i++)
{
filter += " RowKey eq '" + split[i] + "' ";
if (i < split.Length - 1)
filter += " or ";
}
TableQuery<Blah> rangeQuery = new TableQuery<Blah>().Where(filter);
var result = table.ExecuteQuery(rangeQuery);
Result has the list of goodies I need.
One thing to keep in mind is that you wouldn't want to use this on a really large table because I am only getting the RowKey which causes a table scan. If you use the PartitionKey and RowKey together it is more efficient. My table is pretty small (few hundred records at most) so it shouldn't be an issue.
Hope this helps someone.
David

How to add a new group in Active Directory using LDAP in C#

I have scenerio to create new groups in Active Directory using LDAP and C#.
Please provide the suggestions
This article on CodeProject is a really good starting point:
Howto: (Almost) Everything In Active Directory via C#
To create a group, you need to:
bind to a container where you want to create the group inside of
create the group and define some properties
Code:
public void Create(string ouPath, string name)
{
if (!DirectoryEntry.Exists("LDAP://CN=" + name + "," + ouPath))
{
try
{
// bind to the container, e.g. LDAP://cn=Users,dc=...
DirectoryEntry entry = new DirectoryEntry("LDAP://" + ouPath);
// create group entry
DirectoryEntry group = entry.Children.Add("CN=" + name, "group");
// set properties
group.Properties["sAmAccountName"].Value = name;
// save group
group.CommitChanges();
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
}
}
else { Console.WriteLine(path + " already exists"); }
}
Some addition info on setting the Group Scope and Group Type, the enums are:
public enum GroupType : uint
{
GLOBAL = 0x2,
DOMAIN_LOCAL = 0x4,
UNIVERSAL = 0x8,
SECURITY = 0x80000000
}
SECURITY ( Shortened from ADS_GROUP_TYPE_SECURITY_ENABLED ) is combined with the first 3 enums to give you the 6 possible options, without it a group will be a Distribution group.
The values are set as an int, which with the security flag goes into negatives, so unchecked() needs to be used.
Alternatively you could create an enum for the combined values.
GLOBAL | SECURITY = 0x80000002 = -2147483646
DOMAIN_LOCAL | SECURITY = 0x80000004 = -2147483644
UNIVERSAL | SECURITY = 0x80000008 = -2147483640
The value is stored in the 'groupType' property:
var groupType = unchecked((int)(GroupType.UNIVERSAL | GroupType.SECURITY));
group.Properties["groupType"].Value = groupType;
group.CommitChanges();
Take a look at this link: http://msdn.microsoft.com/en-us/library/ms180903(v=vs.80).aspx
I think you might be looking for this part of the code:
// Bind to the domain that this user is currently connected to.
DirectoryEntry dom = new DirectoryEntry();
// Find the container (in this case, the Consulting organizational unit) that you
// wish to add the new group to.
DirectoryEntry ou = dom.Children.Find("OU=Consulting");
// Add the new group Practice Managers.
DirectoryEntry group = ou.Children.Add("CN=Practice Managers", "group");
// Set the samAccountName for the new group.
group.Properties["samAccountName"].Value = "pracmans";
// Commit the new group to the directory.
group.CommitChanges();
I just got through solving this problem for a .NET Core 2.0 app - here is an updated solution for those using .NET Core 2.0+.
This utilizes the NuGet package System.DirectoryServices.Protocols:
try
{
string adminUsername = "myAdminUser";
string namingContext = "CN=Test123,DC=MyCompany,DC=com";
string hostNameAndSSLPort = "192.168.123.123:636";
string adminuser = $"CN={adminUsername},{namingContext}";
string adminpass = "password";
using (LdapConnection connection = new LdapConnection(hostNameAndSSLPort))
{
LdapSessionOptions options = connection.SessionOptions;
options.ProtocolVersion = 3;
options.SecureSocketLayer = true;
connection.AuthType = AuthType.Basic;
NetworkCredential credential = new NetworkCredential(adminuser, adminpass);
connection.Credential = credential;
connection.Bind();
string rolesContext = $"CN=Roles,{namingContext}";
string nameOfNewGroup = "MyGroup";
string groupDN = $"CN={nameOfNewGroup},{rolesContext}";
string dirClassType = "group";
AddRequest addRequest = new AddRequest(groupDN, dirClassType);
AddResponse addResponse = (AddResponse)connection.SendRequest(addRequest);
Console.WriteLine($"A {dirClassType} with a dn of\n {groupDN} was added successfully. The server response was {addResponse.ResultCode}");
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Also there are a lot of great code examples in this sample project provided by Microsoft.

Subsonic 3 Saving into different databases

Using subsonic 3 I have a project with a bit of a weird scenario. I have a .net windows service that needs to connect to a master database which stores connections to other database servers and also a set of tables for processing automated SMS messages. Then I have identical databases on the other servers (the connection string table is empty on the other db's) that handle the messages for other applications.
So, subsonic can call to all of the DB's just fine using the connection string/provider name options.
public static List<SMSRequestWithResponseList> SMSRequestListGetAll(string applicationName)
{
string connStr = GetConnectionStringByApplicationName(applicationName);
List<SMSRequestWithResponseList> response = new List<SMSRequestWithResponseList>();
List<DAL.CDYNESMSRequest> lst = DAL.CDYNESMSRequest.All(connStr, providerName).ToList();
foreach (DAL.CDYNESMSRequest mitm in lst)
{
SMSRequestWithResponseList itm = new SMSRequestWithResponseList(mitm, mitm.CDYNESMSResponses.ToList(), string.Empty);
response.Add(itm);
}
return response;
}
The issue is saving...Insert seems to be working.
DAL.CDYNESMSRequest itm = new DAL.CDYNESMSRequest(connStr, providerName).;
itm.KeyCode = KeyCode;
itm.ApplicationName = ApplicationName;
itm.BatchTransaction = BatchTransaction;
itm.AssignedDID = GetParameter("AssignedDID");
itm.PhoneNumber = PhoneNumber;
itm.MessageDetail = MessageText;
itm.MessageCancelled = false;
itm.MessageQueued = false;
itm.MessageSent = false;
itm.IsImmediate = SendImmediate;
itm.InQueue = false;
itm.ScheduledDateTime = ScheduledDateTime;
itm.CreateDT = dt;
itm.ModifiedDT = dt;
itm.Save();
But it doesn't seem to want to update...
DAL.CDYNESMSRequest itm = DAL.CDYNESMSRequest.SingleOrDefault(x => x.RequestID == requestID, connStr, providerName);
if (itm != null)
{
itm.MessageID = messageGUID;
itm.MessageCancelled = messageCancelled;
itm.MessageQueued = messageQueued;
itm.ReferenceID = messageReferenceID;
itm.MessageSent = messageSent;
if (messageSentDT < new DateTime(1753, 1, 1, 0, 0, 0))
itm.MessageSentDT = null;
else
itm.MessageSentDT = messageSentDT;
itm.MessageSMSError = messageSMSError;
itm.ModifiedDT = dt;
itm.Save();
}
I'm calling using the connection string from the correct database but it doesn't update the record.
If I'm saving it incorrectly please let me know. I did try to create a new provider and set it on the save but it barked at me saying it already had an open connection.
Thanks!
Don't use ActiveRecord pattern, but the SimpleRepository which allows you to setup multiple repos and you can specify a connectionstring per repo.
// not sure if this is the right constructor or if it's providerName, connectionString
var repo1 = new SimpleRepository(connectionString1, providerName);
var repo2 = new SimpleRepository(connectionString2, providerName);
var item = repo1.Single<Product>(1);
if (repo2.Exists<Product>(x => x.Id == item.Id))
repo2.Update(item);
else
repo2.Add(item);

Resources