Create DB trigger to Sql DB - asp.net

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

Related

Having difficulty trying to come up with an effective linq lambda expression

Stats:
ASP.NET 4.5.2
MVC 5
Identity 2.2
I am able to make the SQL statement, as I know SQL decently enough, but I am rather new to the whole LINQ Lambda thing.
I have also looked into the two tools most often cited for this job, but sorry -- Linqer is unable to run because the Microsoft tool it uses to create the SQL connection (the dbml file) refuses to install on my Win8.1 system, and LinqPad doesn’t provide an actual translation until you actually buy the product (which makes the “trial” fundamentally broken in the first place, IMHO: how can you possibly evaluate something that prevents you from conducting the action that you are evaluating?).
I am hoping that someone can take my SQL statement below and convert it into a proper LINQ Lambda expression that I can more effectively plug into my controller. This is also going to be a part of a multi-model display, such that multiple lambda expressions will be collated into a model that displays multiple outputs (very similar but slightly different outputs, one for users that shows only the user's dashboard, one for managers that also need the user's dashboard in addition to their managerial dashboard).
My expression is thus:
SELECT
co.CompanyId
, co.CompanyName
, co.CompanyCity
, co.NumberOfEmployees
, co.ProspectingScore
, po.ProvinceAbbr
, cd.PDFResourceLibrary
, cd.PresentationDone
, cd.MOUDone
FROM Company AS co
LEFT JOIN Province AS po ON co.ProvinceId = po.ProvinceId
OUTER APPLY (SELECT TOP 1 MAX(CycleDate) AS CycleDate, PDFResourceLibrary, PresentationDone, MOUDone FROM Cycle AS cy (NOLOCK) WHERE cy.CompanyId = co.CompanyId GROUP BY PDFResourceLibrary, PresentationDone, MOUDone) AS cd
WHERE co.RegionId = '66BD50DD-B6CB-E511-8265-14FEB5FBEAE8'
ORDER BY co.ProspectingScore DESC
For simplicity's sake, the po.ProvinceAbbr and its associated left join can be dropped, the RegionID will be brought in via a variable pulled from a claim, and if need be you can use wildcards (*) instead of named fields for all tables except for the MAX requirement on the Outer Apply (there are more than one cycles per company, I just want to bring back the most recent cycle by date). What you see above is the absolute minimum number of items needed to be pulled to populate the front end.
I have created the appropriate Models in my model namespace:
public class HomeViewModel {
public IEnumerable<DashboardUserData> RegionalCompanies { get; set; }
public IEnumerable<Company> AllOtherCompanies { get; set; }
}
public class DashboardUserData {
public Guid CompanyId { get; set; }
public string CompanyName { get; set; }
public string CompanyCity { get; set; }
public string ProvinceAbbr { get; set; }
public int? NumberOfEmployees { get; set; }
public int? ProspectingScore { get; set; }
public bool? PDFResourceLibrary { get; set; }
public bool? PresentationDone { get; set; }
public bool? MOUDone { get; set; }
}
And I hope to attach the lambda expression to the RegionalCompanies as shown in the model.
As an example, the AllOtherCompanies (which is the Managerial dashboard) has as its original lambda expression the following:
viewModel.AllOtherCompanies = await db.Company.Where(c => c.RegionId != regionId).Include(c => c.Province).ToListAsync();
So you can see that I am at least halfway there -- I am able to bring in the Company table, but I have no clue how to include the Cycle table for each company such that only the most recent Cycle is included with the company and all others are dropped.
Frankly, with the lambda I am still unsure as to how to connect the Cycle table such that only the most recent cycle (by date) is chosen. All other cycles for any company MUST be ignored and dropped, so from what I can tell neither a plain Join nor a GroupJoin would be effective here.
Suggestions?
Why don't you run it as SQL? Like:
var results = ctx.Companies.SqlQuery("SELECT ...", "66BD50DD-B6CB-E511-8265-14FEB5FBEAE8");
Where the GUID is passed as a parameter and the results translated into Company. You need to declare it in the SQL as #p0.

EF Code first : Cannot insert explicit value for identity column in table 'myTable' when IDENTITY_INSERT is set to OFF

