Using AutoBogus to set member property value range - range

I have the following classes:
Class Person
Property ID As String = Nothing
Property Firstname As String = ""
Property Lastname As String = ""
End Class
Class Account
Property AccountNumber As String = ""
Property Owners As New List(Of Person)
End Class
I wish to you utilize https://github.com/nickdodd79/AutoBogus to set a range of values from 1,000 to 10,000 for Person.ID when I instantiate an instance of the Account class like so:
Dim fk = AutoFaker.Create()
Dim acct = fk.Generate(Of Account)
Please how may I do this using AutoBogus?

The original design of AutoBogus was to generate small object graphs for unit tests. Generating the numbers you require could have a performance impact. However, if the above is what you are trying to generate and nothing more complex, then it could be small enough to succeed.
To use AutoBogus out the box you can do the following:
Dim acct = AutoFaker.Generate(Of Account, 1000)
The second parameter should be the number of accounts you want to create.
AutoBogus uses Bogus under the hood and then leverages Reflection to populate unset properties. If you do see any performance issues, you could use Bogus directly with the caveat that you need define a RuleFor for each property. The Bogus docs provide in-depth details on how to achieve this.
Nick.

Solution modified from answer provided by Bogus author bchavez at https://github.com/bchavez/Bogus/issues/394.
Sub Main
Dim personFaker = New AutoFaker(Of Person)
personFaker.RuleFor(Function(p) p.Firstname, Function(f) f.Name.FirstName)
.RuleFor(Function(p) p.Lastname, Function(f) f.Name.LastName)
.RuleFor(Function(p) p.ID, Function(f) f.Random.Int(1000,10000).ToString)
Dim accountFaker = New AutoFaker(Of Account)
accountFaker.RuleFor(Function(a) a.AccountNumber, Function(f) f.Random.Replace("###############"))
.RuleFor(Function(a) a.Owners, Function(f) New List(Of Person)(personFaker.GenerateBetween(1,5)))
accountFaker.Generate().Dump()
End Sub
Class Person
Property ID As String = Nothing
Property Firstname As String = ""
Property Lastname As String = ""
End Class
Class Account
Property AccountNumber As String = ""
Property Owners As New List(Of Person)
End Class

Related

Class of a Class

