I am developing an App in Xamarin type PCL, for my DB structure, I want to use SQLite, but I have the following doubts ...
When entering a record in my DB, it takes the ID = 0, in my data model use
[PrimaryKey, AutoIncrement]
Public int ViolationID {get; set; }
And still, I enter the registry at zero, I do not know I'm doing wrong ... or is this a bug in the SQLite.NET-PCL package?
How can I verify that these records are actually being entered? I have in my code
public class DataAccess : IDisposable
{
private SQLiteConnection connection;
public DataAccess()
{
var entity = DependencyService.Get<IEntity>();
connection = new SQLiteConnection(entity.Plataforma, System.IO.Path.Combine(entity.DirectorioBD, "Infraccions.db3"));
connection.CreateTable<Infraccion>();
}
public void InsertInfraccion(Infraccion infraccion)
{
connection.Insert(infraccion);
}
public void UpdateInfraccion(Infraccion infraccion)
{
connection.Update(infraccion);
}
public Infraccion GetInfraccion(int InfraccionID)
{
return connection.Table<Infraccion>().FirstOrDefault(c => c.InfraccionID == InfraccionID);
}
public List<Infraccion> GetInfraccions()
{
return connection.Table<Infraccion>().OrderBy(c => c.MotivoID).ToList();
}
public void DeleteInfraccion(Infraccion infraccion)
{
connection.Delete(infraccion);
}
public void Dispose()
{
connection.Dispose();
}
}
Should I create a table called Infraccions.db3 on my phone?
Thank you for your comments...
Are you saying that all new records have an ID of 0 and overwrite the existing record in the database?
If so, then this is how SQLite works - for int columns 0 is a valid value, so when the ID is 0 it will overwrite the record, instead of incrementing the value.
The correct way to use int primary keys is to define your primary key as a nullable int, that way the value for a new record is null, which will be updated to the next available id:
[PrimaryKey, AutoIncrement]
public int? ViolationID {get; set; }
Essentially change int to int?.
1) When entering a record in my DB, it takes the ID = 0, in my data
model use
You are using primary key and auto increment in your model that's means you are not able to enter 0 manually in the primary field and it takes automatically next value in this field.
2) How can I verify that these records are actually being entered?
You can get DB file path when you create your database and you can able to open and see that data.
(System.IO.Path.Combine(entity.DirectorioBD, "Infraccions.db3") )
There are many browser plugins to access the sqllite database.
Related
Below is my code for entity and a function where I need to map entity TblEmployee from a key value pair.
In foreach loop I am getting values based on keys, what should be the best approach to do it?
public class TblEmployee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
}
public int Create()
{
tblEmployee employee = new tblEmployee();
using (var ctx = new theparkeee_testEntities())
{
foreach (string key in HttpContext.Current.Request.Form.AllKeys)
{
string value = HttpContext.Current.Request.Form[key];
//how to map value from key value pair to entity employee.
}
}
}
You can use System.Reflection to get the Properties of an object by their name with Type.GetProperty(string name). After you got the PropertyInfo, you can use SetValue to assign a value to it.
foreach (string key in HttpContext.Current.Request.Form.AllKeys) {
// note that "value" is a reserved word, do not use it as variable name
string val = HttpContext.Current.Request.Form[key];
var propertyInfo = typeof(TblEmployee).GetProperty(key); // can maybe be moved outside of the loop
if (propertyInfo != null) {
propertyInfo.SetValue(employee, val);
}
}
This will work for string properties. If the property is of another type, you have to find the correct type (again, using reflection) and cast the string value before assigning it.
Note that this is not the correct approach to store data in MVC. You should not work with the Request.Form directly, instead your POST action should accept a ViewModel that can be mapped (e.g. using Automapper) to the DB entity. I.e. let the ASP ModelBinder do its work, instead of reinventing the wheel!
[HttpPost]
public ActionResult Submit(MyViewModel postData) {
var employee = Mapper.Map<TblEmployee>(postData);
_ctx.Employees.Add(employee);
_ctx.SaveChanges();
return new HttpStatusCodeResult((int)HttpStatusCode.OK);
}
I'd like to know how can I make Entity Framework update an object instead of always inserting a new one for each new main object.
For example:
I have these objects:
Main Object:
public class ExtraArticleAttributes
{
[Key]
public int extraarticleattributes_id { get; set; }
virtual public WorldData world_data { get; set; }
}
Its dependencie:
public class WorldData
{
[Key]
public int worlddata_id { get; set; }
public string country { get; set; }
So, how can I make Entity Framework when inserting a new ExtraArticleAttributes verify if already exists a WorldData object and only update it?
I've been reading some articles about it and I notice that Entity Framework identify an existing object in DB with a HASH code, so when I get it from an API, and try to insert It in the DB, even though the object has the same data, the Entity Framework doesn't recognize like an existed object in DB. Does exist a way of make It, without spending request to the DB to verify if the object exists, if true get It.
Set the entity state to Modified:
using System.Data.Entity;
// Assuming that there is already an existing WorldData record in the database with id 1 and country 'foo', and you want to change the country to 'bar'
using (var context = new MyContext())
{
var extraArticleAttributes = new ExtraArticleAttributes
{
world_data = new WorldData
{
worlddata_id = 1,
country = "bar"
}
};
db.ExtraArticleAttributes.Add(extraArticleAttributes);
db.Entry<WorldData>(extraArticleAttributes.world_data).State = EntityState.Modified;
db.SaveChanges();
// world data 1 country is now 'bar'
}
I'm trying to delete an entity from my datastore using objectify but doesn't seem to be deleted even after shutting down the instance and restarting it. This is what the entity looks like in the datastore (both when it's on the production server & dev server):
This is the code i'm using to try and delete it:
#ApiMethod(name = "deleteDataVersion")
public Result deleteDataVersion(#Named("id") String id) {
// Where id is the id of the entity in the datastore.
if (id != null && !id.equals("")) {
ofy().delete().type(DataVersion.class).id(id).now();
return new Result(Result.STATUS_SUCCESS);
} else
return new Result(Result.STATUS_FAILED);
}
I've also tried this code:
#ApiMethod(name = "deleteDataVersion")
public Result deleteDataVersion(#Named("id") String id) {
if (id != null && !id.equals("")) {
// DataVersion doesn't have a parent.
Key<DataVersion> key = Key.create(null, DataVersion.class, id);
ofy().delete().key(key).now();
return new Result(Result.STATUS_SUCCESS);
} else
return new Result(Result.STATUS_FAILED);
}
But the entity never gets deleted. This is the code for my entity:
#Entity
public class DataVersion {
#Id
private Long id;
String folderName;
#Index
String effective;
public DataVersion() {
}
public DataVersion(String folderName, String effective ) {
this.folderName= folderName;
this.effective = effective;
}
// Getters & setters..
}
I just can't seem to find the problem :( Any help would be greatly appreciated! I'm sure it's something minor I'm overlooking (fairly new to Objectify/AppEngine).
The ID you have in parameter in your Endpoint is a String, and you try to delete the object DataVersion where the ID is a Long.
ofy().delete().type(DataVersion.class).id(Long.valueOf(id)).now();
would work better !
First get the key.
Key<DataVersion> key = Key.create(null, DataVersion.class, id);
Then fetch the entity from the database using the key.
DataVersion dataVersion = ofy().load().key(key).now();
Then delete the entity using objectify.
ofy().delete().entity(dataVersion).now();
My application is using SQLServer and JPA2 in the backend. App makes use of a timestamp column (in the SQLServer sense, which is equivalent to row version see here) per entity to keep track of freshly modified entities. NB SQLServer stores this column as binary(8).
Each entity has a respective timestamp property, mapped as #Lob, which is the way to go for binary columns:
#Lob
#Column(columnDefinition="timestamp", insertable=false, updatable=false)
public byte[] getTimestamp() {
...
The server sends incremental updates to mobile clients along with the latest database timestamp. The mobile client will then pass the old timestamp back to the server on the next refresh request so that the server knows to return only fresh data. Here's what a typical query (in JPQL) looks like:
select v from Visit v where v.timestamp > :oldTimestamp
Please note that I'm using a byte array as a query parameter and it works fine when implemented in JPQL this way.
My problems begin when trying to do the same using the Criteria API:
private void getFreshVisits(byte[] oldVersion) {
EntityManager em = getEntityManager();
CriteriaQuery<Visit> cq = cb.createQuery(Visit.class);
Root<Visit> root = cq.from(Visit.class);
Predicate tsPred = cb.gt(root.get("timestamp").as(byte[].class), oldVersion); // compiler error
cq.where(tsPred);
...
}
The above will result in compiler error as it requires that the gt method used strictly with Number. One could instead use the greaterThan method which simply requires the params to be Comparable and that would result in yet another compiler error.
So to sum it up, my question is: how can I use the criteria api to add a greaterThan predicate for a byte[] property? Any help will be greatly appreciated.
PS. As to why I'm not using a regular DateTime last_modified column: because of concurrency and the way synchronization is implemented, this approach could result in lost updates. Microsoft's Sync Framework documentation recommends the former approach as well.
I know this was asked a couple of years back but just in case anyone else stumbles upon this.. In order to use a SQLServer rowver column within JPA you need to do a couple of things..
Create a type that will wrap the rowver/timestamp:
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.xml.bind.annotation.XmlTransient;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Arrays;
/**
* A RowVersion object
*/
public class RowVersion implements Serializable, Comparable<RowVersion> {
#XmlTransient
#JsonIgnore
private byte[] rowver;
public RowVersion() {
}
public RowVersion(byte[] internal) {
this.rowver = internal;
}
#XmlTransient
#JsonIgnore
public byte[] getRowver() {
return rowver;
}
public void setRowver(byte[] rowver) {
this.rowver = rowver;
}
#Override
public int compareTo(RowVersion o) {
return new BigInteger(1, rowver).compareTo(new BigInteger(1, o.getRowver()));
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RowVersion that = (RowVersion) o;
return Arrays.equals(rowver, that.rowver);
}
#Override
public int hashCode() {
return Arrays.hashCode(rowver);
}
}
The key here is that it implement Comparable if you want to use it in calculations (which you definitely do)..
Next create a AttributeConverter that will move from a byte[] to the class you just made:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* JPA converter for the RowVersion type
*/
#Converter
public class RowVersionTypeConverter implements AttributeConverter<RowVersion, byte[]> {
#Override
public byte[] convertToDatabaseColumn(RowVersion attribute) {
return attribute != null ? attribute.getRowver() : null;
}
#Override
public RowVersion convertToEntityAttribute(byte[] dbData) {
return new RowVersion(dbData);
}
}
Now let's apply this RowVersion attribute/type to a real world scenario. Let's say you wanted to find all Programs that have changed on or before some point in time.
One straightforward way to solve this would be to use a DateTime field in the object and timestamp column within db. Then you would use 'where lastUpdatedDate <= :date'.
Suppose that you don't have that timestamp column or there's no guarantee that it will be updated properly when changes are made; or let's say your shop loves SQLServer and wants to use rowver instead.
What to do? There are two issues to solve.. one how to generate a rowver and two is how to use the generated rowver to find Programs.
Since the database generates the rowver, you can either ask the db for the 'current max rowver' (a custom sql server thing) or you can simply save an object that has a RowVersion attribute and then use that object's generated RowVersion as the boundary for the query to find the Programs changed after that time. The latter solution is more portable is what the solution is below.
The SyncPoint class snippet below is the object that is used as a 'point in time' kind of deal. So once a SyncPoint is saved, the RowVersion attached to it is the db version at the time it was saved.
Here is the SyncPoint snippet. Notice the annotation to specify the custom converter (don't forget to make the column insertable = false, updateable = false):
/**
* A sample super class that uses RowVersion
*/
#MappedSuperclass
public abstract class SyncPoint {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// type is rowver for SQLServer, blob(8) for postgresql and h2
#Column(name = "current_database_version", insertable = false, updatable = false)
#Convert(converter = RowVersionTypeConverter.class)
private RowVersion currentDatabaseVersion;
#Column(name = "created_date_utc", columnDefinition = "timestamp", nullable = false)
private DateTime createdDate;
...
Also (for this example) here is the Program object we want to find:
#Entity
#Table(name = "program_table")
public class Program {
#Id
private Integer id;
private boolean active;
// type is rowver for SQLServer, blob(8) for postgresql and h2
#Column(name = "rowver", insertable = false, updatable = false)
#Convert(converter = RowVersionTypeConverter.class)
private RowVersion currentDatabaseVersion;
#Column(name = "last_chng_dt")
private DateTime lastUpdatedDate;
...
Now you can use these fields within your JPA criteria queries just like anything else.. here is a snippet that we used inside a spring-data Specifications class:
/**
* Find Programs changed after a synchronization point
*
* #param filter that has the changedAfter sync point
* #return a specification or null
*/
public Specification<Program> changedBeforeOrEqualTo(final ProgramSearchFilter filter) {
return new Specification<Program>() {
#Override
public Predicate toPredicate(Root<Program> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
if (filter != null && filter.changedAfter() != null) {
// load the SyncPoint from the db to get the rowver column populated
SyncPoint fromDb = synchronizationPersistence.reload(filter.changedBeforeOrEqualTo());
if (fromDb != null) {
// real sync point made by database
if (fromDb.getCurrentDatabaseVersion() != null) {
// use binary version
return cb.lessThanOrEqualTo(root.get(Program_.currentDatabaseVersion),
fromDb.getCurrentDatabaseVersion());
} else if (fromDb.getCreatedDate() != null) {
// use timestamp instead of binary version cause db doesn't make one
return cb.lessThanOrEqualTo(root.get(Program_.lastUpdatedDate),
fromDb.getCreatedDate());
}
}
}
return null;
}
};
}
The specification above works with both the binary current database version or a timestamp.. this way I could test my stuff and all the upstream code on a database other than SQLServer.
That's it really: a) type to wrap the byte[] b) JPA converter c) use attribute in query.
Using Play! framework and it's JPASupport class I have run into a problem with a legacy database.
I have the following class:
#Entity
#Table(name="product_catalog")
public class ProductCatalog extends JPASupport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer product_catalog;
#OneToOne
#JoinColumn(name="upper_catalog")
public ProductCatalog upper_catalog;
public String name;
}
Some product catalogs don't have an upper catalog, and this is referenced as 0 in a legacy database. If I supply the upper_catalog as NULL, then expectedly JPA inserts a NULL value to that database column.
How could I force the null values to be 0 when writing to the database and the other way around when reading from the database?
I don't see any easy way of achieving what you want with JPA directly (and there are great chance that even if you find a way that works with basic operation like save or load, that it will not work with more complex use case, like complex criteria / hql, none standard fetching mode, etc)
So i would do that :
#Entity
#Table(name="product_catalog")
public class ProductCatalog extends JPASupport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer product_catalog;
#Column(name="upper_catalog")
public Long upper_catalog_id;
public String name;
public ProductCatalog getUpperCatalog() {
if (upper_catalog_id == 0)
return null;
return ProductCatalog.findById(upper_catalog_id);
}
public void setUpperCatalog(ProductCatalog pc) {
if (pc == null) {
upper_catalog_id = 0;
}
else {
if (pc.id == null) {
// option 1. a bit like a cascade
pc.save();
// option 2. if you consider passing a transient entity is not valid
throw new RuntimeException("transient entity " + pc.toString());
}
upper_catalog_id = pc.id;
}
}
}
I see two options:
Use a primitive data type as Id (i.e. int instead of Integer)
If you are using Hibernate as JPA provider, use a CustomType to do the conversion