I have a table with a DATETIME DEFAULT field.
CREATE TABLE People
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
...
DtOccurrence DATETIME DEFAULT getDATE(),
);
Using scaffolding for generate Class and Entitity for Controllers + Views.
Default CRUD working fine, but if I try update a register, [DtOccurrence] get NULL in database.
How fix it? Thanks in advance
Create saving OK
Update only [Name] field send null [DtOccurrence] for database and my auto-generated class dont have this [DtOccurrence] field:
UPDATE:
CONTROLLER Create method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] People people)
{
if (ModelState.IsValid)
{
_context.Add(people);
await _context.SaveChangesAsync();
return RedirectToAction("Edit", "Pessoas", new { people.Id });
}
return View(people);
}
CONTROLLER Edit method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (id != people.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(people);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PeopleExists(people.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(people);
}
Auto-generated class scaffolding
public partial class Pessoa
{
public Pessoa()
{
public int Id { get; set; }
public string Name { get; set; }
}
}
As mentioned in my comment, while your initial request to provide data to the view was given an entity from the DB Context, the object (Person) you get back in your Update method is not the same entity, and is not associated with your DbContext. It is a deserialized copy. Calling Update with it when it does not contain all fields will result in fields getting set to #null. Calling Update with a detached entity like this from a client is also an attack vector for unauthorized updates to your domain. (Debugging tools /plugins can intercept the call to the server and alter the entity data in any number of ways.)
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,")] People people)
{
if (!ModelState.IsValid)
return View(people);
var dataPeople = await _context.People.SingleAsync(x => x.id == people.id);
dataPeople.name = people.name;
await _context.SaveChangesAsync(); // dataPeople is a tracked entity and will be saved, not people which is acting as a viewmodel.
return RedirectToAction(nameof(Index));
}
Using Update will generate an update statement where all fields on the entity are overwritten. You may decide to pass an incomplete entity to the view, or an incomplete entity back from the view, but EF has no notion of what data is missing because it wasn't provided/changed, vs. what was cleared out so it updates everything. Instead, you should load the entity from the DbContext based on the ID provided (which will error if the ID is not found) then set the properties you want to change on that tracked entity before calling SaveChanges. This ensures that the resulting SQL update statement contains only the columns you want changed.
As a general rule I recommend using view model classes for communicating models between server and client so it is clear what the data being passed around actually is. Passing entities between server and views is an anti-pattern which is prone to performance problems, serialization issues, and both intentional and accidental data corruption.
Additional validations should include making sure the changes are complete/legal, and potentially checking a row version # or last modified date between the passed model and the data loaded from the DB to ensure they match. When the user opened the page they may have gotten version #1 of the record. When they finally submit the form, if the DB returned version #2, it would indicate that someone else modified that row in that time. (Otherwise you are overwriting the changes)
Related
I'm fetching data from a website (JSON) and save it in a database. This works well. However, I want to upgrade the database with a new version number every time the data fetched from the internet changes. For that, I think the best way is to drop the table, upgrade the database number version and recreate the table before filling it with the new data (at least there is a way to insert only new records and update only the old ones that have changed).
I saw that it could be done with the onUpgrade callback on a Database from the sqflite plugin.
So, I created an Helper with an init() method that open the database.
However, I don't understand when the onUpgrade callback is called. Indeed, in this code below, version is always at 1.
I would like to have a method that init the DB and :
creates it if it does not exists OR
open the current version if no number version is specified (something like that) OR
upgrade to a new version by auto-incrementing the version number (by calling database.upgrade() for example).
Do you think this is possible in a unique method or do I need to split that in two methods ? If yes, what's the point of the onUpgrade callback ?
class DBHelper {
// Private constructor
DBHelper._privateConstructor();
// Get an instance of DBHelper
static final DBHelper _dbHelper = DBHelper._privateConstructor();
// Getter to get the instance of the DBHelper
factory DBHelper() => _dbHelper;
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await init();
return _database;
}
Future<Database> init() async {
print("DBHelper: init database");
// Get a location using path_provider
String path = await getDBPath();
// I THINK ALL HAPPENS HERE
return await openDatabase(path, version: 1, onCreate: _onCreate, onUpgrade: _onUpgrade);
}
void _onCreate(Database db, int version) async {
print("DBHelper: _onCreate called");
// When creating the db, create the table
_createTable();
}
void _onUpgrade(Database db, int oldVersion, int newVersion) async{
print("DBHelper: _onUpgrade called");
try {
await db.transaction((Transaction txn) async {
await txn.execute("DROP TABLE TABLE_NAME");
});
} catch (e) {
print("Error : " + e.toString());
}
_createTable();
}
void _createTable() async {
Database db = await database;
try {
await db.transaction((Transaction txn) async {
await txn.execute("CREATE TABLE TABLE_NAME ("
"TABLE_ID INTEGER PRIMARY KEY AUTOINCREMENT,"
"TABLE_INT INTEGER,"
"TABLE_TEXT TEXT,");");
});
} catch (e) {
print("Error : " + e.toString());
}
}
}
Best
Database versioning in sqflite matches what has been done in Android where the version is a constant for a particular version of your app with a particular schema. onCreate/onUpgrade should typically get called only once in the lifecyle of your application. Anyway it won't get called unless you close and re-open your database. See https://github.com/tekartik/sqflite/blob/master/sqflite/doc/migration_example.md.
So I would say that the user version as it exists now does not match your need. So I would not used this value nor onUpgrade in your scenario. However you could defined your own singleton value (i.e. your own versioning system) and drop/create table in a transaction while the database is open. Nothing prevents you from doing that.
I am using Entity Framework to link my data to asp.net mvc application.
The weird part is that I can read the records normally using the below GetALL function but none of the Insert and Delete are working. Is there any reason for that? I am wondering if there is any restriction on the database and how to fix it if any? Note that I am getting no error however it is saying create is successful and delete is successful as well.
public IEnumerable<tbl_Category> GetALL()
{
return db.tbl_Category.ToList();
}
public tbl_Category GetByID(int Id)
{
return db.tbl_Category.Find(Id);
}
public void Insert(tbl_Category cat)
{
db.tbl_Category.Add(cat);
}
public void Delete(int Id)
{
tbl_Category cat = db.tbl_Category.Find(Id);
db.tbl_Category.Remove(cat);
}
I think you have to call the SubmitChanges() / SaveChanges() on the data context.
I tried to solve by myself, but... Looks like I need help from people.
I have Business Silverlight application with WCF RIA and EntityFramework. Access to Database I get via LinqToEntites.
Common loading data from database I making by this:
return DbContext.Customers
This code returns full Customers table from DataBase. But sometimes I do not need to show all data. Easy way is use linq filters in client side by next code:
public LoadInfo()
{
...
var LO1 = PublicDomainContext.Load(PublicDomainContext.GetCustomersQuery());
LO1.Completed += LO1Completed;
...
}
private void LO1Completed(object sender, EventArgs eventArgs)
{
...
DatatViewGrid.ItemsSource = null;
DatatViewGrid.ItemsSource = loadOperation.Entities.Where(c=>c ...filtering...);
//or PublicDomainContext.Customers.Where(c=>c ...filtering...)
...
}
However this way has very and very important flaw: all data passing from server to client side via DomainService may be viewed by applications like Fiddler. So I need to come up with another way.
Task: filter recieving data in server side and return this data.
Way #1: LinqToEntites has a beautiful projection method:
//MSDN Example
var query =
contacts.SelectMany(
contact => orders.Where(order =>
(contact.ContactID == order.Contact.ContactID)
&& order.TotalDue < totalDue)
.Select(order => new
{
ContactID = contact.ContactID,
LastName = contact.LastName,
FirstName = contact.FirstName,
OrderID = order.SalesOrderID,
Total = order.TotalDue
}));
But, unfortunately, DomainServices cannot return undefined types, so this way won't work.
Way #2: I found next solution - make separate DTO classes (DataTransferObject). I just read some samples and made on the server side next class:
[DataContract]
public partial class CustomerDTO
{
[DataMember]
public int ISN { get; set; }
[DataMember]
public string FIO { get; set; }
[DataMember]
public string Listeners { get; set; }
}
And based this class I made a row of methods which return filtered data:
[OperationContract]
public List<CustomerDTO> Customers_Common()
{
return DbContext.Customers....Select(c => new CustomerDTO { ISN = c.ISN, FIO = c.FIO, Listeners = c.Listeners }).ToList();
}
And this works fine, all good...
But, there is strange problem: running application locally does not affect any troubles, but after publishing project on the Web Site, DomainService returns per each method HTTP 500 Error ("Not Found" exception). Of course, I cannot even LogIn into my application. DomainService is dead. If I delete last class and new methods from application and republish - all works fine, but without speacial filtering...
The Question: what I do wrong, why Service is dying with new classes, or tell me another way to solve my trouble. Please.
U P D A T E :
Hey, finally I solved this!
There is an answer: Dynamic query with WCF RIA Services
Your best shot is to find out what is causing the error. For that, override the OnError method on the DomainService like this:
protected override void OnError(DomainServiceErrorInfo errorInfo)
{
/* Log the error info to a file. Don't forget inner exceptions.
*/
base.OnError(errorInfo);
}
This is useful, because only two exceptions will be passed to the client, so if there are a lot of nested inner exceptions, you should still be able to see what actually causes the error.
In addition, you can inspect the error by attaching the debugger to the browser instance you are opening the site with. In VS2010 this is done by doing [Debug] -> [Attach to Process] in the menu-bar.
I'm execute method Datastore.delete(key) form my GWT web application, AsyncCallback had call onSuccess() method .Them i refresh http://localhost:8888/_ah/admin immediately , the Entity i intent to delete still exist. Smilar to, I refresh my GWT web application immediately the item i intent to delete still show on web page.Note the the onSuccess() had been call.
So, how can i know when the Entity already deleted ?
public void deleteALocation(int removedIndex,String symbol ){
if(Window.confirm("Sure ?")){
System.out.println("XXXXXX " +symbol);
loCalservice.deletoALocation(symbol, callback_delete_location);
}
}
public AsyncCallback<String> callback_delete_location = new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}
public void onSuccess(String result) {
// TODO Auto-generated method stub
int removedIndex = ArryList_Location.indexOf(result);
ArryList_Location.remove(removedIndex);
LocationTable.removeRow(removedIndex + 1);
//Window.alert(result+"!!!");
}
};
SERver :
public String deletoALocation(String name) {
// TODO Auto-generated method stub
Transaction tx = Datastore.beginTransaction();
Key key = Datastore.createKey(Location.class,name);
Datastore.delete(tx,key);
tx.commit();
return name;
}
Sorry i'm not good at english :-)
According to the docs
Returns the Key object (if one model instance is given) or a list of Key objects (if a list of instances is given) that correspond with the stored model instances.
If you need an example of a working delete function, this might help. Line 108
class DeletePost(BaseHandler):
def get(self, post_id):
iden = int(post_id)
post = db.get(db.Key.from_path('Posts', iden))
db.delete(post)
return webapp2.redirect('/')
How do you check the existence of the entity? Via a query?
Queries on HRD are eventually consistent, meaning that if you add/delete/change an entity then immediately query for it you might not see the changes. The reason for this is that when you write (or delete) an entity, GAE asynchronously updates the index and entity in several phases. Since this takes some time it might happen that you don't see the changes immediately.
Linked article discusses ways to mitigate this limitation.
I'm developing a dynamic data app, in which I'm trying perform insert RequestRevision during update of request (logging change of request, somthing like that)
public partial class ProjectDataContext
{
partial void UpdateRequest(Request instance)
{
RequestRevision rv = new RequestRevision(Requests.GetOriginalEntityState(instance));
this.ExecuteDynamicInsert(rv);
this.ExecuteDynamicUpdate(instance);
}
}
this code error "The operation cannot be performed for the entity because it is not being change tracked."
when I added code for change tracking
public partial class ProjectStatusDataContext
{
partial void UpdateRequest(Request instance)
{
bool tracking = ObjectTrackingEnabled;
ObjectTrackingEnabled = true;
RequestRevision rv = new RequestRevision(Requests.GetOriginalEntityState(instance));
//instance.RequestRevisions.Add(rv); --not working
this.ExecuteDynamicInsert(rv);
//this.RequestRevisions.InsertOnSubmit(rv); -- not working
ObjectTrackingEnabled = tracking;
this.ExecuteDynamicUpdate(instance);
}
}
Now I'm getting error "Data context options cannot be modified after results have been returned from a query."
I achieve this using sql triggers. Which fire when an update id performed on the request table.