problem with asp.net gridview - asp.net

I have problem with gridview deleting.I have table name Doctor with
Id,Name,Address,Phone.Id is auto generated field.After adding data
when i am displaying in gridview then if delete any id from gridview
Again then if i add any new details from the form its starting from
the new number.I mean if i delete the last id no 5 then again if i
add any new doctor its taking id value 6 not from 5.My query is it
should start again from 5.Here is my code.Pls help me.
public class Doctor
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
}
public static class DoctorDataLayer
{
public static void AddDoctor(Doctor doctor)
{
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; // JohannesH: Changed from .ToString() to .ConnectionString
using(var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand("insert into doctor values(#name,#address,#phone)", connection))
{
command.Parameters.AddWithValue("#name", doctor.Name);
command.Parameters.AddWithValue("#address", doctor.Address);
command.Parameters.AddWithValue("#phone", doctor.Phone);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
}
}
}
public static class DoctorBusinessLayer
{
public static void CreateDoctor(string name, string address, string phone)
{
DoctorDataLayer.AddDoctor(new Doctor {Name = name, Address = address, Phone = phone});
}
}

This is perfectly normal database behaviour and has nothing to do with your GridView. If you have an issue with gaps in autogenerated (identity) columns, either use your own logic to generate unique ID's or use custom SQL scripts to check for gaps in Identity values and fill those gaps.
Example B in the Transact-SQL reference shows a way to do just this.

So the Id is created by the database (autonumber). When id 5 is used it's used up. This is normal behavior.

As other have noted, if this is an autogenerated ID from the DB then once it is used it will not be regenerated, each ID is unique regardless if the data still exists or not. If IDs were recycled you could get into issues with foreign references that may have pointed to the old item with that ID and now would point to a new different record with the reused ID.
Typically you don't expose the IDs to the user anyway so it is a non issue.

You shouldn't depend on autogenerated ids sequences being ordered or not having gaps. As others have noted, the behavior you are seeing is perfectly normal behavior for an autogenerated id and to make it otherwise you'll need to jump through a lot of hoops. If you need the ids to be ordered by the insertion sequence, you should put in an autogenerated date/time field and then select the data ordered by that field (and index it). That way if you ever decide to switch from a numeric id to a GUID or some other id format in which the sort order is different than the insertion order your data will still be ordered correctly. If you need to have a "place order" for each, generate that automatically (say a rownumber) as you are selecting ordered by date. That way you will still have strict numerical ordering even if records get deleted later.

Related

Unwanted unique constraint in many to many relationship

