Associations strange behavior - vb.net

I found a following issue regarding EF5 running under .NET 4.0, in Model First approach:
I have following auto-generated entity:
Partial Public Class Notice
Private _id As Integer
Public Property id As Integer
Get
Return _id
End Get
Friend Set(ByVal value As Integer)
_id = value
End Set
End Property
Public Property order_id As Integer
Public Property employee_id As Integer
Public Property sysdate As Datetime
Public Property content As String
Public Overridable Property order As Order
Public Overridable Property employee As Employee
End Class
Notice entity is associated with Order entity and Employee entity by 1 (Order, Employee) to many (Notice) relationship.
Afterward, Order entity has also association with Employee entity: many (Order) to 1 (Employee) relationship.
Then, I expect the following unit test to fail, because of Notice entity relation to Employee entity violation (I don't assign notice1.employee navigation property):
<TestMethod()>
<ExpectedException(GetType(DbUpdateException))>
Public Sub ShouldNotAllowSaveNoticeWithoutAssignedEmployee()
Dim notice1 = CreateNewNotice() ' returned entity has not set any relation
notice1.order= CreateNewOrderWithAllRequiredAndRelatedEntities()
DbContext.noticeSet.Add(notice1)
DbContext.SaveChanges()
End Sub
But in result test is passed. In Database, Notice->employee_id value is equal to Order->employee_id value, what is not expected, because these foreign keys may point to different Employee object. I expected that i have to set up notice1.employee navigation property myself, and I would like to get DbUpdateException exception if i forget to do it.
What is the reason of this strange EF5 behavior?
Update:
CreateNewNotice() and CreateNewOrderWithAllRequiredAndRelatedEntities() implementation:
Protected Function CreateNewNotice(Optional ByVal suffix As Int32 = 1) As Notice
Dim notice1 = New Notice() With {
.sysdate = DateTime.Now,
.content = Description & suffix ' Description is constant, for testing purposes
}
Return notice1
End Function
Protected Function CreateNewOrderWithAllRequiredAndRelatedEntities(Optional ByVal suffix As Int32 = 1) As Order
Dim order1 = CreateNewOrder(suffix) ' returned entity has not set any relation
order1.employee = CreateNewEmployee(suffix)
order1.customer = CreateNewCustomerWithAllRequiredRelatedEntities(suffix)
order1.seller = CreateNewSeller(suffix)
For i As Integer = 1 To 3
order1.notices.Add(CreateNewNotice(i)) ' created notices have not initialized relations
Next
Return order1
End Function

I have recognized this issue, and it looks like there is a different behavior of EF5 depending on whether we use 'foreign key associations' or 'independent associations' (some informations about relationships in EF: Relationships and Navigation Properties)
Lets assume that we use EF5 Model First Approach (with .NET 4.5). Lets take in consideration automatically generated entity classes, related to example showed up in topic's questions:
Partial Public Class Employee
Public Property id As Integer
Public Property firstname As String
Public Property lastname As String
Public Overridable Property Notices As ICollection(Of Notice) = New HashSet(Of Notice)
Public Overridable Property Orders As ICollection(Of Order) = New HashSet(Of Order)
End Class
Partial Public Class Order
Public Property id As Integer
Public Property Employee_id As Integer
Public Property article As String
Public Overridable Property Notices As ICollection(Of Notice) = New HashSet(Of Notice)
Public Overridable Property Employee As Employee
End Class
Partial Public Class Notice
Public Property id As Integer
Public Property Employee_id As Integer
Public Property Order_id As Integer
Public Property sysdate As Date
Public Property content As String
Public Overridable Property Employee As Employee
Public Overridable Property Order As Order
End Class
This entities classes use 'foreign key associations' and 'independent associations'.
Then, we have the following unit tests class:
<TestClass()>
Public Class UnitTests
Protected DbContext As DbModelContext
<TestInitialize()>
Public Sub TestInitialize()
Thread.CurrentThread.CurrentUICulture = New CultureInfo("en-us")
DbContext = New DbModelContext()
End Sub
<TestCleanup()>
Public Sub TestCleanup()
DbContext.Dispose()
End Sub
<TestMethod()>
Public Sub Test1()
Dim notice1 = CreateNewNotice()
notice1.Order = CreateNewOrderWithAllRequiredAndRelatedEntities()
DbContext.NoticeSet.Add(notice1)
DbContext.SaveChanges() ' no DbUpdateException exception
Assert.AreEqual(notice1.Employee.id, notice1.Order.Employee.id)
Assert.AreEqual(notice1.Employee.id, notice1.Order.Notices.First().Employee.id)
End Sub
<TestMethod()>
Public Sub Test2()
Dim notice1 = CreateNewNotice()
notice1.Order = CreateNewOrderWithAllRequiredAndRelatedEntities()
DbContext.NoticeSet.Add(notice1)
Dim employee2 = CreateNewEmployee()
DbContext.EmployeeSet.Add(employee2)
DbContext.SaveChanges() 'DbUpdateException exception
End Sub
''' <summary>
''' Create new Order object along with required associated objects,
''' however related Notice objects do not have assigned required associated objects
''' </summary>
Protected Function CreateNewOrderWithAllRequiredAndRelatedEntities(Optional ByVal suffix As Int32 = 1) As Order
Dim order1 = CreateNewOrder(suffix)
order1.Employee = CreateNewEmployee(suffix)
For i = suffix To suffix + 2
order1.Notices.Add(CreateNewNotice(i))
Next
Return order1
End Function
''' <summary>
''' Create new Order object without required associated objects
''' </summary>
Protected Function CreateNewOrder(Optional ByVal suffix As Int32 = 1) As Order
Dim order1 = New Order() With {
.article = "article" & suffix
}
Return order1
End Function
''' <summary>
''' Create new Employee object required without associated objects
''' </summary>
Protected Function CreateNewEmployee(Optional ByVal suffix As Int32 = 1) As Employee
Dim employee1 = New Employee() With {
.firstname = "firstname" & suffix,
.lastname = "lastname" & suffix
}
Return employee1
End Function
''' <summary>
''' Create new Notice object without associated objects
''' </summary>
Protected Function CreateNewNotice(Optional ByVal suffix As Int32 = 1) As Notice
Dim notice1 = New Notice() With {
.sysdate = DateTime.Now,
.content = "Description" & suffix
}
Return notice1
End Function
End Class
If we run tests, Test1() passes, but Test2() fails with exception:
System.Data.Entity.Infrastructure.DbUpdateException: Unable to determine the principal end of the 'Model1.NoticeEmployee' relationship. Multiple added entities may have the same primary key. ---> System.Data.UpdateException: Unable to determine the principal end of the 'Model1.NoticeEmployee' relationship. Multiple added entities may have the same primary key.
Conclusions:
Inside Test1() there is only one Employee object and one Order object in the DbContext. Relations which are not set in code (Notice.Employee, Notice.Order) are automatically set up by EF within DbContext.SaveChanges() statement. Inside Test2() there are two Employee object in DbContext, so Notice.Employee has not automatically assigned value.
What is strange, if we remove foreign key properties from model entities, in order to have only 'independent associations' functionality, both tests fails with the same System.Data.Entity.Infrastructure.DbUpdateException exception.
Similarly, if we remove navigation properties from model entities, in order to have only 'foreign key associations' functionality, both tests fails with the same exception.

Related

Code to for AutoMapper complex mapping

AutoMapper with VB.NET
I have the following classes below. OrderA With List (Of OrderALineItem) and OrderBList With List (Of OrderB). I want to copy data from OrderA to OrderBList. Which copies ItemName, ItemQty, Price from List (Of OrderALineItem) to List (Of OrderB) and OrderID, CustomerName from OrderA itself. I have found almost all codes in C# and am not able to convert it to vb.net code.
Public Class OrderA
Public Property OrderID As String
Public Property CustomerName As String
Public Property OrderLineItem As List(Of OrderALineItem)
End Class
Public Class OrderALineItem
Public Property ItemName As String
Public Property ItemQty As Integer
Public Property Price As Decimal
End Class
Public Class OrderBList
Public Property OrderBLineItem As List(Of OrderB)
End Class
Public Class OrderB
Public Property OrderID As String
Public Property CustomerName As String
Public Property ItemName As String
Public Property ItemQty As Integer
Public Property Price As Decimal
End Class
My VB.NET code until now is:
Dim mapperConfiguration = New MapperConfiguration(Sub(config)
config.CreateMap(Of OrderALineItem, OrderBList)()
End Sub)
Dim mapper = mapperConfiguration.CreateMapper()
Dim objOrderB = mapper.Map(Of OrderBList)(objOrder.OrderLineItem)
The above code creates and object from copies the data from objOrder.OrderLineItem to OrderBList. That's it.
Can anybody help me out on this in VB.NET.
Note: Am totally new in AutoMapper
Version: AutoMapper 6.2.2.0
Done myself, I hope the code below will be helpful to somebody.
Dim mapperConfiguration = New MapperConfiguration(Sub(config)
config.AddProfile(New CustomProfile_1)
End Sub)
Dim objMapper = mapperConfiguration.CreateMapper()
Dim objOrderB As List(Of Dest_OrderB) = objMapper.Map(Of Src_OrderA, List(Of Dest_OrderB))(objOrderA)
Public Class CustomProfile_1
Inherits Profile
Sub New()
CreateMap(Of Src_OrderALineItem, Dest_OrderB)()
CreateMap(Of Src_OrderA, List(Of Dest_OrderB))() _
.ConstructProjectionUsing(
Function(Src1) Src1.List_Src_OrderALineItem.Select(Function(Src2) New Dest_OrderB _
With {.CustomerName = Src1.CustomerName,
.OrderID = Src1.OrderID,
.ItemName = Src2.ItemName,
.ItemQty = Src2.ItemQty,
.Price = Src2.Price}
).ToList())
End Sub
End Class

Use TreeNode Class to work with hierarchically structured data without a TreeView

I am somehow stuck and can't see the forest for the trees.
What I want to do:
I have a large list of data (about 6000 nodes), at the moment pretty simple:
Unique ID
Parent
List of Children
List item
Currently, this is flat data but I want to create a hierarchy of it, so i can
search for UniqueID in a specific depth of the tree
for any ID, list row of parents up to the root
list all childrens of a single ID
loop through entries (vertical, horizontal)
order horizontal items
List items
What I tried:
I started with this code:
see link
<Serializable> _
Public Class TreeNode
Private _uniqueID As Integer
Private _name As String
Private _parentID As Integer
Private _depth As Integer
Private _children As ArrayList
Public Sub New()
End Sub
Public Sub New(name As String, parentID As Integer)
Me.New(0, name, parentID, -1)
End Sub
Public Sub New(uniqueID As Integer, name As String, parentID As Integer, depth As Integer)
_uniqueID = uniqueID
_name = name
_parentID = parentID
_depth = depth
End Sub
''' <summary>
''' Gets or sets the unique ID associated with this category
''' </summary>
''' <remarks>Once a non-zero ID has been set, it may not be modified.</remarks>
Public Property UniqueID() As Integer
Get
Return _uniqueID
End Get
Set
If _uniqueID = 0 Then
_uniqueID = value
Else
Throw New Exception("The UniqueID property cannot be modified once it has a non-zero value")
End If
End Set
End Property
Public ReadOnly Property Depth() As Integer
Get
Return _depth
End Get
End Property
''' <summary>
''' Gets or sets the label for this node
''' </summary>
Public Property Name() As String
Get
Return _name
End Get
Set
_name = value
End Set
End Property
''' <summary>
''' The ID of the parent node
''' </summary>
Public Property ParentID() As Integer
Get
Return _parentID
End Get
Set
_parentID = value
End Set
End Property
''' <summary>
''' Gets the children TreeNode objects for this category
''' </summary>
''' <remarks>In .NET 2.0, this can be modified to use generics, and have type ArrayList<TreeNode></remarks>
Public Property Children() As ArrayList
Get
Return _children
End Get
Set
_children = value
End Set
End Property
End Class
I created my tree:
Public Dendrogram As List(Of TreeNode)
.. and added all nodes to it. Super clean, understandable, but no functions!
This brought me to another approach But it is far too complex for my purposes.
.. then I was wondering: why not use the TreeNode Class from MS? But I don't want to use the TreeView associated with it. There is this example, but it is in C and I can't seem to apply it in VBNet (got stuck at implementation of the ITreeNode).
My question:
How can I use functionalities of the TreeView such as "treeView1.Nodes.Add(topNode)" or "treeView1.Nodes(0).Nodes.Find(searchterm, True)" without actually having it on my form (I just need it to structure my data, without visualizing it).
I hope this makes sense and anyone can point me in the right direction!
Although TreeNode is in the System.Windows.Forms namespace, there doesn't seem to be anything in it that is really tied to WinForms (it already seems to be inherited in a couple of other namespaces) so, assuming it gives you the functionality you need, can't you just use it? e.g.
Imports System.Windows.Forms
Sub Main
Dim root = New TreeNode("Root")
root.Nodes.Add("Node 1")
root.Nodes.Add("Node 2")
root.Nodes.Add("Node 3")
root.Nodes(0).Nodes.Add("Node 1.1")
root.Nodes(0).Nodes(0).Nodes.Add("Node 1.1.1")
root.Nodes(1).Nodes.Add("Node 2.1")
PrintNode(root, 0)
End Sub
' Define other methods and classes here
Sub PrintNode(node As TreeNode, level As Integer)
Console.WriteLine("{0}{1}", New String(" ", level * 2), node.Text)
For Each child In node.Nodes
PrintNode(child, level + 1)
Next
End Sub
Output:
Root
Node 1
Node 1.1
Node 1.1.1
Node 2
Node 2.1
Node 3

Get value of a property with propertyinfo object

Is there a way to get value of a object properties with a propertyinfo object?
psudo code:
propertyinfoObject = Text
myobject.toCommand(propertyinfoObject)
The psudo code above should do the same as
myobject.Text
My goal is to create a simpel Properties form that will work on any object (Later I will use keywords to filter out what options I want the use to see).
My real code
Public Class PropertiesForm
Dim propertyInfoVar() As PropertyInfo
Dim Properties As New Form2
Dim listItem As New ListViewItem
Dim stringarray() As String
Public Sub New(ByRef sender As Object)
propertyInfoVar = sender.GetType().GetProperties()
For Each p In propertyInfoVar
stringarray = {p.Name.ToString, #INSERT VALUE SOMEHOW HERE#}
listItem = New ListViewItem(stringarray)
Properties.ListView1.Items.Add(listItem)
Next
Properties.Visible = True
End Sub
EDIT
Just use propertyGrid as suggested below!
The standard PropertyGrid already does all that for you. Filtering properties is not so obvious, here's how:
The control includes a BrowsableAttributes property which allows you to specify that only properties with the specified attribute value should be shown. You can use existing attributes, or custom ones. This is specifically for tagging visible props:
<AttributeUsage(AttributeTargets.Property)>
Public Class PropertyGridBrowsableAttribute
Inherits Attribute
Public Property Browsable As Boolean
Public Sub New(b As Boolean)
Browsable = b
End Sub
End Class
Apply it to an Employee class to hide pay rates or anything else:
Public Class Employee
<PropertyGridBrowsable(True)>
Public Property FirstName As String
...
<PropertyGridBrowsable(False)>
Public Property PayRate As Decimal
<PropertyGridBrowsable(False)>
Public Property NationalInsuranceNumber As String
Test code:
Dim emp As New Employee With {.Dept = EmpDept.Manager,
.FirstName = "Ziggy",
.PayRate = 568.98D,
...
.NationalInsuranceNumber = "1234567"
}
propGrid.BrowsableAttributes = New AttributeCollection(New PropertyGridBrowsableAttribute(True))
propGrid.SelectedObject = emp
BrowsableAttributes is a collection, so you can add several.

Entity Framework : Query with opposite condition in entity childs produce wrong results

I'm using Entity Framework.
I have this case :
Partial Public Class Myobj
Public Property id As Integer
Public property name as string
Public Overridable Property chld As ICollection(Of chld) = New HashSet(Of chld)
End Class
Partial Public Class Myobj
Public shared cond1 as DateTime
<NotMapped> Public ReadOnly Property vls As integer
Get
Return chld.AsQueryable.Where(Function(t2) t2.date1<cond1).Select(Function(t3) t3.quantity).DefaultIfEmpty.Sum()
End Get
End Property
End Class
Partial Public Class chld
Public Property id As Integer
Public Property date1 as DateTime
Public Property quantity as Integer
Public Property ParentID as integer
Public Overridable Property MyObj1 As MyObj
End Class
Now on my form, I have this code :
Dim dt1 as DateTime=CDate("08/08/2014")
Myobj.cond1=dt1
Dim list1 = (From t In context.MyObj Select New With { _
.Parent = t, _
.chl = (From t2 In t.chld.AsQueryable.Where(Function(t3) t3.Date1>=dt1) Select t2)
}).ToList
As you can see, on Not mapped property vls is calculated the sum of child's quantity before date "08/08/2014".
On the main query are selected the children with date after the date "08/08/2014"
This query always produces 0 in vls property for each item in Myobj1.( !! but it's not true according to data in database!!).
Why this query product a such result?
Thank you!
You didn't tell, but you have lazy loading disabled. This means that after the query has ended, the MyObj entities only have chld objects >= "08/08/2014" in their chld collections. Entity Framework populates these collections by relationship fixup.
So if you access MyObj.vls, there are no chld's < "08/08/2014". With lazy loading enabled (and the context still in scope) this would trigger lading the full chld collection and you'd get the required results.
However, it's more efficient to query the MyObj entities with chld included and do the processing afterwards in memory.

Assign direct value to object

I have several properties, for example
Public Property FIRSTNAME As New SQLString("FirstName", 50)
Public Property FULLNAME As New SQLString("Name", 50)
The SQLString object is defined as:
Public Class SQLString
Property SQL_Column As String
Property Limit As Integer
Property Value As String
Public Sub New(SQLcolumn As String, limit_ As Integer)
SQL_Column = SQLcolumn
Limit = limit_
End Sub
Public ReadOnly Property SQL_value() As String
Get
Return "'" & clean(Value, Limit) & "'"
End Get
End Property
End Class
Notice that through this method, each of my properties (e.g. FIRSTNAME) is able to have several sub properties, which is necessary.
To access them, it's simply for example FIRSTNAME.SQL_Column.
This works, however what I would like is to also be able to store a value (e.g. string data type) on the FIRSTNAME property itself, which would make accessing it like:
Dim MyFirstName As String = FIRSTNAME
Rather than:
Dim MyFirstName As String = FIRSTNAME.Value
Which is what I currently have to do.
The only way I can see to do this is to have the SQLString object be set to string (or another data type) by default, like:
Public Class SQLString As String
Obviously the above code does not work, but I'm wondering if there is an equivalent that does?
The default access modifier to a property (ie: Public, Private, etc) is the most restrictive when no access modifier is provided. In SQLString class, since there is not a Public access modifier in front of the properties in the class, they are essentially Private and not accessible from outside of the class.
Adding the access modifier to the properties should fix the issue you see:
Public Property SQL_Column As String
Public Property Limit As Integer
Public Property Value As String
Please tell me the problem for the vote downs - here is a working .NET fiddle of the proposed code changes above (https://dotnetfiddle.net/96o8qm).
Imports System
Dim p as Person = new Person()
p.FIRSTNAME = new SQLString("Test", 1)
p.FIRSTNAME.Value = "Test Value"
Console.WriteLine("Person Value: {0}", p.FIRSTNAME.Value)
Public Class Person
Public Property FIRSTNAME AS SQLString
End Class
Public Class SQLString
Public Property SQL_Column As String
Public Property Limit As Integer
Public Property Value As String
Public Sub New(SQLcolumn As String, limit_ As Integer)
SQL_Column = SQLcolumn
Limit = limit_
End Sub
Public ReadOnly Property SQL_value() As String
Get
Return ""
End Get
End Property
End Class
This yields the output:
Person Value: Test Value
The answer to your question is quite simple; add a CType widening operator.
Example:
Public Class SQLString
Public Shared Widening Operator CType(ByVal s As SQLString) As String
Return If((s Is Nothing), Nothing, s.Value)
End Operator
Public Property Value As String
End Class
Test:
Dim firstName As New SQLString() With {.Value = "Bjørn"}
Dim myName As String = firstName
Debug.WriteLine(myName)
Output (immediate window):
Bjørn