F#: Why those two collections are not equal? - c#

Was writing some units using XUnit until that at some points I bumped into something surprising:
let id = Guid.Empty
let contact = {
Name = {
FirstName = "Marcel"
MiddleInitial = None
LastName = "Patulacci"
}
DateOfBith = new DateTime(1850, 12, 25)
Address = {
Address1 = "41 av 8 Mai 1945"
Address2 = None
City = "Sarcelles"
State = None
Zip = "95200"
}
PhoneNumber = {
DialOutCode = 33
LocalNumber = "766030703"
}
Email = "marcel.patulacci#outlook.com"
}
[<Fact>]
let ``Open an account...``() =
let event = Event.AccountOpened({
AccountId = id
Contact = contact
})
let a = [event]
let b = seq { yield event }
Assert.Equal(a, b)
System.NullReferenceException : Object reference not set to an instance of an object.
It seems surprising especially since considering that the overload used by Assert is:
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual)
Which states that:
Verifies that two sequences are equivalent, using a default comparer.
Why are they considered different, and why does Assert.Equal raise a System.NullReferenceException?
[EDIT]
System.NullReferenceException : Object reference not set to an instance of an object.
at Domain.Events.AccountOpenedEvent.Equals(Object obj, IEqualityComparer comp)
at Domain.Events.Event.Equals(Object obj, IEqualityComparer comp)
Seems
type PersonalName = {
FirstName: string;
MiddleInitial: string option;
LastName: string;
}
type Address = {
Address1: string;
Address2: string option ;
City: string;
State: string option;
Zip: string;
}
type PhoneNumber = {
DialOutCode : int;
LocalNumber: string
}
type Contact = {
Name: PersonalName;
DateOfBith: DateTime
Email: string;
Address: Address;
PhoneNumber: PhoneNumber
}
type AccountOpenedEvent = {
AccountId: Guid
Contact: Contact
}
type Event =
| AccountOpened of AccountOpenedEvent
It turns out one of the fields of event was null, but not event itself.

The problem resided in the id and contact that were defined right above the test / [<Fact>]:
let id = Guid.Empty
let contact = {
Name = {
FirstName = "Marcel"
MiddleInitial = None
LastName = "Patulacci"
}
DateOfBith = new DateTime(1850, 12, 25)
Address = {
Address1 = "41 av 8 Mai 1945"
Address2 = None
City = "Sarcelles"
State = None
Zip = "95200"
}
PhoneNumber = {
DialOutCode = 33
LocalNumber = "766030703"
}
Email = "marcel.patulacci#outlook.com"
}
[<Fact>]
let ``Open an account...``() =
let event = Event.AccountOpened({
AccountId = id
Contact = contact
})
let a = [event]
let b = seq { yield event }
Assert.Equal(a, b)
The thing is when running the test independently the id and contact are not initialized, hence even though event was not null, contact was null (id being a Guid aka a struct it has a value anyway).
Since F# works with structural equality, if one of the field is not initialized it was enough to have a field null to make the Assert failed at some point in its implementation.
There are a few solutions / workarounds:
Defining those variables directly in the unit test body.
Defining methods which produce those values out of the unit test body
let getId() = Guid.Empty
let getContact() = {
Name = {
FirstName = "Marcel"
MiddleInitial = None
LastName = "Patulacci"
}
DateOfBith = new DateTime(1850, 12, 25)
Address = {
Address1 = "41 av 8 Mai 1945"
Address2 = None
City = "Sarcelles"
State = None
Zip = "95200"
}
PhoneNumber = {
DialOutCode = 33
LocalNumber = "766030703"
}
Email = "marcel.patulacci#outlook.com"
}
[<Fact>]
let ``Open an account...``() =
let id = getId()
let contact = getContact()
let event = Event.AccountOpened({
AccountId = id
Contact = contact
})
let a = [event]
let b = seq { yield event }
Assert.Equal(a, b)
While those workarounds work, I am surprised that the variables declared right above the unit test function are not considered when the test is running / and are uninitialized.
It might worth to shoot another question about why this is the case.
This is surprising in the sense that if a function can be defined and returning pretty much the same thing as those variables it means that let is also properly compiled, so why this is not the case with the variables?

Related

if else statement inside a local variable/class