I'm getting just killed trying to make a class of a class. I have shopped around the site and seen several examples but maybe because its 1:43 I am having a hard time understanding them.
I was successfully able to use a class to automate a huge data entry project at work. I created a class called catDist which is the category distribution of types of agricultural products a company could manufacture or sell.
catDist contains six properties:
Private selfWorth As String
Private Q1 As Double
Private Q2 as Double
Private Q3 as Double
Private Q4 As Double
Private activated As Boolean
They all have the standard get and let codes.
There are 48 possible categories. I have a module that creates 48 instances of them with 48 different values for selfWorth (e.g "Cottonseed", or "maize" etc), and sets Q1 through Q4 as 0 . The module originally worked with a Userform that I could type in the values and hit enter. If it saw that I had entered a value inside a particular textbox (yes there were 48X4 textboxes) it would set activated to true and changes the relevant Q's to the values I entered.
WHAT I WANT TO DO NOW.
It was a great success. Now what I want to do is create a class called "Distributor". Each distributor class would have 4 collections have catDist objects. I can create the distributor class. I can create the catDist class. But for the love of God I can not figure out a way to set the corresponding distributor catDist property to the catDist value I used in the Set method.
Sub testRegist()
Dim registrant As testRegistrant
Set registrant = New testRegistrant
registrant.registNum = "Z000123"
'MsgBox (registrant.registNum)
Dim cowMilk As testcatDist
Set cowMilk = New testcatDist
cowMilk.selfWorth = "Cow Milk"
cowMilk.distribution = 4.6
registrant.testCat = cowMilk
Debug.Print registrant.testCat.selfWorth
End Sub
catDist Class
Private pselfWorth As String
Private pdistribution As Double
Public Property Get selfWorth() As String
selfWorth = pselfWorth
End Property
Public Property Let selfWorth(name As String)
pselfWorth = name
End Property
Public Property Get distribution() As Double
distribution = pdistribution
End Property
Public Property Let distribution(dist As Double)
pdistribution = dist
End Property
Registrant a.k.a distributor class
Private pRegistNum As String
Private pCatDist As testcatDist
Public Property Get registNum() As String
registNum = pRegistNum
End Property
Public Property Let registNum(registration As String)
pRegistNum = registration
End Property
Public Property Get testCat() As testcatDist
testCat = pCatDist
End Property
Public Property Let testCat(cat As testcatDist)
Set pCatDist = New testcatDist
pCatDist = cat
End Property
The only problem I see is that you are using Let instead of Set. In VBA you use Set when assigning to objects.
When you write registrant.testCat = cowMilk (in your Sub), testCat = pCatDist (in the getter of testRegistrant.testCat) and pCatDist = cat (in the setter of testRegistrant.testCat) you are implicitly using Let (it's as if you had written Let registrant.testCat = cowMilk) instead of (explicitly) using Set.
So, if you write Set registrant.testCat = cowMilk in your test Sub, Set testCat = pCatDist in the getter and Set pCatDist = cat in the setter you should be good to go.
Also, in the same setter, the initialization of pCatDist isn't needed since you are passing cat to it in the next line.
And, as #GSerg (thank you) says, the signature of your setter should be Public Property Set testCat(cat as testcatDist) instead of Public Property Let.

combining classes in vba?

I have a problem finding the right structure of my programme and I am trying to solve it using classes, without success.
I have 1 class for Patients, each Patient has the following:
Name
Operation
Surgeon
OperationDuration
Then each Surgeon should have a schedule for everyday.
Therefore, I am thinking using another class for daily schedule, which should have :
Day
TotalDuration
Something in my thinking doesn't look correct and I am struggling to understand what should I do.
1)Is my structure correct for what I want to do?
2)How can I check if a surgeon has a planned daily schedule and if he doesn't then add a patient to his schedule?
Any help would be much appreciated.
Thanks,
George
Class Patients
Private mstrName As String
Private mstrOperationDescription As String
Private mlngSurgeon As Long
Private mdblOpDuration As Double
Public Property Get Name() As String
Name = mstrName
End Property
Public Property Get OperationDescription() As String
OperationDescription = mstrOperationDescription
End Property
Public Property Get Surgeon() As Long
Surgeon = mlngSurgeon
End Property
Public Property Get OpDuration() As Double
OpDuration = mdblOpDuration
End Property
Class Schedule
Private mlngDay As Long
Private mdblTotalDuration As Double
Public Property Get Day() As Long
Day = mlngDay
End Property
Public Property Let Day(ByVal lDay As Long)
mlngDay = lDay
End Property
Public Property Get TotalDuration() As Double
TotalDuration = mdblTotalDuration
End Property
Public Property Let TotalDuration(ByVal dTotalDuration As Double)
mdblTotalDuration = dTotalDuration
End Property
Test Sub calculating the total duration but I am not able to list them according to days count
Public Sub Test()
Dim mydata As New clsData
Dim schedule1 As New clsSchedule
Dim schedule2 As New clsSchedule
Dim i As Integer
mydata.InputData
For i = 1 To mydata.PatientCount
If mydata.patient(i).Surgeon = 1 Then
schedule1.TotalDuration = schedule1.TotalDuration + mydata.patient(i).OpDuration
Else
schedule2.TotalDuration = schedule2.TotalDuration + mydata.patient(i).OpDuration
End If
Next i
MsgBox "Total Duration is: " & schedule1.TotalDuration
End Sub
One solution would be to have a Collection of Surgeon objects and a Collection of Patient objects. Each Patient has a Surgeon as a property, and each Surgeon has a Collection of Patients.
From this you can find out the total operation duration across all patients belonging to a Surgeon.
The trick is to know how the objects relate to each other - in this case each Patient has one Surgeon, and each Surgeon can have many Patients. This leads quite logically to the following structure:
Patient
Private mstrName As String
Private mstrOperationDescription As String
Private mobjSurgeon As cSurgeon
Private mdblOpDuration As Double
Property Let Name(txt As String)
mstrName = txt
End Property
Property Let OperationDescription(txt As String)
mstrOperationDescription = txt
End Property
Property Let Surgeon(objSurgeon As cSurgeon)
Set mobjSurgeon = objSurgeon
End Property
Property Let OpDuration(num As Double)
mdblOpDuration = num
End Property
Property Get OpDuration() As Double
OpDuration = mdblOpDuration
End Property
Surgeon
Private mstrName As String
Private mlngSurgeonId As Long
Private mcolPatients As Collection
Private Sub Class_Initialize()
Set mcolPatients = New Collection
End Sub
Property Let Name(txt As String)
mstrName = txt
End Property
Property Get Name() As String
Name = mstrName
End Property
Property Let IdNumber(num As Long)
mlngSurgeonId = num
End Property
Property Get IdNumber() As Long
IdNumber = mlngSurgeonId
End Property
Sub AddPatient(objPatient As cPatient)
mcolPatients.Add objPatient, objPatient.Name
End Sub
Function TotalHours() As Double
Dim objPatient As cPatient
For Each objPatient In mcolPatients
TotalHours = TotalHours + objPatient.OpDuration
Next objPatient
End Function
Test Routine
Sub CheckSurgeonHours()
Dim colSurgeons As Collection
Set colSurgeons = New Collection
Dim colPatients As Collection
Set colPatients = New Collection
'Populate Surgeon and Patient collections from input data
'This is the static data for each object, i.e. name, Id, operation type/duration
Dim objSurgeon As cSurgeon
For Each objSurgeon In colSurgeons
Dim objPatient As cPatient
For Each objPatient In colPatients
objPatient.Surgeon = objSurgeon
objSurgeon.AddPatient objPatient
Next objPatient
Debug.Print objSurgeon.TotalHours
Next objSurgeon
End Sub