Set the primary key to identity in database (SQL Server)
When using EF to insert data I am getting error
Cannot insert explicit value for identity column in table 'myTable'
when IDENTITY_INSERT is set to OFF
with this particular entity, though I am using the same approach with other entities to insert the data and getting no error with them.
public class MyTable
{
[Key/*,DatabaseGenerated(DatabaseGeneratedOption.Identity)*/]
public int ID{get;set;}
public string name {get;set;}
public string type {get;set;}
public string status {get;set;}
public int country {get;set;}
public DateTime start_date {get;set;}
public DateTime due_date {get;set;}
}`
Query
MyTable check= new MyTable();
check.name = checkName;
check.type = "DB";
check.status = "New";
check.country = country;
check.start_date = DateTime.Now;
check.due_date = dueDate;
db.myTables.Add(check);
db.SaveChanges();
Updated :
After uncommenting the above line
(DatabaseGenerated(DatabaseGeneratedOption.Identity))
it's throwing an exception
A dependent property in a ReferentialConstraint is mapped to a
store-generated column. Column: 'ID'
Try this:
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
I am not sure if it is in your case too....but the second error says..you are using your primary key as foreign key in some relation which is only valid for property having DatabaseGeneratedOption.None .
So to use your primary key as identity you can decorate it with DatabaseGeneratedOption.Identity
and in that case it will throw an exception because you can not use identity for relation(use DatabaseGeneratedOption.None instead).
Try this once:
objDBEntities.MyTable.AddObject(
new tblEmployee
{
check.name = checkName,
check.type = "DB",
check.status = "New",
check.country = country,
check.start_date = DateTime.Now,
check.due_date = dueDate
}
);
objDBEntities.SaveChanges();
For more details : Entity Framework: How to add row data with supporting to identity column on
Cannot insert explicit value for identity column in table 'myTable' when IDENTITY_INSERT is set to OFF
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'
It looks like you didn't define your 1....1 table relation correctly. if your pk is fk make sure you used "[Key,ForeignKey("Caller")]".
I hate the way EF throw an exception. takes decays to finding out the root causes. :(
if it is not fixed your issue post both tables are related. or for root cause it is always better moving your related tables to a simple solution and tested out there so you eliminated other facts. and also make yourself more familiar with convention,fluent api and data annotation.

Gridview with Dictionary

I've created a Dictionary like
Dictionary<Department,bool> dict= new Dictionary<Department,bool>();
here Department is a class and I have Id,Name and Code for the departments. And in the bool am sending whether the person is HOD or not.
Am adding the records to this Dictionary like
dict.Add(department,chkHOD.checked);
here the records are successfully added to the Dictionary and after this am binding the Dictionary to a GridView like
gridDept.Datasource=dict;
gridDept.Databind();
now the inserted records are displayed fine in the gridview. After this am storing this records in the 'StaffDepartments' table in my database. I have 3 columns in the 'Staffdepartments' table
1.StaffId(PK - has link with the Staff table)
2.DepartmentId(PK - has link with the Department table)
3.IsHOD.
here the records are stored fine in the database.No problem in adding the records into the database.
I have some questions here
*1.How can check whether the DepartmentId is already there in the Dictionary before adding to it.
2.When am editing the staff detail how can I delete the Selected Department from the Dictionary by checking the checkbox in Gridview rows.(here the records are coming from the database, so when I click delete button the records should be deleted in the database as well)*
if its a List instead of Dictionary, I can get the DepartmentId by
int departmentId = (int)gridDept.DataKeys[row.RowIndex].Values["DepartmentId"];
but in Dictionary i dunno how to do the same with Key and Value pairs....can anyone help me here.
How can check whether the DepartmentId is already there in the
Dictionary before adding to it.
You could use this:
if (!dict.Keys.Any(d => d.DepartmentId == department.DepartmentId))
dict.Add(department,chkHOD.checked);
But something is wrong here. If your real key is the DepartmentId and not the Department (object identity) you should make it the key in the dictionary. For example, you could define a helper class:
public class DepartmentBindingHelper
{
public int DepartmentId { get; set; }
public Department Department { get; set; }
public bool Checked { get; set; }
}
An then define a dictionary like this:
var dict = new Dictionary<int, DepartmentBindingHelper>();
And add the objects this way to the dictionary:
if (!dict.ContainsKey(department.DepartmentId))
dict.Add(department.DepartmentId, new DepartmentBindingHelper
{
DepartmentId = department.DepartmentId,
Department = department,
Checked = chkHOD.checked
});
Then you can bind only the value collection to the grid:
gridDept.Datasource = dict.Values;// it's an IEnumerable<DepartmentBindingHelper>
gridDept.Databind();
And your code to retrieve the DepartmentId from a row would work without changes:
int departmentId = (int)gridDept.DataKeys[row.RowIndex].Values["DepartmentId"];

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.

problem with asp.net gridview

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.

Resources