Why cannot I use if else statement inside this local variable account? Inside the "if" I'm planning to enter the code IsActive = "Yes", then inside the "else" IsActive = "No". But the IsActive string that I declared in the class is not appearing/suggesting.
private void btnAdd_Click(object sender, EventArgs e)
{
UserAccount account = new UserAccount()
{
FirstName = txtFirstName.Text,
LastName = txtLastName.Text,
Email = txtEmail.Text,
TeamName = txtTeamName.Text,
Password = txtPassword.Text,
// IsActive = "Yes",
if (chkIsActive.Checked == true)
{
}
else
{
}
UserId = int.Parse(txtUserId.Text)
};
}
You can't use an if else statement inside an object initialization. You can only assign to public writable (that is, properties having a setter) properties there.
As suggested in one comment below, starting from C# 9 you can also set init-only properties when using object initialization syntax. These properties can be written only during object construction, once the object construction phase is completed you are not allowed to write the property.
Regardless of the C# language version, you can't use any control flow statement in object initialization.
A working version of your code is the following:
string isActive = chkIsActive.Checked ? "Yes": "No";
var account = new UserAccount()
{
FirstName = txtFirstName.Text,
LastName = txtLastName.Text,
Email = txtEmail.Text,
TeamName = txtTeamName.Text,
Password = txtPassword.Text,
IsActive = isActive,
UserId = int.Parse(txtUserId.Text)
};
You are trying to use control statement inside the class initializer. You cannot do that.
You can use ternary operator.
UserAccount account = new UserAccount()
{
FirstName = txtFirstName.Text,
LastName = txtLastName.Text,
Email = txtEmail.Text,
TeamName = txtTeamName.Text,
Password = txtPassword.Text,
//IsActive = "Yes",
IsActive = chkIsActive.Checked ? "Yes" : "No",
UserId = int.Parse(txtUserId.Text)
};
Don't use boolVariable == true, just use boolean itself. Only exception is a nullable boolean.
You can do this a few ways, the following is an example of using a ternary condition to set the property value:
private void btnAdd_Click(object sender, EventArgs e)
{
UserAccount account = new UserAccount()
{
FirstName = txtFirstName.Text,
LastName = txtLastName.Text,
Email = txtEmail.Text,
TeamName = txtTeamName.Text,
Password = txtPassword.Text,
IsActive = chkIsActive.Checked ? "Yes" : "No",
UserId = int.Parse(txtUserId.Text)
};

SQLite on C#. Select query does not work with string of letters

I'm learning C# since the past week and databases since a couple of days ago, so if you see something dodgy and you ask yourself "Why did he do that like that?", the answer is probably going to be "Because that's all I know for the moment".
In the title I said "of letters" because if I use a string of numbers, it works.
I have a tiny database with three columns. Id (int), FirstName (text) and LastName (text).
Id is unique, primary key and autoincrements. FirstName is unique. The three are not null.
In that database I have two records:
Id FirstName LastName
3- 6666 2222
4- Test O'Test
This is my method:
public static bool isOnDb(string nombre, string apellido)
{
bool flag = false;
{
try
{
using (IDbConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
string tempName = "Test"; // This is temporarily replacing the argument 'nombre'
int tempNum= 3; // More testing. See below
//cnn.Query<Person>($"select * from Person where FirstName = { tempName }", new DynamicParameters());
// This four lines below are just for testing. They are going to be deleted
var output = cnn.Query<Person>($"select * from Person where FirstName = { tempName }", new DynamicParameters());
var person = output.First();
Console.WriteLine("Funciona");
Console.WriteLine($"{ person.Id } - { person.FullName }");
flag = true;
return flag;
}
}
catch (Exception)
{
Console.WriteLine("Derecho a excepcion");
return flag;
}
}
}
Basically, if tempName = "Test", it falls into an exception. But if tempName = "6666" it returns the row.
I also tried selecting by id. That's why tempNum is there.
int tempNum= 4;
var output = cnn.Query<Person>($"select * from Person where Id = { tempNum }", new DynamicParameters());
Well in SQLite the strings are signified by '' so when you pass FirstName = { tempName } and its FirstName = Test it looks for the column called Test in Table person rather than equating it to the 'Test' string
So you can do:
var output = cnn.Query<Person>($"select * from Person where FirstName = '{ tempName }'");
Or better yet:
var people = cnn.Query<Person>("SELECT * FROM PERSON WHERE FirstName = #FirstName", new { FirstName = tempName });

Implicit ViewModel generation from LINQ?

In my project I have a GenericImageViewModel which is used by many entities.
Example of getting the ASP User Entity:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = a.UserProfile.ProfileImage
});
The ProfileImage is the GenericImageViewModel and has an implicit operator as follows:
public static implicit operator TRDGenericImageViewModel(TRDImage image)
{
return new TRDGenericImageViewModel
{
Id = image.Id,
AspectRatio = image.Ratio,
Url = image.Url,
};
}
If I run the query Entity Framework throws an exception:
"Unable to cast the type 'TRDImage' to type 'TRDGenericImageViewModel'. LINQ to Entities only supports casting EDM primitive or enumeration types."
If I create the GenericImageViewModel manually for each ViewModel everything is running fine:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel {
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
});
But in this case I have to copy and paste the TRDGenericImageViewModel generation in every ViewModel and thats not the way it should goes. If something changed I have to modify all related classes.
So is there a way to avoid this exception?
Using .AsEnumerable() prior lambda is not possible due to selects later in that query.
var query = UserRepository
.Get(Id)
.AsEnumerable()
.Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel
{
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
Statistics = new TRDUserStatisticsViewModel
{
PostCount = a.Posts.Count(),
CommentCount = a.Comments.Count(),
ImageCount = a.Images.Count(),
VideoCount = a.Videos.Count(),
VoteCount = a.PostVotes.Count(),
}
});
When calling .AsEnumerable() only the entities included by the Include statement are counted. But if the user has more than 1000 Posts and 10.000 Votes the query is a data nightmare.

