Powershell: using splatting with scriptblocks? - scripting

I wrote a simple function to create a hashtable out of an xml-file which will hold params that should be passed to a cmdlet.
My XML-File looks like this:
<params>
<Parameter>
<Name>After</Name>
<Value>(get-date).adddays(-7)</Value>
</Parameter>
<Parameter>
<Name>Log</Name>
<Value>System</Value>
</Parameter>
</params>
My function looks like this:
function Create-ParamTable {
param ([string]$ConfigFile,[string]$Root = "params", [string]$Child = "Parameter")
$hash = #{}
[xml]$config = get-content $ConfigFile
foreach ($param in $config.$root.$child) {
$hash.add($param.name,$param.value)
}
return $hash
}
I'm using that returned hashtable with the splat-operator:
PS > $h = create-paramtable -configfile c:\tmp\params.xml ; get-eventlog #h
I want to be able to pass scriptblocks as parameter-value in order to use other cmdlets like get-date to calculate a few values.
For example: I want to store params for get-eventlog in a xml-config-file but I always want to have the logs from the past 7 days.
How do I have to store the value in order to get it executed when passing it to a cmdlet via splatting?

You need to evaluate the parameter values before sticking them in the hashtable. Something like this.
foreach ($param in $config.$root.$child) {
$hash.add($param.name,(Invoke-Expression $param.value))
}

This worked for me in limited testing:
$hash.add($($param.name),$($param.value))

Related

Calling SQL scalar function from Linq Query

