Specflow table column binding prevent Nulls - bdd

Given the following code:
public class Bob {
public string Name { get; set; }
public int Age { get; set; }
public decimal Height { get; set; }
}
Feature: Bob checker
Scenario: Check Bob
Given I have the following Bob
| Name | Age | Hieght |
| Robert | 63 | 1.87 |
When . . . .
Then . . . .
[Given(#"I have the following Bob")]
public void IHaveTheFollowingBob(Table table) {
var bob = table.CreateInstance<Bob>();
}
You'l notice the word 'Height' is not spelt correctly in the table. The CreateInstance method will still work but the 'Height' on the Bob object will be 0 or for reference type, null.
Is there a way to make SpecFlow fail if the column doesn't bind to the Type provided. Thanks

There is no such thing as throwing error if there is a column that does not match any of the class properties.
However there is a work-arround. You may implement a method to check if there is an "invalid" column to prevent typos:
[Given("I have the following Bob")]
public void TestMethod1(Table table)
{
AssertAllTableColumnsMatch<Bob>(table);
var bob = table.CreateInstance<Bob>();
}
public void AssertAllTableColumnsMatch<T>(Table table)
{
foreach (var header in table.Header)
{
bool columnHeaderMatches = HasProperty(typeof(T), header);
Assert.IsTrue(columnHeaderMatches,
string.Format("The column header [{0}] does not match any property of the type [{1}]",
header, typeof(T).Name));
}
}
private bool HasProperty(Type obj, string propertyName)
{
return obj.GetProperty(propertyName) != null;
}
The following error will be thrown:
-> error: Assert.IsTrue failed. The column header [Hieght] does not match any property of the type [Bob]
The method simply iterates all the columns and checks if the provided type has such property.

I should RTFM. table.CompareToInstance(bob); Will fail if the Property is not there or is not populated the correctly. If you use this in conjuction with table.CreateInstance you will get failures where there are nulls. Leaving Q&A here just in case anyone else has brain fail.
SpecFlow Assist Helper, CompareToInstance
If you have have complex types you can represent them in the table and use IValueRetrieverand IValueComparer extensions
For example consider the following micky mouse code:
public class Bob {
public string Name { get; set; }
public int Age { get; set; }
public decimal Height { get; set; }
}
public class BobModel {
public string Name { get; set; }
public int Age { get; set; }
public decimal Height { get; set; }
}
Feature: Bob checker
Scenario: Check Bob
Given I have the following Bob
| Name | Age | Hieght |
| Robert | 63 | 1.87 |
When I map this to the model
Then Bob the model looks like this
| Name | Age | Height |
| Robert | 63 | 1.87 |
[Given(#"I have the following Bob")]
public void IHaveTheFollowingBob(Table table) {
var bob = table.CreateInstance<Bob>();
}
[When(#"I map this to the model")]
public void IMapThisToTheModel() {
sut = production.Map(bob);
}
[Then(#"Bob the model looks like this") {
public void BobTheModelLooksLikeThis(Table table) {
table.CompareToInstance<BobModel>(sut);
}
In this instance if there was a typo in the second table it would fail saying it can't find the property. If there is a typo in the first table then this will still be null but the assertion will fail. No false positives

Related

Summing Up Using Latest Rates From A Separate Price Log - LINQ

In an ASP Core project, with EF, have a table containing the details in respect of different types of items in inventory like:
public class InventoryDetails
{
[Key, StringLength(15)]
public string InventoryID { get; set; }
[Required, MaxLength(150)]
public string InventoryName { get; set; }
[MaxLength(200)]
public string Details { get; set; }
public ICollection<PriceLog> PriceLogs { get; set; }
public ICollection<SalesItem> SalesItems { get; set; }
}
Because of frequently changing prices of the said items, a separate price log is maintained as follows:
public class PriceLog
{
[Key]
public int ID { get; set; }
[Required]
public DateTime PriceTime { get; set; }
[Required, StringLength(15)]
public string InventoryID { get; set; }
public virtual InventoryDetails InventoryItems { get; set; }
[Required, Column(TypeName = "decimal(19, 4)")]
public decimal PricePerUnit { get; set; }
[Required, Column(TypeName = "decimal(19, 4)")]
public decimal TaxPerUnit { get; set; }
}
Similarly, Sales are being recorded in a table like the following invoices model:
public class SaleInvoice
{
[Key]
public int SInvoiceID { get; set; }
[Required]
public DateTime SalesTime { get; set; }
public ICollection<SalesItem> SalesItems { get; set; }
}
while its items are being recorded in a separate table as:
public class SalesItem
{
[Key]
public int ID { get; set; }
[Required]
public int SInvoiceID { get; set; }
public virtual SaleInvoice Sales { get; set; }
[Required, StringLength(15)]
public string InventoryID { get; set; }
public virtual InventoryDetails InventoryItems { get; set; }
[Required, Column(TypeName = "decimal(19, 4)")]
public decimal Qty { get; set; }
}
Now the main objective is to show/use the LATEST price against each item? In other words, if the SalesInvoice is dated 31-Dec-2017, how to use the LATEST Sales rates of EACH item that exist (as per the Price Log table) in the year 2017 or earlier, against the Quantity (Qty) as per the SalesItem, and being SUMMED up at the Invoice Level?
For the sake of clarification, consider the following prices recorded as per Price Log like:
01-Dec-2017 | Strawberry | 8.59
02-Dec-2017 | Strawberry | 7.36
02-Dec-2017 | Apple | 15.68
04-Dec-2017 | Apple | 18.91
05-Dec-2017 | Banana | 8.1
08-Dec-2017 | Strawberry | 7.4
09-Dec-2017 | Strawberry | 10.62
10-Dec-2017 | Orange | 11.16
12-Dec-2017 | Apple | 15.79
14-Dec-2017 | Strawberry | 9.57
15-Dec-2017 | Banana | 8.34
16-Dec-2017 | Orange | 10.37
18-Dec-2017 | Banana | 10.07
19-Dec-2017 | Strawberry | 10.05
20-Dec-2017 | Apple | 17.59
23-Dec-2017 | Apple | 16.51
27-Dec-2017 | Strawberry | 10.02
30-Dec-2017 | Orange | 10.88
04-Jan-2018 | Orange | 12.00
06-Jan-2018 | Strawberry | 10.74
09-Jan-2018 | Apple | 17.47
09-Jan-2018 | Banana | 10.47
In this regard, this time, consider an invoice dated 5-Jan-2018, with following items:
10 Strawberries
05 Apples
10 Oranges
15 Bananas
what would be the appropriate LINQ part to sum up the entire sales with the rates latest on the said date as:
27-Dec-2017 | Strawberry | 10.02 * 10 = 100.20
23-Dec-2017 | Apple | 16.51 * 05 = 82.55
04-Jan-2018 | Orange | 12.00 * 10 = 120.00
18-Dec-2017 | Banana | 10.07 * 15 = 151.05
Thus, the Index of SalesInvoice reflecting the entire list of Invoices with the entry pertaining to such a date as:
Invoice_ID Sales_Time Net_Sales
.... .... ....
.... .... ....
0007 5-Jan-2018 453.80
.... .... ....
.... .... ....
Looking forward for you experts' help in this regard.
Thanks
How about adding function to your SalesInvoice (or overloading it with a partial class).
This is not exactly LINQ query but it should get the job done.
public class SaleInvoice
{
[Key]
public int SInvoiceID { get; set; }
[Required]
public DateTime SalesTime { get; set; }
public ICollection<SalesItem> SalesItems { get; set; }
/* This returns total amount for each salesitem compared to salesdate*/
public decimal LatestRates(List<PriceLog> priceLogs)
{
var uniquePriceLogs = new List<PriceLog>();
foreach (var p in priceLogs.Where(w => w.PriceTime < this.SalesTime).GroupBy(g => g.InventoryID))
{
var orderedLog = p.OrderByDescending(o => o.PriceTime).FirstOrDefault();
if (orderedLog != null) uniquePriceLogs.Add(orderedLog);
}
decimal total = 0;
foreach(var s in this.SalesItems)
{
var unique = uniquePriceLogs.FirstOrDefault(f => f.InventoryID == s.InventoryID);
if(unique != null)
{
total += s.Qty * unique.PricePerUnit;
}
}
return total;
}
}
However if I am misunderstaing your question then you could please clarify if you wanted to add to the current schema or simply create a single linq query to rule them all :)

EF one to one or zero relationship with custom foreign key column name

How do i make the foreign key column to have a custom name. by default EF will append "_" to the foreign key column
Customer
POCO:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Order Order { get; set; }
}
EF Config:
HasKey(m => m.Id);
HasOptional(m => m.Order).WithOptionalPrincipal(e => e.Customer);
Customer table:
+====+=======+
| Id | Name |
+====+=======+
| 1 | Cust1 |
+----+-------+
| 2 | Cust2 |
+----+-------+
| 3 | Cust3 |
+----+-------+
| 4 | Cust4 |
+----+-------+
| 5 | Cust5 |
+====+=======+
Order
POCO:
public class Order
{
public int Id { get; set; }
public string ItemName { get; set; }
public virtual Customer Customer { get; set; }
}
EF Config:
HasKey(m => m.Id);
Order table:
+====+=============+==========+
| Id | Customer_Id | ItemName |
+====+=============+==========+
| 1 | 2 | Modem |
+----+-------------+----------+
| 2 | 5 | Router |
+====+=============+==========+
How can i make the Order table as follow (to have "CustomerId" as the actual property name)?
+====+=============+==========+
| Id | CustomerId | ItemName |
+====+=============+==========+
| 1 | 2 | Modem |
+----+-------------+----------+
| 2 | 5 | Router |
+====+=============+==========+
I usually use the attributes to define my mapping to SQL directly on the entities (but I can see plenty of reasons why one wouldn't do that). Anyway, here is how to do it using attributes:
public class Order
{
[Key]
public int Id { get; set; }
[Column]
public string ItemName { get; set; }
[Column]
public int? CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
UPDATE
Something like
HasKey(m => m.Id);
HasOptional(m => m.Order)
.WithOptionalPrincipal(e => e.Customer)
.Map(m => m.MapKey("CustomerId"));
might do if you don't want to use attributes.

How to create Lookup table and define relationships

As you can see at below, there are a Lookup table for the enum values and I want to create a relationship between a table's enum values and LookupKey column of the Lookup table (instead of ID column of the Lookup table).
Lookup table:
ID | LookupType | LookupKey | LookupValue |
101 | Status | 0 | Passive |
106 | Gender | 1 | Male |
113 | Status | 1 | Active |
114 | Gender | 2 | Female |
118 | Status | 2 | Cancelled |
Main Table:
ID | Status | Gender | Name | ...
1 | 0 | 1 | John Smith | ...
2 | 1 | 2 | Christof Jahnsen | ...
3 | 2 | 1 | Alexi Tenesis | ...
4 | 0 | 2 | Jurgen Fechtner | ...
5 | 1 | 2 | Andreas Folk | ...
However, when using PK-FK relation and InverseProperty as on DataAnnotations - InverseProperty Attribute the relation is created with the ID column of the Lookup table and I cannot make the relation to the LookupKey column. Could you give an example how to achieve this?
We have a common lookup table here. It looks simlar to yours. LookupData has a primary key and a foreign key to LookupTypes which is equivalent to your enum and the value. We might also have some other simple fields like a flag or code which are identified in the LookupType metadata table. Then in out main table we might have "GenderLookupId" which points to the LookupData.Id field. The IDs themselves have no meaning and can be entered in any order. If you want gender 1 and 2 to have meaning, you should probably add another attribute for that (see surrogate keys).
Example with data:
LookupType
ID Description CodeDesc BooleanDesc
1 Genders Gender Code NULL
2 Races Race Code Is Active
LookupData
ID LookupTypeId Description Code Boolean
789 1 Male M NULL
790 2 White W True
791 1 Female F NULL
792 2 Hispanic H False
Main Name Table
NameId Name GenderLookupId RaceLookupId
1234 Joe Smith 789 790
1235 Mary Meyers 791 792
Classes:
public class LookupType
{
public int Id { get; set; }
public string Description { get; set; }
public string CodeDescription { get; set; }
public string BooleanDescription { get; set; }
}
public class LookupData
{
public int Id { get; set; }
public int LookupTypeId { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public bool? BooleanValue { get; set; }
public LookupType LookupType { get; set; }
}
public class Name
{
public int Id { get; set; }
public string FullName { get; set; }
public int? GenderLookupId { get; set; }
public LookupData Gender { get; set; }
}
LookupData Config:
HasRequired(p => p.LookupType).WithMany(p=>p.LookupData).HasForeignKey(p=>p.LookupTypeId).WillCascadeOnDelete(false);
Name Config:
HasOptional(p => p.Gender).WithMany(p=>p.Name).HasForeignKey(p=>p.GenderLookupId).WillCascadeOnDelete(false);

How to define matching column in InverseProperty

In my MVC application, I have an entity relation between Student and Lookup tables with StatusID-LookupKey and GenderID-LookupKey pairs inestead of (StatusID-ID and GenderID-ID pairs). However, not being able to define the LookupKey column as the matching parameter with the StatusID and GenderID, I have to use ID column of Lookup table. How can I use InverseProperty and match the related columns to the LookupKey column? Thanks in advance.
Here are the Lookup table and the entities below:
Lookup Table:
ID | LookupType | LookupKey | LookupValue |
1 | Status | 0 | Passive |
2 | Gender | 1 | Male |
3 | Status | 1 | Active |
4 | Gender | 2 | Female |
5 | Status | 2 | Cancelled |
Lookup Entity:
public class Lookup
{
[Key]
public int ID { get; set; }
public string LookupType { get; set; }
public int LookupKey { get; set; }
public string LookupValue { get; set; }
public virtual ICollection<Student> Students { get; set; }
//This works when using PK (ID) as matching column.
public ICollection<Student> StatusLookupStudents { get; set; }
public ICollection<Student> TermLookupStudents { get; set; }
}
Student Entity:
public class Student
{
[Key]
public int ID { get; set; }
//Foreign key for Status
public int StatusID { get; set; }
//Foreign key for Gender
public int GenderID { get; set; }
//This works when using PK (ID) as matching column. But I want to use LookupKey column for matching
[ForeignKey("StatusID")]
[InverseProperty("StatusLookupStudents")]
public virtual Lookup StatusLookup { get; set; }
[ForeignKey("TermID")]
[InverseProperty("TermLookupStudents")]
public virtual Lookup TermLookup { get; set; }
}

How to map a relation in entity framework

I have 3 tables
================= ================= =================
| TableA | | RelationAC | | TableC |
================= ================= =================
| IdA | | Id | | IdC |
| | | IdA | | |
| Field1A | | IdC | | Field1C |
================= ================= =================
And this are my model's code:
public partial class TableA
{
public TableA()
{
this.RelationAC = new HashSet<RelationAC>();
}
[Key]
public decimal IdA { get; set; }
public string Field1A { get; set; }
[ForeignKey("IdA")]
public virtual ICollection<RelationAC> RelationAC { get; set; }
}
public partial class TableC
{
[Key]
public decimal IdC { get; set; }
public string Field1C { get; set; }
}
public partial class RelationAC
{
public RelationAC ()
{
this.TableC= new HashSet<TableC>();
}
[Key]
public decimal Id { get; set; }
public decimal IdA{ get; set; }
public decimal IdC{ get; set; }
[ForeignKey("IdC")]
public virtual ICollection<TableC> TableC { get; set; }
}
If made this query
var query = from d in db.TableA
select d;
foreach( TableA ta in query.Tolist())
{
foreach(RelationAC rac in ta.RelationAC.Tolist())
{
TableC tc = rac.TableC.First(); // It allways has count = 0 , even my db has data
}
}
Why TableC tc is allways empty ?
Your property is probably having issues w/ lazy loading. Try eager loading the collection:
db.TableA.Include("RelationAC.TableC")

Resources