Accessing data members of an inherited class

So I have 4 classes: Employee (base class), PartTime : Employee, FullTime : Employee, Manager : Employee. I'm trying to access unique but can't figure out exactly how. I tried casting but that didn't work. Here's what I have so far.
Employee emp1 = new Manager();
emp1.FirstName = txtFirst.Text;
emp1.LastName = txtLast.Text;
emp1.Ssn = Convert.ToInt32(txtSSN.Text);
emp1.HireDate = Convert.ToInt32(txtHire.Text);
emp1.TaxRate = Convert.ToDecimal(txtTax.Text);
emp1.Email = txtEmail.Text;
emp1.PhoneNum = Convert.ToInt32(txtPhone);
if (emp1 is PartTime)
{
emp1.HourlyRate = txtRate.Text;
emp1.HoursWorked = txtHrs.Text;
}
if (emp1 is FullTime)
{
emp1.Salary = Convert.ToDecimal(txtSalary.Text);
emp1.VacationDays = Convert.ToDouble(txtVacation.Text);
emp1.SickDays = Convert.ToDouble(txtSick.Text);
emp1.IsTaxExempt = comboTax.SelectedIndex == 0 ? true : false;
emp1.HasInsurance = comboInsurance.SelectedIndex == 0 ? true : false;
}
if (emp1 is Manager)
{
(Manager)emp1.BonusEarned = Convert.ToDecimal(txtBonus.Text);
(Manager)emp1.Department = comboDepartment.SelectedText;
(Manager)emp1.OfficeLocation = txtOffice.Text;
}
In this example, Manager has the properties BonusEarned, Department, and OfficeLocation but Employee, FullTime, and PartTime don't.
Try this (pay attention to the parenthesis):
((Manager)emp1).BonusEarned = Convert.ToDecimal(txtBonus.Text);
((Manager)emp1).Department = comboDepartment.SelectedText;
((Manager)emp1).OfficeLocation = txtOffice.Text;
Ugh, I think that is just invalid syntax. You're doing cast in the LHS of an assignment statement... It doesn't work like that. Cast has to be on the RHS so the result can be assigned. Instead try something like this;
if (emp1 is Manager)
{
var man = (Manager)emp1
man.BonusEarned = Convert.ToDecimal(txtBonus.Text);
man.Department = comboDepartment.SelectedText;
man.OfficeLocation = txtOffice.Text;
}

Add new customer to database

I am able to successfully create a token, then create a new customer enrolling them in a particular subscription plan.
I cannot figure out how to capture the customer_id that is created for them by Stripe. I need this in order to make changes later (change plan, update CC, etc.). The code I have for creating the customer is below (I use a reader to get fname, lname, etc. and excluded that here for brevity):
private StripeCustomer CreateCustomer()
{
NameValueCollection nvc = Request.Form;
string tokenID = nvc["stripeToken"];
if (tokenID != null)
{
var tokenService = new StripeTokenService();
StripeToken stripeToken = tokenService.Get(tokenID);
}
var myCustomer = new StripeCustomerCreateOptions();
myCustomer.Email = email;
myCustomer.Description = fname + " " + lname + " (" + email + ")";
myCustomer.TokenId = tokenID;
string plan = "basic";
myCustomer.PlanId = plan;
var customerService = new StripeCustomerService();
StripeCustomer CurrentCustomer = customerService.Create(myCustomer);
}
Maybe I am thinking about this incorrectly (must be), but I was looking for the token to provide the CustomerID initially. Here is what that provides:
id: tok_102znI2MdvjLMWitzgclEEcg
livemode: false
created: 1385241151
used: false
object: "token"
type: "card"
card:
id: card_102znI2MdvjLMWitq44B0MY7
object: "card"
last4: "4242"
type: "Visa"
exp_month: 12
exp_year: 2021
fingerprint: "V2WUOPIgMkP5DGGe"
customer: null
country: "US"
name: null
address_line1: null
address_line2: null
address_city: null
address_state: null
address_zip: null
address_country: null
The token is passed back to me before the customer is created, so that obviously doesn't work.
Chris F had the right idea.
After
StripeCustomer CurrentCustomer = customerService.Create(myCustomer);
I simply needed to add something like
string custid = CurrentCustomer.id;
Then add custid to my database.
You can do this also by :
StripeCustomer currentCustomer = customerService.Create(myCustomer);
currentCustomer.StripeCardList.StripeCards.FirstOrDefault().CustomerId;

Categories