I'm trying to set up a Tagging tool for images. Basically I have two tables, one for pictures, and one for tags. Both are connected with a many to many setup. I can already add a single tag to a picture, and the same tag to different pictures. However, when I try to add a second tag to an image I get an exception complaining about a unique constraint that I simply don't see.
public class MediaEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<TagEntity> Tags { get; set; }
}
public class TagEntity
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<MediaEntity> MediaEntities { get; set; }
}
public void updateMedia(MediaEntity model)
{
using (var db = new MediaContext(_dbLocation))
{
db.Update(model);
db.SaveChanges();
}
}
public class MediaContext : DbContext
{
private const string DB_NAME = "PT.db";
private string _path;
public DbSet<MediaEntity> MediaTable { get; set; }
public DbSet<TagEntity> TagTable { get; set; }
public MediaContext(string path)
{
_path = path;
ChangeTracker.AutoDetectChangesEnabled = false;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={Path.Combine(_path, DB_NAME )}");
}
As far as I can tell my setup should create a normal many-to-many relationship, and it the database I also see pretty much this. EF automatically creates a TagTable, MediaTable, and MediaEntityTagEntityTable. But when I try to add a second tag I get this:
SqliteException: SQLite Error 19: 'UNIQUE constraint failed:
MediaEntityTagEntity.MediaEntitiesId, MediaEntityTagEntity.TagsId'.
Data from the table showing I can have the same tag on different pictures:
MediaEntitiesId
TagEntitiesId
1B48E85B-F097-4216-9B7A-0BA34E69CBFF
CF581257-F176-4CDF-BF34-09013DCEAA27
CE33F03F-5C80-492B-88C6-3C40B9BADC6C
CF581257-F176-4CDF-BF34-09013DCEAA27
523178A1-C7F8-4A69-9578-6A599C1BEBD5
0C45C9D1-7576-4C62-A495-F5EF268E9DF8
I don't see where this unique constaint comes in. How can I set up a proper many-to-many relationship?
I suspect the issue you may be running into is with the detached Media and associated Tags you are sending in. You are telling EF to apply an 'Update' to the media, but the DbContext will have no idea about the state of the Tags attached. Assuming some tags may have been newly attached, others are existing relationships. If the Context isn't tracking any of these Tags, it would treat them all as inserts, resulting in index violations (many to many) or duplicate data (many to one / one to many)
When dealing with associations like this, it is generally simpler to define more atomic actions like: AddTag(mediaId, tagId) and RemoveTag(mediaId, tagId)
If you are applying tag changes along with potential media field updates in a single operation I would recommend rather than passing entire entity graphs back and forth, to use a viewModel/DTO for the tag containing a collection of TagIds, from that apply your tag changes against the media server side after determining which tags have been added and removed.
I.e.:
public void updateMedia(MediaViewModel model)
{
using (var db = new MediaContext(_dbLocation))
{
var media = db.Medias.Include(x => x.Tags).Single(x => x.MediaId = model.MedialId);
// Ideally have a Timestamp/row version number to check...
if (media.RowVersion != model.RowVersion)
throw new StaleDataException("The media has been modified since the data was retrieved.");
// copy media fields across...
media.Name = model.Name;
// ... etc.
var existingTagIds = media.Tags
.Select(x => x.TagId)
.ToList();
var tagIdsToRemove = existingTagIds
.Except(model.TagIds)
.ToList();
var tagIdsToAdd = model.TagIds
.Except(existingTagIds)
.ToList();
if(tagIdsToRemove.Any())
media.Tags.RemoveRange(media.Tags.Where(x => tagIdsToRemove.Contains(x.TagId));
if(tagIdsToAdd.Any())
{
var tagsToAdd = db.Tags.Where(x => tagIdsToAdd.Contains(x.TagId)).ToList();
media.Tags.AddRange(tagsToAdd);
}
db.SaveChanges();
}
}
Using this approach the DbContext is never left guessing about the state of the media and associated tags. It helps guard against stale data overwrites and unintentional data tampering (if receiving data from web browsers or other unverifiable sources), and by using view models with the minimum required data, you improve performance by minimzing the amount of data sent over the wire and traps like lazy load hits by serializers.
I always explicitly create the join table. The Primary Key is the combination of the two 1:M FK attributes. I know EF is supposed to map automatically, but since it isn't, you can specify the structure you know you need.

Update database items from the website

My current problem is (probably) not necessarily directly related to MVC 6, but how working with database actually works, and therefore any help/suggestions in this matter would be more than appreciated.
For the sake of this question, let's say that we have a very simple database with the following tables (C# classes) [we are using Entity Framework to work with the database]:
public class ShoppingUser
{
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<ShoppingItem> Items { get; set; }
}
public class ShoppingItem
{
public int Id { get; set; }
public string Quantity { get; set; }
public string Text { get; set; }
public bool ToRemove { get; set; }//if item has been bought, it can be removed from the shopping list
}
This demo will be for a super duper simple shopping list app, where user (ShoppingUser who is registered in the system can have a List of ShoppingItem where user can decide on what is the text of the item (e.g. Bread, Butter, Tomatoes, ...) and also a quantity (3 pieces, 5kg, ... simple string)
Afterwards in my ASP.NET Core app, I have defined a repository which is communicating with the database and has access to the ShoppingItem class (as we are only interested in shopping items of currently logged in user).
Example of some method we could use here:
public IEnumerable<ShoppingItem> ReturnUserItems(string sUsername)
{
if (string.IsNullOrWhiteSpace(sUsername))
return null;
var result = _context.ShoppingUsers.Include(n => n.Items).Where(n => n.UserName == sUsername).FirstOrDefault();
if (result != null)
return result.Items;
else
return null;
}
Finally we have an API controller with JsonResult for either GET, POST, DELETE, ..., which is used for communication between client side AngularJs App and our server side logic.
Example of GET Method:
// GET: /<controller>/
[HttpGet("")]
public JsonResult Get(string sUserName)
{
try
{
var results = _repository.ReturnUserItems(User.Identity.Name);
if (results != null)
{
var result = Mapper.Map<IEnumerable<ShoppingItemViewModel>>(results);
return Json(result);
}
Response.StatusCode = (int)HttpStatusCode.OK;
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { Message = ex.Message });
}
return null;
}
Here comes the tricky part (at least for me). From video tutorials I have learned, that I should never (or almost never) expose my real database model to the website (I guess it's for security reasons). Due to that (as visible from my GET method above) I have declared my ShoppingItemViewModel which contains only properties I want to expose to the user (e.g. meaning that Id of my item is not visible).
This is how it looks like:
public class ShoppingItemViewModel
{
public string Quantity { get; set; }
[Required]
public string Text { get; set; }
[Required]
public bool ToRemove { get; set; }//if item has been bought, it can be removed from the shopping list
}
And for the communication from my AngularJS App I am using simple $http.get and $http.post calls for retrieving / posting updated data.
Finally the question:
My problem is, that if a user decides to either delete an item from his shopping list, or decides to change the content of either text / quantity (meaning that originally in the database it was tomatoes - 5 kg but he manages to buy only 2 kg and therefore changes the quantity to tomatoes - 3kg), how can the app understand which elements have actually been changed and how? The problem I have in this case is, that we are no longer exposing the database Id of the items.
If I was writing a desktop app, where I wouldn't have to create this sub view (ShoppingItemViewModel), my EntityFramework is intelligent enough to check & update all the changes in my database. Unfortunately in this case, I do not understand how this is achievable.
When I was thinking about it I came with the following: Add a new property into the ShoppingItem and ShoppingItemViewModel: public string sCustomKey {get; set; }, which would serve as a unique key for every item. This way, we no longer need to expose our database Id, but we are exposing the 'fake' one.
Second question:
I case my solution would be accurate, what is the best way to update items in the database? The only way I can think of is iterating through all the items in the database and manually check for changes?
Example of what I have in mind:
//IEnumerable<ShoppingItem> would be re-mapped result of ShoppingItemViewModel we have received back from the website
public void UpdateValues(IEnumerable<ShoppingItem> items, string sUserName)
{
//retrieves list of shopping items for specified customer
var allItems = _context.ShoppingUsers
.Include(n => n.Items)
.FirstOrDefault(n => n.UserName == sUserName);
//updates the values
foreach (var sItem in items)
{
var updatedItem = allItems.Items.FirstOrDefault(n => n.Text == sItem.sCustomKey);
if (updatedItem == null)
{
//create new item
var newItem = new ShoppingItem();
newItem.Text = sItem.Text;
newItem.ToRemove = sItem.ToRemove;
allItems.Items.Add(newItem);
}
else
updatedItem.ToRemove = sItem.ToRemove;
}
_context.SaveChanges();
}
But this approach does not seem right to me.
Any help regarding these matters would be more than appreciated as I am still learning how to work with ASP.NET Core and web projects.
In your first question, exposing the item ID in the ViewModels is fine. In your domain layer, you can add validation logic that those ID exists/valid item.
Alternatively, you can use a Guid for your item/product because the ID (int) can easily be predicted.
As far as updating the items, you should not use the "username" as Identifier (of the cart) because that can be predicted/altered by the calling client. You can use Guid either persisted(to Db) or
in-memory. You can add validation as well if this Guid belongs to this username/emailAddress. So updating the items in the cart, consider adding/removing one at a time if that is doable
instead of sending list of items.
I think you have misunderstood something.
Here comes the tricky part (at least for me). From video tutorials I have learned, that I should never (or almost never) expose my real database model to the website (I guess it's for security reasons). Due to that (as visible from my GET method above) I have declared my ShoppingItemViewModel which contains only properties I want to expose to the user (e.g. meaning that Id of my item is not visible).
ViewModel <=> Domain Model <=> ReadModel (Database Model)
The point is that you shouldn't use your ReadModel(Database model) as your ViewModel in Presentation Layer (MVC). All three models will have identity.

Create DB trigger to Sql DB

Im new to DB stuff and I need to create DB trigger to azure SQL DB.
I've create MVC5 project with model first and I have this model which reflect the DB table.
public class Employee
{
public int Id { get; set; }
public string EmployeeName { get; set; }
public string EmpAddress { get; set; }
public string EmpEmail { get; set; }
public string EmpPhone { get; set; }
}
public class EmployeeDbContext : DbContext
{
public EmployeeDbContext()
: base("DefaultConnection")
{
}
public DbSet<Employee> EmployeeCollection { get; set; }
}
}
Now I want to create DB trigger for it and I try like following.(I take this example from SO and adjust it...)
Create Trigger Dbo.triggerName ON Employee
AFTER INSERT, Update, Delete
AS Begin
-- Log Deleted Rows and Old Value
Insert Into EmployeeCopy(Id ,EmployeeName, EmpAddress,EmpEmail,EmpPhone)
Select CURRENT_EMPLOYEE, Columns
From Deleted
-- Log Inserted Row and New Value
Insert Into EmployeeCopy(Id ,EmployeeName, EmpAddress,EmpEmail,EmpPhone)
Select CURRENT_EMPLOYEE, Columns
From Inserted
End
I want it to create new table which is EmployeeCopy and copy all the rows to it.
so this is my question (I new to this topic)
1. what is the trigger name?
2.Do I write the Insert into right ?
3. Select CURRENT_EMPLOYEE, Columns ..Here Im not sure if this is the right way to write it?
Your insert into statement may be changed (see below) - but you did not specify condition of your select => you will probably insert all rows from your deleted and Inserted tables on each trigger operation.
See more details about insert into, for example, here.
You should probably write and test your select statement as stand-alone query (just run it from New Query window), When you'll get expected results - put it in the trigger's logic.
Generally, I'll suggest to try each part of your trigger logic as stand-alone statements.
Create Trigger dbo.MyTrigger ON Employee
AFTER INSERT, Update, Delete
AS Begin
-- Log Deleted Rows and Old Value
Insert Into EmployeeCopy
Select (Id ,CURRENT_EMPLOYEE, EmpAddress,EmpEmail,EmpPhone)
From Deleted
WHERE Your_conditions_logic_here
-- Log Inserted Row and New Value
Insert Into EmployeeCopy
Select (Id ,CURRENT_EMPLOYEE, EmpAddress,EmpEmail,EmpPhone)
From Inserted
WHERE Your_conditions_logic_here
End

Entity Framework CTP5, code-first. Many to many with cascade delete

I have two entities (Customer and CustomerRole) and would like to declare many-to-many relationship between them. I can do using the following code:
modelBuilder.Entity<CustomerRole>()
.HasMany(cr => cr.Customers)
.WithMany(c => c.CustomerRoles)
.Map(m => m.ToTable("Customer_CustomerRole_Mapping"));
But it creates the relationship (and the third mapping table) with cascade delete switched off by default. How can I tell EF to create the relationship with cascade delete switched on when using many-to-many?
As of CTP5, there seems to be no way to directly turn on cascade deletes on Many to Many associations by Fluent API.
That said, if your intention is to make sure that you can delete the principal (e.g. a Customer record) without having to worry about the dependent record in the join table (i.e. Customer_CustomerRole_Mapping) then you don't need to turn on cascades on the database since EF Code First will take care of the cascade deletes on the client side when it comes to Many to Many associations.
For example, when you delete a Customer object, EF is smart enough to first send a delete statement to get rid of the dependent record in the join table and after that it will send another delete statement to delete the Customer record.
Update:
Due to a bug in CTP5, you need to explicitly eager/Lazy load the navigation property and have it loaded on the context when you remove the dependent. For example, consider this model:
public class User
{
public int UserId { get; set; }
public virtual ICollection Addresses { get; set; }
}
public class Address
{
public int AddressID { get; set; }
public virtual ICollection Users { get; set; }
}
Assuming that we have a User with an address in the database, this code will throw:
using (EntityMappingContext context = new EntityMappingContext())
{
User user = context.Users.Find(1);
context.Users.Remove(user);
context.SaveChanges();
}
However, this one will perfectly work with removing the link table's record first:
using (EntityMappingContext context = new EntityMappingContext())
{
User user = context.Users.Find(1);
((IObjectContextAdapter)context).ObjectContext
.LoadProperty(user, u => u.Addresses);
context.Users.Remove(user);
context.SaveChanges();
}
Please note that this is just a workaround and we will be able to (hopefully) remove a principal without loading its navigation property.

Adding/updating child and parent record same time

Can someone please show me the easiest way to create/update a parent and child record at the same time (like customer with multiple addresses) with least or no code as possible? Both Web Forms and in MVC.
The basic idea would be to create/update the parent record and return the new ID (key). Then use that key to create the related child records. For example, say you have an Events table and a related EventDates table:
public static int CreateEvent(
out int eventId,
DateTime datePosted,
string title,
string venue,
string street1,
string city,
string state,
string zipCode)
{
...
}
public static void AddEventDates(
int eventDateID,
int eventID,
DateTime startDate,
DateTime endDate)
{
...
}
It's important to maintain data integrity here; if one of the updates fails then both need to be returned to the original state. You could implement this yourself or use transactions:
http://msdn.microsoft.com/en-us/library/z80z94hz%28VS.90%29.aspx

Resources