There are many similar threads here, but none of them can't solve my problem.
As far as I know, there are two methods for calling SQL scalar function from linq.
1. method:
I've added my function to .edmx file using XML editor and my function is:
<Function Name="prisustvoPostotci" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<CommandText>
select dbo.prisustvoPostotci(#matica_ID,#godina)
</CommandText>
<Parameter Name="matica_ID" Type="int" Mode="In" />
<Parameter Name="godina" Type="int" Mode="In" />
</Function>
I went to model browser and double clicked on my function in Function Imports and changed return collection type to Int32. My function is returning integer.
Now I can call my function from linq using:
using (DB_Entities dm = new DB_Entities())
{
dm.prisustvoPostotci(1, 2016).FirstOrDefault();
}
It returns valid integer value!
But if I call my function from Linq Query like this:
query = query.Where(x => x.date.Value.Year == max_year &&
dm.prisustvoPostotci(x.ID, max_year).FirstOrDefault() >= 50);
It throws this error:
LINQ to Entities does not recognize the method
'System.Data.Entity.Core.Objects.ObjectResult1[System.Nullable1[System.Int32]]
prisustvoPostotci(System.Nullable1[System.Int32],
System.Nullable1[System.Int32])' method, and this method cannot be
translated into a store expression.
2. method:
I've added my function to .edmx file using XML editor and my function is:
<Function Name="prisustvoPostotci" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<CommandText>
select dbo.prisustvoPostotci(#matica_ID,#godina)
</CommandText>
<Parameter Name="matica_ID" Type="int" Mode="In" />
<Parameter Name="godina" Type="int" Mode="In" />
</Function>
I went to model browser and double clicked on my function in Function Imports and changed return collection type to Int32. My function is returning integer.
Then I created a partial class and wrote this method:
public static class EntityFunctions
{
[EdmFunction("Model.Store", "prisustvoPostotci")]
public static int prisustvoPostotci(int matica_ID, int godina)
{
throw new NotSupportedException("Direct calls not supported");
}
}
"Model.Store" is the correct name of my Model Store read from my Schema Namespace inside .edmx file.
Now, if I call my function from linq using:
EntityFunctions.prisustvoPostotci(119, 2016).ToString()
It throws this error:
throw new NotSupportedException("Direct calls not supported");
Also, if I call my function from Linq Query like this:
query = query.Where(x => x.date.Value.Year == max_year &&
EntityFunctions.prisustvoPostotci(x.ID, max_year) >= 50);
It throws this error:
The function or function import 'Model.Store.prisustvoPostotci' is not composable. A non-composable function or function import cannot be called in a query expression.
I tried to edit my .edmx file and change a property IsComposable="true", but it gives me this error:
Functions declaring command text cannot be composed.
Can You help me solve this problem!?
Many thanks in advance !!
::cheers::
Josip
Thanks to Gerd Arnold I realize that scalar functions cannot be used inside query where statement.
Here is how I managed to filter my query by calling scalar function outside query:
var result = query.ToList();
for (int i = 0; i < result.Count; i++)
{
// prisustvoPostotci(ID, year) is my scalar function
if (dm.prisustvoPostotci(result[i].ID, max_year).FirstOrDefault() >= 50)
{
result.Remove(result[i]);
i--;
}
}
This way calling scalar function will work and we can remove matching records from the result!
Hope this will help someone.
:: cheers ::
Josip

How to return the value of a row, without column name in a query?

I am writing a Powershell script that extracts data via the SQLPS module, executing a query directly to the SQL Server. If I do a plain
Select <column A> from <table B>
I get the column listed as well, as stated like this:
Column A
--------
Value C
Here I wish to only retrieve the Value C, for storing it as a variable.
If you are not bound to use this SQLPS module then this might be a easier way to do it:
$connection = new-object System.Data.SqlClient.SqlConnection("Data Source=.;Initial Catalog=TestDB;Integrated Security=True");
$connection.Open()
$query = "SELECT [A] FROM [dbo].[Tablename]"
$cmd = new-object "System.Data.SqlClient.SqlCommand" ($query, $connection)
$cmd.CommandTimeout = 0
$executeReader = $cmd.ExecuteReader()
while ($executeReader.Read()) {
$Name = $executeReader.GetValue(0)
//Do what you desire with the resultset.
$Name + "`r`n" >> D:\PathToResultFolder\result.txt
}
$executeReader.Close()
$connection.Close()
Also I read and think that this should be handled outside of the Query as it is not normal for a Query to not show column-names.

Get response using pdo sql?

I am trying to get the actual response (the data) from my database using prepared statements:
$stmt=$dbconn->prepare("SELECT user_videos FROM public.account_recover_users WHERE user_mail= :email");
$stmt->execute(array(':videos'=>$json_videos,':email'=>$email));
I know that $stmt->execute(array(':videos'=>$json_videos,':email'=>$email)); will return a boolean, not the actual data. But how to get the data from my database into an array? I will need to later return that data, the script is accessed via a GET request, and I will need to do exit("{'data':$data_from_db}"); so I don't want to fetch each row using foreach($stmt as $row). Just pass it all as it is.
$results = array();
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
$results[] = $row;
}

Bindparam and Like condition when querying

I have this function that should just look in the database for a name similars to an user input. However I can't use Like with the param :uname.
Everyone I look in the web they suggest me to do something like this
$username = "$%username%";
However the query doesn't return any result.
I know the database is properly made because if I ask this, it returns the proper answer
SELECT * FROM $schema.pessoa WHERE nome LIKE %Mike%
However in my code $username contains "Mike" and yet it doesn't return anything, I assumed %username hadn't be properly made however if I make an echo of it, it indeed contains the string I want- "Mike".
So the problem seems to be in the way I am questioning it with the parameter but I have no idea
function SearchUser($username) {
global $dbh, $schema;
try {
$username = "$%username%";
$stmt = $dbh->prepare("SELECT * FROM $schema.pessoa WHERE nome LIKE :uname");
$stmt->bindParam(':uname', $username);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if(empty($result))
{echo 'empty';}
return $result;
}
catch(PDOException $e) {
$_SESSION["s_errors"]["generic"][] = "ERRO[32]: ".$e->getMessage();
header("Location: list.php");
die;
}
It does look right to me,
I could only suggest you try it this way and see if that makes any difference
$username = 'Mike';
$stmt->bindValue(":uname", "%".$username."%");
Edit.
Looking at it again, this doesn't look right to me..
$username = '$%username%';
Shouldn't it be
$username = '%'.$username.'%';

Name a variable after a variable. (looping through AD forests and counting servers and generating report)

I am counting servers in three different forests and I'd like to email the results.
I have an integer ($i) that increases for each server, but I reset this number for every forest.
Is there a way that I can create a variable from the forest name ($forest) and a $i count?
I would also love if my mail would foreach the servers in forests count so this would be dynamic.
I guess I am asking if I can name a variable after a variable.. For example $forest.$i or similar...
Please help!
And happy new year!!
Edit:
# Get password (this is secure!)
$password = Read-Host "Enter password" -AsSecureString
# Create a container for your objects.
$forestContainer = #()
$forests = "corp.foresta.com","corp.forestb.com","corp.forestc.com"
foreach ($forest in $forests) {
# Create credentials for forest
$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist "$forest\administrator",$password
# Connect to current forest
$forestconnection = Connect-QADService -Service $forest -Credential $credentials
$servers = Get-QADComputer -WarningAction SilentlyContinue -OSName *server*,*hyper*
echo $($servers.Length)
$currentForest = New-Object PsObject -Property #{
Name = $forest
Count = $($servers.Length)
}
$forestContainer += $currentForest
}
Yes, you can do this. What you're looking to do is create a map/table between the forest and the count for the forest.
Something like
Forest count
====== ======
F1 120
F2 120
Three approaches are available
Use numerical indexing
Use a HashTable for named indexing
Use dynamic properties for named indexing
Let's look at each
1. Use numerical indexing
$fList = #() #initialize an empty array
$fList += $count # add a new entry
$fList += $count # add another entry
$fList # show all values
$fList[0] # get the 1st value
$fList[1] = 30 # change the 2nd value
numerical indexing is great if you know the order of values in the table.
2. Use a HashTable for named indexing
$fHT = #{} # hold counts of each forest
$forest = "example"
$count = 100
$fHT.$forest = $count
$fHT # show all
$fHT.$forest # show the count for the current value of $forest
3. Use dynamic properties for named indexing
$fDP = new-object PSObject
$fDP | add-member -membertype noteproperty -name $forest -value $count
and as Andy suggests, these can even be combined
$fHT = #{} # hold counts of each forest
$forest = "example forest 1"
$count = 100
$fHT.$forest = $count
$fDP = New-Object PSObject -Property $fHT
$fDP # show all
$fDP.$forest # show the count for the current value of $forest
How about creating a custom object. Creating your own object is a great way to associate pieces of data together. In your case you'd like to associate a number to either a name of a forest or a forest object created by something else. Regardless if you are working with a list of forest name strings or objects you can use this approach.
Create a container (an array) for your custom objects.
$forestContainer = #()
As you loop through your forests, create a custom object for each and add it to the array. In this example $myForests is either a collection of objects or a string array of names.
$i = 1
foreach ($forest in $myForests) {
# Create a custom object associating all the data you want.
$currentForest = New-Object PsObject -Property #{
Forest = $forest
Count = $i
# Note: You can add as many properties to the object here as you like.
}
$forestContainer += $currentForest
$i += 1
}
The benefit of this approach is you will be able to use the array filled with your new objects with other Powershell cmdlets easily. For example:
$forestContainer | where {$_.Count -lt 2}
Or
$forestContainer | format-table
Or
$forestContainer | Out-GridView
To access the Count property which contains the current integer when that forest was processed you can use this syntax:
$forestContainer[0].Count
Hopefully you can apply this to your code. If you post some of your code I can help you more.