i have this classes :
Public Class Customer
Public Property CustomerID As Integer
Public Property CustomerName As String
Public Property OrdersList As ICollection(Of Order)
End Class
Public Class Order
Public Property OrderID As Integer
Public Property CustomerID As Integer
Public Property OrderTotal As Decimal
Public Property OrderDetails As ICollection(Of OrderDetail)
Public Property OrderCustomer As Customer
End Class
Public Class OrderDetail
Public Property OrderDetailID As Integer
Public Property OrderID As Integer
Public Property ProductID As Integer
Public Property Price As Decimal
Public Property Quantity As Decimal
Public Property LineTotal As Decimal
Public Property OrderDetailOrder As Order
Public Property OrderDetailProduct As Product
End Class
Public Class Product
Public Property ProductID As Integer
Public Property ProductName As String
Public Property ProductPrice As Decimal
Public Property OrderDetails As ICollection(Of OrderDetail)
End Class
and this is the PUT Method
Function PutOrder(ByVal id As Integer, ByVal order As Order) As HttpResponseMessage
If ModelState.IsValid And id = order.OrderID Then
db.Entry(order).State = EntityState.Modified
Try
db.SaveChanges()
Catch ex As DbUpdateConcurrencyException
Return Request.CreateResponse(HttpStatusCode.NotFound)
End Try
Return Request.CreateResponse(HttpStatusCode.OK)
Else
Return Request.CreateResponse(HttpStatusCode.BadRequest)
End If
End Function
but when execute :db.Entry(order).State = EntityState.Modified
i have this error
A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
thank u all
It means that
either order.CustomerID != order.OrderCustomer.CustomerID
or OrderDetail.OrderID != OrderDetail.OrderDetailOrder.OrderID for at least one of the OrderDetails
or OrderDetail.ProductID != OrderDetail.OrderDetailProduct.ProductID for at least one of the OrderDetails
You must ensure that those values are equal before you attach the order to the context.
i think this is the solution
Dim x = db.Orders.Include(Function(a) a.OrderDetails).Include(Function(o) o.OrderCustomer).FirstOrDefault
db.Entry(x).CurrentValues.SetValues(order)
thank u #Slauma ,your tips is helpfull
Related
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
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.
I'm a web programmer transitioning to vb.net programming and I am in need of proper guidance. What I am trying to do is perform an SQL table join as shown in the reference here. It's my understanding that it's a good practice to for OOP to create a Class Object for each "type" of table in SQL.
For example, In the link in Reference I would have the following classes in .net.
Public Class Orders
Private _OrderID As GUID
Private _CustomerID As GUID
Private _OrderDate As Date
Public Property OrderID As Guid
Get
Return _OrderID
End Get
Set(ByVal value As Guid)
_OrderID = value
End Set
End Property
Public Property CustomerID As Guid
Get
Return _CustomerID
End Get
Set(ByVal value As Guid)
_CustomerID = value
End Set
End Property
Public Property OrderDate As Date
Get
Return _OrderDate
End Get
Set(ByVal value As Date)
_OrderDate = value
End Set
End Property
Public Sub New(ByVal obj As Object)
End Sub
End Class
and
Public Class Customers
Private _CustomerID As GUID
Private _CustomerName As String
Private _ContactName As String
Private _Country As String
Public Property CustomerID As Guid
Get
Return _CustomerID
End Get
Set(ByVal value As Guid)
_CustomerID = value
End Set
End Property
Public Property CustomerName As String
Get
Return _CustomerName
End Get
Set(ByVal value As String)
_CustomerName = value
End Set
End Property
Public Property ContactName As String
Get
Return _ContactName
End Get
Set(ByVal value As String)
_ContactName = value
End Set
End Property
Public Property Country As String
Get
Return _Country
End Get
Set(ByVal value As String)
_Country = value
End Set
End Property
Public Sub New(ByVal obj As Object)
End Sub
End Class
So given the two classes in .NET, how can I query my SQL with a table join and still maintain a proper OOP approach? Should I create a 3rd class which combines both tables?
Like This...
Public Class CustomerOrdersCombined
Private _OrderID As GUID
Private _CustomerID As GUID
Private _OrderDate As Date
Private _CustomerName As String
Private _ContactName As String
Private _Country As String
Public Property OrderID As Guid
Get
Return _OrderID
End Get
Set(ByVal value As Guid)
_OrderID = value
End Set
End Property
Public Property CustomerID As Guid
Get
Return _CustomerID
End Get
Set(ByVal value As Guid)
_CustomerID = value
End Set
End Property
Public Property OrderDate As Date
Get
Return _OrderDate
End Get
Set(ByVal value As Date)
_OrderDate = value
End Set
End Property
Public Property CustomerName As String
Get
Return _CustomerName
End Get
Set(ByVal value As String)
_CustomerName = value
End Set
End Property
Public Property ContactName As String
Get
Return _ContactName
End Get
Set(ByVal value As String)
_ContactName = value
End Set
End Property
Public Property Country As String
Get
Return _Country
End Get
Set(ByVal value As String)
_Country = value
End Set
End Property
Public Sub New(ByVal obj As Object)
End Sub
End Class
What is the best way to do this as an object? Let me know if this post is kind of confusing. I'm not sure if I made my question very clear. Thanks in advance.
You're falling into the OO purity trap.
Don't go for that trap where you query for a collection, then iterate over the collection to do the JOIN. That's the n+1 latency trap. You'll die a slow, non-performance death doing things like that. Objects aren't buying you anything.
Databases have been optimized to do JOINs very well; let them. Do the JOIN in SQL and sort out the mapping into objects that way.
An Order object might have a collection of LineItem objects underneath. Bring them all back in one query and map them once you've got the data.
IMO you should not "always" think that everything you do, you must create a new object especially in this kind of situation regarding tables in .NET. I suggest learning ADO.NET first and you will see there DataTables class that you can use. Also, please study LINQ as you will be captivated by this when working with sql queries.
First of all, yes this question is a bit confusing, mainly because it seems you're showing objects and asking about an INNER JOIN SQL statement into an object.
That being said I thing it seems that you have 2 tables in your Database Customers and Orders. I am assuming that a customer can have many orders, but an order can have only one customer making Orders and Customers have a 1-N relationship (one-to-many).
Now that I think I understand your question I will go into a bit of explaining why you shouldn't have a third object and the proper way to solve the problem. The main reason for normalizing your database and having 2 separate tables is that you don't want your Orders table also containing all the information for your customers. Now I'm assuming that the SQL query for your INNER JOIN would look something like this
SELECT
*
FROM
Customers INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
WHERE
Customers.CustomerID = #CustomerID
This would mean your are pulling all the data back for the same customer for each order. This means that if a customer has 100 orders you would in essence be creating 100 instances of your CustomerOrdersCombined Class and this would be in addition too your instance of customers class and 100 instances of your Orders Class. Hopefully you can see why this is a bad idea.
Now for how I would solve this problem.
Way one Make a method:
One way to solve the issue would be to create a method in your Customers class that would return all the Orders that are related to the customer
Public Function GetCustomerOrders() As List(Of Orders)
Return (New Orders).GetOrdersByCustomerID(Me.CustomerID)
End Function
The function "GetOrdersByCustomerID" would be a constructor for the Orders Class that returns a list of Orders by a CustomerID The SQL for that would be something like
SELECT * FROM Orders WHERE CustomerID = #CustomerID
Way 2 of solving your problem:
Orders is a property of your Customer Class, this may sound confusing but think of this assume that before your the instance of Customer class dies you want to look at the customers orders several times. In the above solution every time you want to look a the orders you have to hit the data base, this is in efficient and can be handled better. By making the customers orders a property of the Customer class, that way lives in memory until the customer object dies so you only have to hit the database once. Now you might be thinking what if I want to get information about a customer and not their 100 orders, why should I look that too. Well you shouldn't! There is a method called "Lazy Loading" This mean that you only load the order if/when they're requested. So This is how I would design the Customer Class
Public Class Customers
Private m_CustomerID As GUID
Private m_CustomerName As String
Private m_ContactName As String
Private m_Country As String
Private m_Orders As List(Of Orders)
Public Property CustomerID As Guid
Get
Return _CustomerID
End Get
Set(ByVal value As Guid)
_CustomerID = value
End Set
End Property
Public Property CustomerName As String
Get
Return _CustomerName
End Get
Set(ByVal value As String)
_CustomerName = value
End Set
End Property
Public Property ContactName As String
Get
Return _ContactName
End Get
Set(ByVal value As String)
_ContactName = value
End Set
End Property
Public Property Country As String
Get
Return _Country
End Get
Set(ByVal value As String)
_Country = value
End Set
End Property
Public Property Orders AS List(Of Orders)
Get
If m_Orders Is Nothing Then
'Only get the orders if requested'
m_Orders = (New Orders).GetOrdersByCustomerID(Me.CustomerID)
End If
Return m_Orders
End Get
Set(ByVal value As List(Of Orders))
m_Orders = value
End Set
End Property
Public Sub New(ByVal obj As Object)
End Sub
End Class
Notice in the getter is where the value is set, so otherwise orders with be a blank value. I hope this long answer is helpful to you.
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.
I am really struggling with getting two tables I have joined to show in a view (I just want to show the customer name which comes from the customer tables and the bill status from the bill table - both of which are joint on a CustomerId primary key). I spent ages researching this and came to the conclusion of creating another model just for this view which I did however in my controller when I try and use the following:
Function ShowAll() As ViewResult
Dim BillQuery = From d In db.Bills
Join c In db.Customers On d.CustomerId Equals c.CustomerId
Select c.CustSurname, d.BillStatus
Dim BillList As List(Of BillView) = BillQuery.ToList()
Return View(BillList)
End Function
I get an error:
Value of type 'System.Collections.Generic.List(Of )' cannot be converted to 'System.Collections.Generic.List(Of Laundry.BillView)'
Here are my Bill and BillView models:
Imports System.Data.Entity
Imports System.ComponentModel.DataAnnotations
Public Class Bill
'Key
Public Property BillId() As Integer
'Others
<Required()>
<Display(Name:="Customer ID")>
Public Property CustomerId() As String
<Required()>
<Display(Name:="Bill Status")>
Public Property BillStatus() As String
End Class
Public Class BillView
Public Property CustSurname() As String
Public Property BillStatus() As String
End Class
And Customer Model:
Imports System.Data.Entity
Imports System.ComponentModel.DataAnnotations
Public Class Customer
'Key
Public Property CustomerId() As Integer
'Others
<Display(Name:="Group ID")>
Public Property GroupId() As Integer
<Required()>
<Display(Name:="Firstname")>
Public Property CustFirstname() As String
<Required()>
<Display(Name:="Surname")>
Public Property CustSurname() As String
<Display(Name:="Email")>
Public Property CustEmail() As String
<Display(Name:="Cellphone")>
Public Property CustCellphone() As String
<Display(Name:="Address")>
Public Property CustAddress() As String
<Required()>
<Display(Name:="Status")>
Public Property CustStatus() As String
<Required()>
<Display(Name:="Account Balance")>
Public Property AccBalance() As Double
<Required()>
<Display(Name:="Loyalty Ammount")>
Public Property LoyaltyAmount() As Double
<Required()>
<Display(Name:="Customer Discount")>
Public Property PersonalDiscount() As Double
End Class
It looks like your first LINQ query is returning an anonymous type which can't then be cast to List(Of BillView). Try the following code to return a collection of BillView models prior to the cast (note the added syntax Select New BillView With {}).
Function ShowAll() As ViewResult
Dim BillQuery = From d In db.Bills
Join c In db.Customers On d.CustomerId Equals c.CustomerId
Select New BillView With {.CustSurname = c.CustSurname, .BillStatus = d.BillStatus}
Dim BillList As List(Of BillView) = BillQuery.ToList()
Return View(BillList)
End Function