How do I access data driven values in a web test plugin - visual-studio

I have a data driven web performance test in Visual Studio 2012. I want to access the values from the data source within a PreRequest() or a PostRequest() plugin. The values are being accessed already for form post parameters via bindings like {{DataSource1.PaymentAccounts#csv.USER_ID}}.
My end goal is to write the values into a web log comment so the data source values of failed tests are easier to identify. So the values would be passed into a call of e.WebTest.AddCommentToResult(string.Format(...)).

The values are stored in the text context, so can just access the value with WebTestRequestPlugin code such as:
object contextParameterObject;
if ( e.WebTest.Context.TryGetValue("DataSource1.PaymentAccounts#csv.USER_ID",
out contextParameterObject) ) {
string contextParameter = contextParameterObject.ToString();
e.WebTest.AddCommentToResult(contextParameter);
}
else {
throw new WebTestException("'DataSource1.PaymentAccounts#csv.USER_ID' not found");
}
By default the context only holds those fields (columns) of the data source that are explicitly used in the web test file. To make all the fields in the datasource available set the Select columns property of the datasource file to Select all columns.

[Description("Captures the datasource value used in a request and writes it to a file.")]
public class WriteDataSourceValueToFile : WebTestRequestPlugin
{
[Description("The name of the file to save the scraped values to. E.g., ResponseTimes.log. ")]
public string OutputFileName { get; set; }
[Description(#"Path of the file to save the scraped values to. Format: C:\temp. (Note no trailing backslash.)")]
public string OutputPathName { get; set; }
// The LogWriter class is in the main project, not in the utilities project.
private LogWriter writer = LogWriter.Instance;
[System.ComponentModel.Description("Name of the datasource.")]
[System.ComponentModel.DefaultValue("UserIds")]
public string DatasourceName { get; set; }
[System.ComponentModel.Description("Name of the CSV file. Use the syntax that Visual Studio uses, with a # sign in place of the period. E.g., UserIds#csv")]
[System.ComponentModel.DefaultValue("UserIds#csv")]
public string CsvFileName { get; set; }
[System.ComponentModel.Description("Field name in the CSV file")]
[System.ComponentModel.DefaultValue("UserIds")]
public string FieldName { get; set; }
public override void PreRequest(object sender, PreRequestEventArgs e)
{
}
public override void PostRequest(object sender, PostRequestEventArgs e)
{
object contextParameterObject;
if (e.WebTest.Context.TryGetValue(DatasourceName + "." + CsvFileName + "." + FieldName,
out contextParameterObject))
{
writer.WriteToLog($"Value chosen from {DatasourceName } ={contextParameterObject.ToString()}" );
}
else
{
throw new WebTestException(DatasourceName + "." + CsvFileName + "." + FieldName + " not found");
}
}
}
Two crucial Tips:
Beware lack of ANSI! Make sure you have saved the DS file (csv file) as ANSI per this stackoverflow post. Those unicode byte order marks at the beginning will make it difficult to refer to the column name when you write the DS string.
"DataSource1.mycsvfile#csv.myDSColumnName" is so much better than:
"DataSource1.mycsvfile#csv.myDSColumnName"
Then, in addition to trygetvalue (which is good) you should also simply be able to write:
string currentRowFromDS = e.WebTest.Context["DataSource1.mycsvfile#csv.myDSColumnName"].ToString();
After adding the DS to the webtest, be sure to change the "Select Columns" property to "Select all Columns" (and NOT "select only bound columns"). If you leave it at the default, "select only bound columns," and you have not actually bound the columns yet, Visual Studio will NOT place the row value into the web test. The context parameter will not be there at all.
This behavior is different than a SQL datasource in a unit test for instance, where you simply assign the variable. In general, the API's for adding data to unit tests (Microsoft.VisualStudio.TestTools.UnitTesting) are slightly different than the API's for adding data to a web test Microsoft.VisualStudio.TestTools.WebTesting).

Related

How can I remove a column from my SQLite table with sqlite-net-pcl that comes with Xamarin Forms?

I have a table that was created with this C# code:
public class ViewHistory
{
[PrimaryKey]
public string YYMMDD { get; set; }
public int UtcNow { get; set; }
public int Assign { get; set; }
public int Learn { get; set; }
public int Practice { get; set; }
public int Quiz { get; set; }
}
and then
db2.CreateTable<ViewHistory>();
I would like to check if the Assign column exists and then remove the Assign column if it exists. So far the suggestions I have seen have all been either quite old or have suggested creating a new table and also don't include any check to see if it exists.
Now with the latest release of sqlite-net-pcl (1.5.2) is there any way I can drop a column? If not can someone recommend a way to do this that's just using the C# features that are available to me with the PCL or with SQL that I could execute.
I saw this on SO but it doesn't really help me:
Delete column from SQLite table
SQLite does not support ALTER TABLE x DROP COLUMN x so you need to create a new table and copy data.
You can do all this via a single multi-line SQL statement and execute it, but this will walk you though the steps using the ORM as much as possible:
Note: This assumes that your model has be updated and does not include that column anymore and your current database might or might not have it...
var conn = new SQLiteConnection(.....
conn.CreateTable<ViewHistory>();
~~~
if (0 < conn.ExecuteScalar<int>("SELECT COUNT(*) AS colcount FROM pragma_table_info('ViewHistory') WHERE name='Assign'"))
{
try
{
conn.BeginTransaction();
conn.Execute("ALTER TABLE ViewHistory RENAME TO backup;");
conn.CreateTable<ViewHistory>();
// Column map and copy data
var columns = conn.GetMapping<ViewHistory>(CreateFlags.None).Columns;
var columnNames = string.Join(",", columns.Select(p => p.Name));
conn.Execute($"INSERT INTO ViewHistory ({columnNames}) SELECT {columnNames} FROM backup;");
conn.Execute("DROP TABLE backup;");
conn.Commit();
conn.Execute("VACUUM");
}
catch (Exception ex)
{
conn.Rollback();
throw ex;
}
}
Note: Typically I just use "DB Browser for SQLite", make all the table/column alterations to the database and copy the "DB Log output" that contains all the SQL statements and paste that into a single SQLConnnection.Exceute statement...

How do I proccess a string of numbers as string in a VS webtest datasource?

I have a problem. I am using a Visual studio web performance test and I have a csv file with the data that I need to send in a string body of a web request. The issue is that when the web test retrieve the data from accountID it takes the data as int instead of a string. So if the account number is 000005 or 000016 the test put a 5 and a 15 ignoring the zeroes on the left. That give me an error on my web service.
Is there a way to force the web test to see all the data as strings? Thanks
Below you can see an example of the csv file. The data is not a lot and is only 2 columns so I don’t want to create a database for that
AccountsNames, AccountID
Nombre1, 00001
Nombre3, 00002
Nombre4, 00003
Nombre5, 00004
Nombre6, 00005
Nombre7, 00006
Nombre8, 00007
What I end up doing is creating a Web Request plug in that gets the data source from the webtest context and editing the value.
Here is a code example:
namespace WebTestProjectToPlayArround
{
[DisplayName("Add Padding Zeroes")]
[Description("")]
public class EditContextParameter : WebTestRequestPlugin
{
[DisplayName("DataSourceContext")]
[Description("")]
public string DataSourceContextParam { set; get; }
[DisplayName("ContextParamToStoreValue")]
[Description("")]
public string ContextParam { set; get; }
public override void PreRequest(object sender, PreRequestEventArgs e)
{
base.PreRequest(sender, e);
var value = (string) e.WebTest.Context[DataSourceContextParam];
string newValue = "";
object contextParam;
string contextName = value.Replace("{","").Replace("}", "");
if (e.WebTest.Context.TryGetValue(contextName, out contextParam))
{
newValue = contextParam.ToString();
newValue = newValue.PadLeft(10, '0');
}
e.WebTest.Context[ContextParam] = newValue;
}
}
}

VS Webtest: How to use extracted value from request in Webtest loop count?

I have two extracted values (count and count1) -> these should be integers. I need to subtract (count1 - count = difference) and use the difference in a loop of webtest (delete generated groups).
How can I pass this result to Webtest loop (iteration) count?
http://hmp.me/pz0
http://hmp.me/pz2
You might create a web test request plugin, give it three parameters (ie properties) two are the count1 and count2 context parameters. The plugin does a subtraction and stores the result into a third context parameter (named via the third property). Then just use the value in the third context parameter in the loop condition.
The input context parameters will contain strings that will need to be converted to integers (or floats or whatever your application needs). The result will need to be converted back to a string before saving.
The plugin could be as follows. Note: not compiled or tested. Note: same code could also be done in a PreRequest or a PreRequestDataBinding plugin.
public class SubtractContextParameters : WebTestRequestPlugin
{
public string LeftHandNumberCP { get; set; }
public string RightHandnumberCP { get; set; }
public string ResultCP { get; set; }
public override void PostRequest(object sender, PostRequestEventArgs e)
{
int left = int.Parse(e.WebTest.Context[LeftHandNumberCP].ToString());
int right = int.Parse(e.WebTest.Context[RightHandnumberCP].ToString());
int result = left - right;
e.WebTest.Context[ResultCP] = result.ToString();
}
}
The name passed to the ResultCP property could then be used in a NumberComparison condition in the web test loop.

Combining Linq Expressions for Dto Selector

We have a lot of Dto classes in our project and on various occasions SELECT them using Expressions from the entity framework context. This has the benefit, that EF can parse our request, and build a nice SQL statement out of it.
Unfortunatly, this has led to very big Expressions, because we have no way of combining them.
So if you have a class DtoA with 3 properties, and one of them is of class DtoB with 5 properties, and again one of those is of class DtoC with 10 properties, you would have to write one big selector.
public static Expression<Func<ClassA, DtoA>> ToDto =
from => new DtoA
{
Id = from.Id,
Name = from.Name,
Size = from.Size,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
Also, they cannot be reused. When you have DtoD, which also has a propertiy of class DtoB, you would have to paste in the desired code of DtoB and DtoC again.
public static Expression<Func<ClassD, DtoD>> ToDto =
from => new DtoD
{
Id = from.Id,
Length = from.Length,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
So this will escalate pretty fast. Please note that the mentioned code is just an example, but you get the idea.
I would like to define an expression for each class and then combine them as required, as well as EF still be able to parse it and generate the SQL statement so to not lose the performance improvement.
How can i achieve this?
Have you thought about using Automapper ? You can define your Dtos and create a mapping between the original entity and the Dto and/or vice versa, and using the projection, you don't need any select statements as Automapper will do it for you automatically and it will project only the dto's properties into SQL query.
for example, if you have a Person table with the following structure:
public class Person
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
public string Initial { get; set; }
public string PreferredName { get; set; }
public string FormerTitle { get; set; }
public string FormerFamilyName { get; set; }
public string FormerGivenName { get; set; }
}
and your dto was like this :
public class PersonDto
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
}
You can create a mapping between Person and PersonDto like this
Mapper.CreateMap<Person, PersonDto>()
and when you query the database using Entity Framework (for example), you can use something like this to get PersonDto columns only:
ctx.People.Where(p=> p.FamilyName.Contains("John"))
.Project()
.To<PersonDto>()
.ToList();
which will return a list of PersonDtos that has a family name contains "John", and if you run a sql profiler for example you will see that only the PersonDto columns were selected.
Automapper also supports hierachy, if your Person for example has an Address linked to it that you want to return AddressDto for it.
I think it worth to have a look and check it, it cleans a lot of the mess that manual mapping requires.
I thought about it a little, and I didn't come up with any "awesome" solution.
Essentially you have two general choices here,
Use placeholder and rewrite expression tree entirely.
Something like this,
public static Expression<Func<ClassA, DtoA>> DtoExpression{
get{
Expression<Func<ClassA, DtoA>> dtoExpression = classA => new DtoA(){
BDto = Magic.Swap(ClassB.DtoExpression),
};
// todo; here you have access to dtoExpression,
// you need to use expression transformers
// in order to find & replace the Magic.Swap(..) call with the
// actual Expression code(NewExpression),
// Rewriting the expression tree is no easy task,
// but EF will be able to understand it this way.
// the code will be quite tricky, but can be solved
// within ~50-100 lines of code, I expect.
// For that, see ExpressionVisitor.
// As ExpressionVisitor detects the usage of Magic.Swap,
// it has to check the actual expression(ClassB.DtoExpression),
// and rebuild it as MemberInitExpression & NewExpression,
// and the bindings have to be mapped to correct places.
return Magic.Rebuild(dtoExpression);
}
The other way is to start using only Expression class(ditching the LINQ). This way you can write the queries from zero, and reusability will be nice, however, things get harder & you lose type safety. Microsoft has nice reference about dynamic expressions. If you structure everything that way, you can reuse a lot of the functionality. Eg, you define NewExpression and then you can later reuse it, if needed.
The third way is to basically use lambda syntax: .Where, .Select etc.. This gives you definitely better "reusability" rate. It doesn't solve your problem 100%, but it can help you to compose queries a bit better. For example: from.MyCList.Select(dtoCSelector)

MVC 3 with Dynamic Data - applying data types to Dynamic Data

I have an MVC 3 C# / ADO.NET / Dynamic Data app set up and working(ish). To set it up I created an MVC 3 app, added the Dynamic Data components in, split out Presentation, Business and Data in to three projects, set the references to match the MVC pattern and set up the routes and scaffolding.
List, Edit and Insert all work with the standard DD page templates, however I've hit a wall getting the Presentation Layer to apply data type attributes to the data displayed in Gridview and Details views, particularly for URLs, which I want be typed as DataType.Url and so use the associated DD display attributes.
Have tried setting up a meta data class for the Link table and applying something like:
[DataType(DataType.Url)]
public object URL {get; set;}
(the Url field in table "Link" is "URL")
.. within a partial class, which is something I read about for pure DD sites.
Can anyone point me in the right direction, or tell me if is this even possible?
Many Thanks.
Yes this is possible. I would write a custom FieldTemplate for Urls. Using the UIHint metadata, you can assign a custom fieldtemplate to the column. Something like this (untested):
public partial class FooUrl : System.Web.DynamicData.FieldTemplateUserControl
{
string getUrl()
{
var metadata = MetadataAttributes.OfType<DataTypeAttribute>().FirstOrDefault();
if (metadata == null)
return FieldValueString;
switch (metadata.DataType)
{
case DataType.Url:
string url = FieldValueString;
if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
return url;
return "http://" + FieldValueString;
case DataType.EmailAddress:
return "mailto:" + FieldValueString;
default:
throw new Exception("Unknown DataType");
}
}
protected override void OnDataBinding(EventArgs e)
{
HyperLinkUrl.NavigateUrl = getUrl();
}
public override Control DataControl
{
get
{
return HyperLinkUrl;
}
}
}
}
I hope this helps.

Resources