When I update the database, I have to hard code mapping of each property, because using attach results in exception. This does not seem too elegant. Is there some easier solution here I'm not aware of? My code is below, showing the "MapData" method I call for this purpose:
Btw, entity classes (here; Users) are autogenerated with SqlMetal.
Public Class UserDataService
Public Sub Save(ByVal user As Users)
Dim ctx As New TestDB(connection)
Dim id As Integer = user.Id
If user.Id = 0 Then
Insert(user)
Else
Dim q = (From n In ctx.Users Where n.Id = id Select n).Single
q.MapData(user)
For Each o In user.Orders
o.Save()
Next
' ctx.Users.Attach(user, q) ' Does not work
' ctx.Users.Attach(user, True) ' Does not work
End If
ctx.SubmitChanges()
ctx.Dispose()
End Sub
End Class
Partial Public Class Users
Public Sub MapData(ByVal row As Users)
Me.Name = row.Name
End Sub
End Class
EDIT1:
Exceptions:
ctx.Users.Attach(user, q)
Cannot add an entity with a key that is already in use.
ctx.Users.Attach(user, True)
An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
EDIT2:
I tried to add a column timestamp, which I believe is supposed to satisfy the last mentioned exception. So I add the column shown here. This doesn't help, but perhaps I need to make some further settings for that to be effective?
This way should work:
ctx.Users.Attach(user)
ctx.Refresh(RefreshMode.KeepCurrentValues, user) 'this line is important
ctx.SubmitChanges()
This is my console test app(it works), in C# though:
class Program
{
public static void Main(string[] args)
{
var t = new Test();
Customer c = t.GetCustomer();
c.CompanyName = "X";
t.AttachCustomer(c);
}
class Test
{
public Customer GetCustomer()
{
Customer cust;
using(DataContext db = new DataContext())
{
cust = db.Customers.Where(x => x.CustomerID == "ALFKI").Single();
db.Dispose();
}
return cust;
}
public void AttachCustomer(Customer cx)
{
using (DataContext db = new DataContext())
{
db.Customers.Attach(cx);
db.Refresh(RefreshMode.KeepCurrentValues, cx);
db.SubmitChanges();
db.Dispose();
}
}
}
}
Related
The JpaRepository allows for executing some SQL queries without having to specify these specifically. For example, it is possible to execute the following method: myRepository.existsById(id).
I would like to execute this method, but instead of checking for the existence of the id, I would like to check for a different property, which I call employeeId. I am aware that I will have to specify this query; this is what I have tried:
#Query("SELECT 1 FROM MyTable t WHERE t.employeeId = ?1")
Integer existsByEmployeeId(Long id);
I am calling this method just before executing a DELETE query:
public Long deleteEntryByEmployeeId(Long id) {
if (myRepository.existsByEmployeeId(id) == 1) {
myRepository.deleteEntryByEmployeeId(id);
return id;
}
return null;
}
This doesn't really work as expected though, as I am returned the error:
Cannot invoke "java.lang.Integer.intValue()" because the return value of "myRepository.existsByEmployeeId(java.lang.Long)" is null
I understand that myRepository.existsByEmployeeId(id) returns null. This seems to be the case if there is no entry in myTable for the specified id. Anyways, I am looking for a smooth solution to this problem. Can somebody help?
There are multiple approaches.
You want to stick with declarative #Query annotation.
#Query("SELECT CASE WHEN count(t) = 1 THEN TRUE ELSE FALSE END FROM MyTable t WHERE t.employeeId = ?1")
Integer existsByEmployeeId(Long id);
It's OK to implement custom functionality.
public interface CustomEmployeeRepository {
boolean existsByEmployeeId(Long id);
}
#Repository
#Transactional(readOnly = true)
class CustomEmployeeRepository implements CustomEmployeeRepository {
#PersistenceContext
private EntityManager em;
#Override
boolean existsByEmployeeId(Long id) {
List list = em.createQuery("SELECT 1 FROM MyTable t WHERE t.employeeId = :id")
.setParameter("id", id)
.getResultList();
return list.size() == 1;
}
}
// inject EmployeeRepository and call existsByEmployeeId as usual
public interface EmployeeRepository extends JpaRepository<Employee, Long>, CustomEmployeeRepository {
}
Can someone help me with my problem?
The picture below shows the same item I inputted. What I want is I don't like to show duplicate items in DataGridView. If the same product record adds, then the new will not be showed, it just add the quantity when clicking the "Save" button. And I don't know how to code it I'm just new to vb.net. Can somebody help me how to do it?? It would be a big help for me if you do, thank you so much!
Below is my code for Save button:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
initializeCon()
Dim found As Boolean = False
If (DataGridView1.Rows.Count > 0) Then
For Each row As DataGridViewRow In DataGridView1.Rows
If (Convert.ToString(row.Cells(1).Value) = dtDate.Text) And (Convert.ToString(row.Cells(2).Value) = txtProductCode.Text) AndAlso
(Convert.ToString(row.Cells(3).Value) = txtProductName.Text) Then
row.Cells(4).Value = Convert.ToString(txtQuantity.Text + Convert.ToInt16(row.Cells(4).Value))
found = True
End If
Next
If Not found Then
cmd = New SqlCommand("INSERT INTO tbl_productOrders VALUES('" & txtID.Text & "','" & dtDate.Text & "','" & txtProductCode.Text & "','" & txtProductName.Text & "'," & txtQuantity.Text & ");", con)
cmd.ExecuteNonQuery()
clrtxt()
SaveMsg()
Getdata()
End If
End If
End Sub
this is an axample to avoid duplicates while adding rows
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim exist As Boolean = False, numrow As Integer = 0, numtext As Integer
For Each itm As DataGridViewRow In DataGridView1.Rows
If itm.Cells(0).Value IsNot Nothing Then
If itm.Cells(0).Value.ToString = TextBox1.Text Then
exist = True
numrow = itm.Index
numtext = CInt(itm.Cells(1).Value)
Exit For
End If
End If
Next
If exist = False Then
DataGridView1.Rows.Add(New String() {TextBox1.Text, TextBox2.Text})
Else
DataGridView1.Rows(numrow).Cells(1).Value = CInt(TextBox2.Text) + numtext
End If
End Sub
There are two aspects to improve your program.
1. Make comparing products better
Since you need to check individual properties to find equal product, the better way is to overload == operator along with implementing IEquatable interface. You can read more here. When you do this, you can compare products with == operator: if (product1 == product2) { }. In this case three properties are being compared. If they all are the same, then two products are equal.
2. Make adding products to DataGridView easier
In order to make adding products to DataGridView easier, you need to leverage the handy binding mechanism in Windows Forms, which appeared in .NET Framework 2.0 - BindingSource. It acts like a medium between your data and controls. In order to make this class usable, you need to use another handy class - BindingList. You feed BindingList to BindingSource and assign BindingSource to DataSource property of DataGridView. After that you work only with BindingList collection (adding/removing/editing) - and all the propagation is done by BindinSource.
To summarize, here is the explanation and full code. Here you can download the project itself.
When you add product, the code first checks whether such product already exists in DataGridView (Add product button). Do note that we don't work with DataGridView directly - we work only with the collection. Thanks to enhancements, we can use LINQ here. If the product is not found, we add it to collection. If it's found, we just update the quantity.
If you need to update selected product data (Change quantity button), you only need to retrieve it from selected rows and use BoundDataItem - this is where our product is located. Then just update properties of product you need.
If you need to remove product (Delete product button), retrieve it from selected row and delete it from the collection.
Important! Do not forget to call Refresh() method on DataGridView after all taken actions in order to see the changes.
namespace WinFormsApp
{
public partial class Root : Form
{
private BindingList<Product> list = null;
private BindingSource bindingSource = null;
public Root() => InitializeComponent();
private void OnFormLoaded(object sender, EventArgs e)
{
// Our collection holding products
list = new BindingList<Product>();
// I've set up columns manually (with applied binding),
// so switch generating columns off.
dataGrid.AutoGenerateColumns = false;
// Disable adding new row
dataGrid.AllowUserToAddRows = false;
// Create our medium between grid and collection
bindingSource = new BindingSource { DataSource = list };
// Set binding
dataGrid.DataSource = bindingSource;
}
private void OnAddRecord(object sender, EventArgs e)
{
// Create new product
var new_product = new Product
{
Date = dtPicker.Value.ToShortDateString(),
Code = txtCode.Text,
Name = txtName.Text,
Quantity = npQuantity.Value
};
// No need to check individual properties here
// as == and IEquatable will do all the work.
// We can safely use LINQ here.
var p = list.FirstOrDefault(x => x == new_product);
if (p == null)
{
// Product is not found. Add it to list.
list.Add(new_product);
}
else
{
// Product is found. Update quantity.
p.Quantity += new_product.Quantity;
}
dataGrid.Refresh(); //Required to reflect changes
}
private void OnChangeQuantity(object sender, EventArgs e)
{
// Change quantity here.
var product = GetProduct();
if (product != null)
{
// Update product's quantity.
product.Quantity *= 2;
// Do not forget to refresh grid.
dataGrid.Refresh();
}
}
private void OnDeleteProduct(object sender, EventArgs e)
{
// Delete product here.
var product = GetProduct();
if (product != null)
{
// We need to delete product only from collection
list.Remove(product);
// Do not forget to refresh grid
dataGrid.Refresh();
}
}
// Retrieve product from selected row.
private Product GetProduct() =>
dataGrid.SelectedCells.Count == 0 ?
null :
dataGrid.SelectedCells[0].OwningRow.DataBoundItem as Product;
}
class Product : IEquatable<Product>
{
public string Date { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public decimal Quantity { get; set; }
// Overload == operator
public static bool operator ==(Product firstProduct, Product secondProduct)
{
// Check for null on left side.
if (Object.ReferenceEquals(firstProduct, null))
{
if (Object.ReferenceEquals(secondProduct, null))
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return firstProduct.Equals(secondProduct);
}
// Overload != operator (required when overloading == operator)
public static bool operator !=(Product firstProduct, Product secondProduct) =>
!(firstProduct == secondProduct);
// Implementing IEquatable<T> interface
public bool Equals(Product other)
{
// If 'other' is null, return false.
if (Object.ReferenceEquals(other, null))
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, other))
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != other.GetType())
{
return false;
}
// Return true if the fields match.
return
Date == other.Date &&
Code == other.Code &&
Name == other.Name;
}
public override bool Equals(object obj) => this.Equals(obj as Product);
public override int GetHashCode() =>
Date.GetHashCode() + Code.GetHashCode() + Name.GetHashCode();
// Optional. For debugging purposes.
public override string ToString() =>
$"Date: {Date}, Code: {Code}, Name: {Name}, Quantity: {Quantity}";
}
}
I try to use foreign collections in ORMLite. However, I dont know how to convert it into list. I try to do something like this :
public class Car implements Serializable {
#DatabaseField(columnName = "carId" , generatedId = true, id=true)
private int id;
#DatabaseField(columnName = "carNumber")
private String mNumber;
#DatabaseField(columnName = "carName")
private String mName;
#ForeignCollectionField(eager = true,columnName = "carParts")
private Collection<Part> mParts;
ArrayList<Part> parts = new ArrayList<>(mParts);
public ArrayList<Part> getParts() {
return parts;
}
public void setParts(ArrayList<Part> parts) {
this.parts = parts;
}
but when I try to use it I get exception :
java.lang.NullPointerException: collection == null
at this line :
ArrayList<Part> parts = new ArrayList<>(mParts);
please, help.
The reason is simple - you have to wait until mParts will be initialized by ORMLite library, then you can create ArrayList from it.
public ArrayList<Part> getParts() {
return new ArrayList<>( mParts );
}
I have a drop down list written in Razor for a MVC app I am working on as:
#Html.DropDownList("BillId", "")
However the user does not have to select anything according to the logic of my program (the list is populated with 'Bill' objects in my controller). If they do not select any thing I get an error
The ViewData item that has the key 'BillId' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
How do I write a statement in Razor to return a BillId of 0 if nothing is selected?
I am not sure of the syntax as I have a background in straight java and VB but something alongs the line of
If DropdownBox.SelectedIndex = 0
Else
BillId = DropdownBox.SelectedIndex
End
Controller as follows:
Function Create(id As Integer) As ViewResult
ViewBag.id = id
Dim job As Job = New Job
job.CustomerId = id
job.JobAmount = 0
job.JobDate = Date.Now()
job.JobStatus = "Active"
Dim BillList = New List(Of Bill)()
Dim BillQuery = From s In db.Bills
Select s
BillList.AddRange(BillQuery)
ViewBag.BillIdList = New SelectList(BillList, "BillId", "BillDate")
ViewBag.BillId = New SelectList(BillList, "BillId", "BillDate")
Return View(job)
End Function
The POST function for create is as below:
<HttpPost()>
Function Create(job As Job) As ActionResult
If ModelState.IsValid Then
db.Jobs.Add(job)
db.SaveChanges()
Dim customer As Customer = db.Customers.Find(job.CustomerId)
Dim customerNumber As String = customer.CustCellphone.ToString()
Dim messageSender As SendMessage = New SendMessage
Dim smsMessage As String = "LAUNDRY: Job Number " & job.JobId & " has been booked in. You will be notified when individual services within it are ready for collection."
messageSender.SendMessage(smsMessage, customerNumber)
Dim url As String = "/RequestedService/AddService/" + job.JobId.ToString()
Return Redirect(url)
End If
Return View(job)
End Function
EDIT
I was wondering too how this is passed back as in the POST I may be able to check for a 'null'? However I feel the problem may be the moment the submit button is pressed
In your POST controller action you forgot to populate the ViewCrap (oops, I meant ViewBag) before returning the view:
<HttpPost()>
Function Create(job As Job) As ActionResult
If ModelState.IsValid Then
...
End If
' Here you must populate the ViewCrap before returning the view the same
' way you did in your GET action because your view depend on it
Dim BillQuery = From s In db.Bills
Select s
ViewBag.BillId = New SelectList(BillQuery.ToList(), "BillId", "BillDate")
Return View(job)
End Function
But I would hyper strongly recommend you to use view models and forget about the existence of the ...... (the word that I don't want to pronounce).
UPDATE:
Now let's look at the correct way to implement this (which is by using view models). A view model is a class that you should define for each of your views and which will represent its specific requirements. So from what you have said in the comments section to my answer you want to have a dropdown list in your view where the user has to select a bill from a dropdown and which is required.
So let's roll the view model:
public class JobViewModel
{
[Required(ErrorMessage = "Please select a bill")]
[Display(Name = "Bill")]
public int? SelectedBillId { get; set; }
public IEnumerable<SelectListItem> Bills
{
get
{
return db.Bills.ToList().Select(x => new SelectListItem
{
Value = x.BillId.ToString(),
Text = x.BillDate.ToString()
});
}
}
public int CustomerId { get; set; }
... here you could put any other properties that you want
to display on the view, things like JobId, ...
}
then we define our controller with the 2 actions:
public ActionResult Create(int id)
{
var model = new JobViewModel
{
CustomerId = id
};
return View(model);
}
[HttpPost]
public ActionResult Create(JobViewModel model)
{
if (ModelState.IsValid)
{
// Using AutoMapper here to map between the domain model
// and the view model (http://automapper.org/)
var job = Mapper.Map<JobViewModel, Job>(model);
// Now call your service layer to do the necessary processings
// on this job domain model including saving the job and sending
// messages and stuff. This avoids polluting your controller with
// business logic code which belongs to your service layer
ServiceLayer.ProcessJob(job);
return RedirectToAction("AddService", "RequestedService", new { id = job.JobId });
}
return View(model);
}
and finally you will have a corresponding view which will be strongly typed to the view model:
#model JobViewModel
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(x => x.SelectedBillId)
#Html.DropDownListFor(x => x.SelectedBillId, Model.Bills, "-- select --")
#Html.ValidationMessageFor(x => x.SelectedBillId)
</div>
... some other input fields
<p><button type="submit">OK</button></p>
}
And now, as promised in the comments section let me show what I dubbed the absolute pornographic approach to solve this and which if you implemented in your application I will have to ask you to no longer come back and ask any ASP.NET MVC related question on StackOverflow :-)
The pornographic approach consisted into manually inserting an item with id = 0 and text = empty string into the beginning of the list and then inside the controller verifying if the selected id equals 0 in order to check whether the model is valid or not:
So in your GET action:
Function Create(id As Integer) As ViewResult
ViewBag.id = id
Dim job As Job = New Job
job.CustomerId = id
job.JobAmount = 0
job.JobDate = Date.Now()
job.JobStatus = "Active"
Dim Bills = db.Bills.ToList().Select(Function(s) New SelectListItem With { .Value = s.BillId.ToString(), .Text = s.BillDate.ToString() })
Bills.Insert(0, New SelectListItem With { .Value = "0", .Text = "" })
ViewBag.BillId = Bills
Return View(job)
End Function
<HttpPost()>
Function Create(job As Job, BillId as Integer) As ActionResult
If BillId > 0 Then
db.Jobs.Add(job)
db.SaveChanges()
Dim customer As Customer = db.Customers.Find(job.CustomerId)
Dim customerNumber As String = customer.CustCellphone.ToString()
Dim messageSender As SendMessage = New SendMessage
Dim smsMessage As String = "LAUNDRY: Job Number " & job.JobId & " has been booked in. You will be notified when individual services within it are ready for collection."
messageSender.SendMessage(smsMessage, customerNumber)
Dim url As String = "/RequestedService/AddService/" + job.JobId.ToString()
Return Redirect(url)
End If
ModelState.AddModelError("BillId", "Please select a bill")
Return View(job)
End Function
and inside the view:
#Html.DropDownList("BillId")
I have a class on which I'd like to define a unique key using Fluent NHibernate. I think I've done this correctly, but my unit tes,t in which I save two objects with the same value for the field in question, saves both items without error. Here's what I've got:
Mapping override:
public class ItemOverride : IAutoMappingOverride<Item>
{
#region IAutoMappingOverride<Item> Members
public void Override(AutoMapping<Item> mapping)
{
mapping.Map(t => t.Prompt).UniqueKey("UIX_Item_Prompt").Not.Nullable();
}
#endregion
}
Unit Test
[Test]
public void CannotSaveDuplicateItemPrompt()
{
ServiceLocatorInitializer.Init();
IItemManagementService itemManagementService = new ItemManagementService(repository);
Item first = ItemInstanceFactory.CreateValidTransientItem();
first.Prompt = "Duplicate";
itemManagementService.SaveOrUpdate(first);
Item second = ItemInstanceFactory.CreateValidTransientItem();
second.Prompt = "Duplicate";
ActionConfirmation confirm = itemManagementService.SaveOrUpdate(second);
Assert.IsFalse(confirm.WasSuccessful);
Assert.AreEqual(confirm.Message, "");
}
Okay, it works if I wrap the two saves in a transaction. Here's the new Unit Test:
[Test]
[ExpectedException(typeof(GenericADOException))]
public void CannotSaveDuplicateItemPrompt()
{
ServiceLocatorInitializer.Init();
IItemManagementService itemManagementService = new ItemManagementService(repository);
NHibernateSession.Current.BeginTransaction();
Item first = ItemInstanceFactory.CreateValidTransientItem();
first.Prompt = "Duplicate";
itemManagementService.SaveOrUpdate(first);
Item second = ItemInstanceFactory.CreateValidTransientItem();
second.Prompt = "Duplicate";
ActionConfirmation confirm = itemManagementService.SaveOrUpdate(second);
NHibernateSession.Current.Transaction.Commit();
}