classic asp/vbscript class to track employees and performance metrics

I am trying to create a classic asp/vbscript class that will allow me to easily manage a small number of employees (30-40) along with some metrics associated with those employees, about 14 metrics each. I've done some tutorials online and can't quite get how I should proceed. What I have so far is below. It's not much, basically I think I can only add the employees to a dictionary in the class, but I don't know where to go from here.
class iagent
private di_agents
private ar_metrics
private pri_agent_counter
Public function add_agent(uuid)
di_agents.Add uuid, pri_agent_counter
pri_agent_counter=pri_agent_counter+1
end function
private sub Class_initialize
pri_agent_counter=1
dim ar_metrics(14, 5)
set di_agents = CreateObject("Scripting.Dictionary")
end sub
end class
The class you have is just a wrapper around a dictionary. Are you talking about creating a class that represents an employee?
Class Employee
Public Name
Public Age
Public Phone
'other properties
End Class
Then you can instantiate Employee like this and set your properties
Set e = New Employee
e.Name = "Some name"
You could then store your instances of Employee in a dictionary, perhaps paired with an ID:
Set d = CreateObject("Scripting.Dictionary")
Call d.Add(uuid, e)
However, you're better off using a database for this and using ASP/VBS to extract records... Unless this is just an exercise

Explicit Loading of child navigation properties with criteria

Using DBContext in EF5 - after filtering and partial loading based on criteria like a date range.
I'm trying to produce a complete graph or tree of objects - Persons->Events where the only Events that are included are within a date range. All this whilst preserving the standard change tracking that one gets with the following:
Dim Repository As Models.personRepository = New Models.personRepository
Private Sub LoadData()
Dim personViewModelViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("personViewModelViewSource"), System.Windows.Data.CollectionViewSource)
Repository.endDate = EndDate.SelectedDate
Repository.startDate = StartDate.SelectedDate
personViewModelViewSource.Source = Repository.collectionOfpersons
End Sub
A listbox and a datagrid are both bound as a proper datasource. The POCO template has been modified to put INotifyProperty events in the navigation property class, Events.
I've been fighting with this for days now and filtering whether on Lazy loading or Explicit loading does not function. After masses of blog/chaper reading, I'm aware of the rather unreal limitation relating to Include; instead I'm trying explicit loading. I'm using the DBContext book btw.
Being unable to bring back only a subset of the Event data from the database is 100% deal breaker as there is likely to be hundreds of thousands of Events per Person. It doesn't make sense to me or my boss that the Entity Framework doesn't make this functionality fairly obvious-are we missing something?
I've left in the commented code to try and illustrate some of the paths I've attempted. The class itself is a repository that this method belongs to. I'll edit this question further to clarify just how many routes I've tried as it's been a LOT. The View uses a repository layer and a ViewModel so the code behind on the XAML is rather minimal.
In advance for any help, thank you!
Public Overridable ReadOnly Property AllFiltered(startdate As Date, enddate As Date) As ObservableCollection(Of person) Implements IpersonRepository.AllFiltered
Get
Dim uow = New UnitOfWork
context = uow.Context
Dim personQuery = context.persons.Include(Function(p) p.events).AsQueryable.Where(Function(x) x.personID = 10).FirstOrDefault
'Dim eventQuery = From e In context.Notes
' Where e.eventDateTime >= startdate And e.eventDateTime <= enddate
' Select e
'Dim personQuery As person = From r In context.persons
' From e In eventQuery
' Where r.personID = e.personID
' Select r, e
Dim singleperson = personQuery
'For Each r As person In personQuery
' persons.Add(r)
'Next
' context.Entry(eventQuery).Collection()
' context.Entry(personQuery).Reference(personQuery).Load()
context.Entry(singleperson).Collection(Function(d) d.events).Query().Where(Function(x) x.eventDateTime > startdate And x.eventDateTime < enddate).Load()
Return context.persons.Local
End Get
End Property
Note: I'm using logging/exception handling via PostSharp rather than polluting the code.
Below are some of the errors I've generated with previous paths taken.
The entity type DbQuery`1 is not part of the model for the current context.
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery1[VB$AnonymousType_02
[Entity.Person,Entity.Notes]]' to type 'System.Collections.ObjectModel.ObservableCollection`1[Entity.Person]'.
UPDATE: Yet another route I've tried, still cannot get this to fly either:
Private Property _collectionOfPersons As ObservableCollection(Of Person)
Public ReadOnly Property collectionOfPersons As ObservableCollection(Of Person)
Get
For Each Person In context.Persons
_collectionOfPersons.Add(ReturnSinglePerson(startDate, endDate, Person.PersonID))
Next
Return _collectionOfPersons.Where(Function(x) x.events.Where(Function(e) e.eventDateTime > startDate And e.eventDateTime < endDate))
End Get
End Property
Public Overridable ReadOnly Property SinglePerson(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
Dim PersonQuery = context.Persons.Include(Function(p) p.events).AsQueryable.Where(Function(x) x.PersonID = 10).Select(Function(x) x).FirstOrDefault
Dim Person = PersonQuery
context.Entry(Person).Collection(Function(d) d.events).Query().Where(Function(x) x.eventDateTime > startdate And x.eventDateTime < enddate).Load()
Return context.Persons.Local
End Get
End Property
Public Function ReturnSinglePerson(startdate As Date, enddate As Date, id As Integer)
Dim PersonQuery = context.Persons.Include(Function(p) p.events).AsQueryable.Where(Function(x) x.PersonID = id).Select(Function(x) x).FirstOrDefault
Dim Person = PersonQuery
Return Person
End Function
Another shot:
Public Overridable ReadOnly Property FilteredPersons(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
context.Persons.Load()
Dim DateCriteria = Function(e) e.events.Where(Function(d) d.eventDateTime > startdate And d.eventDateTime < enddate)
Dim Res = New ObservableCollection(Of Person)
For Each Person In context.Persons.Local.Select(Function(x) x).Where(DateCriteria)
Res.Add(Person)
Next
Return Res
End Get
End Property
Gives:
Public member 'Where' on type 'ObservableCollection(Of DailyNotes)' not found.
Tantalisingly close, only I get lots of duplicate names on the listbox - but the navigation carries through and the Date criteria work.
<ExceptionAspect>
Public Overridable ReadOnly Property FilteredPersons(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
context.Persons.Load()
Dim test = From r In context.Persons
From e In context.Notes
Where e.eventDateTime > startdate And e.eventDateTime < enddate
Join rr In context.Persons On e.PersonID Equals rr.PersonID
Select r, e
Dim Res = New ObservableCollection(Of Person)
For Each Person In test
Res.Add(Person.r)
Next
Return Res
End Get
End Property
Don't try this one :). It simply selects the child properties only.
Public ReadOnly Property collectionOfResidents As ObservableCollection(Of resident)
Get
For Each resident In context.residents
_collectionOfResidents.Add(ReturnSingleResident(startDate, endDate, resident.residentID))
Next
Return _collectionOfResidents.Select(Function(x) x.events.Where(Function(e) e.eventDateTime > startDate And e.eventDateTime < endDate))
End Get
End Property
I'm hoping that adding my other attempts to this question may prompt both other answers and help others see the circles they can get into when first tackling this!
You can use the Select clause for finer control than with Include
Something like this:
context
.Persons
.Where( ... some predicate on Person ... )
.Select( o => new
{
Person = o,
Events = o.Events.Where( ... some predicate on Event ... )
}
)
;
This will translate both predicates into SQL which execute on the database server.
Ok after a LOT of fiddling and misunderstanding of anonymous types this evening, I think I succeeded. Nicholas's answer just needed to be done in VB which took me a while - I've not used anonymous types before.
This is what appears to work fine in my repository layer:
<ExceptionAspect>
Public Overridable ReadOnly Property FilteredPersons(startdate As Date, enddate As Date) As ObservableCollection(Of Person) Implements IPersonRepository.AllFiltered
Get
context.Persons.Load()
Dim test = context.Persons.Where(Function(r) r.PersonActive).Select(Function(o) New With { _
.Person = o, _
.events = o.events.Where(Function(e) e.eventDateTime > startdate) _
})
Dim PersonList= New ObservableCollection(Of Person)
For Each t In test
PersonList.Add(t.person)
Next
Return PersonList
End Get
End Property
The crucial updating/saving in the wpf View is intact, I'm really happy and grateful to Nicholas for his help on here (and patience...re:cartesion product). So thank you. I hope this helps someone else!

How to determine if an Entity with relationship properties has changes

Dim myEmployee as Employee = myObjectContext.Employee.Where("it.EmployeeID = 1").First()
The following line will cause e.EntityState to equal EntityState.Modified :
myEmployee.Name = "John"
However, changing a property that is a relationship will leave e.EntityState = EntityState.Unchanged. For example:
myEmployee.Department = myObjectContext.Department.Where("it.DepartmentName = 'Accounting'").First()
How can I tell if myEmployee has changes? I need to know so I can log changes made to the Employee record for auditing purposes.
There is a way to get the state of a relationship, but it is not as easy to obtain as the state of an entity.
ObjectContext.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState state)
returns IEnumerable<ObjectStateEntry> with entries for both, entities and relationships (there is IsRelationship property on ObjectStateEntry so you can determinate if it's relationship or entity).
I tested out with with your example when relationship is changed the way you do
myEmployee.Department = myObjectContext.Department.Where("it.DepartmentName = 'Accounting'").First()
and I find out by calling GetObjectStateEntries for each possible EntityState that one ObjectStateEntry is added with state Added:
myObjectContext.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added)
Now, you can peek at the current values of the state entry to see if they match the ends of the relationship (not nice). However, it's a bit complicated and I'm not sure if it's going to meet your needs in every case.
i was having a similar issue when i was trying to validate in Entity framework:
After researching a little bit i found a solution:
(see im posting the whole validation solution)
Interface for validation:
Interface IValidatable
Function Validate(Optional ByVal guardando As Boolean = False) As List(Of ApplicationException)
End Interface
Handling the SavingChanges event in a partial class:
Partial Class FacturacionEntities
Private Sub FacturacionEntities_SavingChanges(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.SavingChanges
Dim objects As New List(Of System.Data.Objects.ObjectStateEntry)
objects.AddRange(Me.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
objects.AddRange(Me.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
Dim errors As New List(Of ApplicationException)
For Each obj In objects
If obj.IsRelationship Then
Dim fro = DirectCast(obj.CurrentValues(1), EntityKey)
Dim k As New EntityKey("FacturacionEntities." & fro.EntitySetName, fro.EntityKeyValues(0).Key, fro.EntityKeyValues(0).Value)
errors.AddRange(DirectCast(Contexto.Facturacion.GetObjectByKey(k), IValidatable).Validate())
Else
errors.AddRange(DirectCast(obj.Entity, IValidatable).Validate)
End If
Next
If errors.Count > 0 Then
Dim err_list As String = ""
For Each s In errors
err_list = err_list & s.Message & vbCrLf
Next
Throw New ApplicationException(err_list)
End If
End Sub
End Class
Please note than "Contexto.Facturacion" is an instance of the Entities class generated by Entity framework engine.