C# 使用 ObjectDataSource 和 DataObjectTypeName,如何处理只有一个 Id 参数的删除方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/913571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using ObjectDataSource and DataObjectTypeName, How Do You Handle Delete Methods With Just An Id Parameter?
提问by Laz
If I have an ObjectDataSource setup like:
如果我有一个 ObjectDataSource 设置,例如:
<asp:ObjectDataSource
ID="ObjectDataSource1"
runat="server"
DataObjectTypeName="Employee"
InsertMethod="Insert"
UpdateMethod="Update"
DeleteMethod="Select"
TypeName="EmployeeDB">
</asp:ObjectDataSource>
and a data/business object with methods like:
以及具有以下方法的数据/业务对象:
public class EmployeeDB
{
public void Insert(Employee emp)
public int Update(Employee emp)
public bool Delete(int id)
}
How do I get the objectdatasource to use the Delete method with the parameter that is not an Employee object?
如何让 objectdatasource 使用带有不是 Employee 对象的参数的 Delete 方法?
If this is not possible, what is the recommended alternative architecture?
如果这是不可能的,推荐的替代架构是什么?
Edit:
编辑:
To clarify, I want to use the method signature on my data/business object as shown above, however if I try to allow an Employee object to be passed into some of the methods using DataObjectTypeName, then I seemingly lose the ability to have some methods take just an integer id for instance.
澄清一下,我想在我的数据/业务对象上使用方法签名,如上所示,但是如果我尝试允许使用 DataObjectTypeName 将 Employee 对象传递到某些方法中,那么我似乎失去了使用某些方法的能力例如,只取一个整数 id。
If I do not use the DataObjectTypeName, then I have to place all the method parameters in the ObjectDataSource and change the methods on the data/business object to match, this seems like a bad design choice because as the Employee object changes I will have to update each of these methods. Is there a better architecture?
如果我不使用 DataObjectTypeName,那么我必须将所有方法参数放在 ObjectDataSource 中并更改数据/业务对象上的方法以匹配,这似乎是一个糟糕的设计选择,因为随着 Employee 对象的更改,我将不得不更新这些方法中的每一个。有没有更好的架构?
回答by J.W.
When you bind your objectdatasource to a control, you should be able to get an Id(in your example, I would assume EmployeeId) based on the data you try to bind to, and then you can set this as a delete parameter.
当您将 objectdatasource 绑定到控件时,您应该能够根据您尝试绑定到的数据获得一个Id(在您的示例中,我假设 EmployeeId),然后您可以将其设置为删除参数。
check out msdn examplehere.
在此处查看msdn 示例。
<asp:objectdatasource
id="ObjectDataSource1"
runat="server"
selectmethod="GetAllEmployees"
deletemethod="DeleteEmployee"
ondeleting="NorthwindEmployeeDeleting"
ondeleted="NorthwindEmployeeDeleted"
typename="Samples.AspNet.CS.EmployeeLogic">
<deleteparameters>
<asp:parameter name="EmpID" type="Int32" />
</deleteparameters>
</asp:objectdatasource>
回答by Phaedrus
You have to remove DataObjectTypeName="Employee"
from your object data source declaration. This property gets and sets the name of the class that the ObjectDataSource control uses for a parameter in your delete method.
您必须DataObjectTypeName="Employee"
从对象数据源声明中删除。此属性获取和设置 ObjectDataSource 控件用于删除方法中的参数的类的名称。
<asp:ObjectDataSource
ID="ObjectDataSource1"
runat="server"
InsertMethod="Insert"
UpdateMethod="Update"
DeleteMethod="Delete"
TypeName="EmployeeDB">
<deleteparameters>
<asp:parameter name="EmpID" type="Int32" />
</deleteparameters>
</asp:ObjectDataSource>
回答by Brendan Kowitz
Even if you implement your class as follows:
即使您按如下方式实现类:
public class EmployeeDB
{
public Employee Insert(Employee emp)
public Employee Update(Employee emp)
public void Delete(Employee emp)
}
You will only get the fields bound to the 'DataKeys'passed through on the object when you try to delete, eg the ID.
当您尝试删除时,您只会获得绑定到对象上传递的“DataKeys”的字段,例如 ID。
*On a side note, returning the object after an Insert or Update allows the DataView to update the row.
*附带说明,在插入或更新后返回对象允许数据视图更新行。
The way I remember it working was you can configure your ObjectDataSource to pass through parameters to your db layer ORpass though the objects, NOT a combination of both.
我记得它的工作方式是您可以将 ObjectDataSource 配置为将参数传递到您的数据库层或传递对象,而不是两者的组合。
Which means the following would be possible with the manually configured parameters:
这意味着可以使用手动配置的参数进行以下操作:
public class EmployeeDB
{
public int Insert(string name, DateTime dob)
public int Update(int id, string name, DateTime dob)
public void Delete(int id)
}
回答by Matt Kocaj
I was using LinqToSql for a project similar to this a short while ago. I had two different types of delete methods:
不久前,我将 LinqToSql 用于与此类似的项目。我有两种不同类型的删除方法:
public void DeleteDisconnected(Product original_entity)
{
var db = new RmsConcept2DataContext(base.ConnectionString);
db.Products.Attach(original_entity, false);
db.Products.DeleteOnSubmit(original_entity);
db.SubmitChanges();
}
public void Delete(int ID)
{
var db = new RmsConcept2DataContext(base.ConnectionString);
Product product = db.Products.Single(p => p.ProductID == ID);
// delete children
foreach (Release release in product.Releases)
db.Releases.DeleteOnSubmit(release);
db.Products.DeleteOnSubmit(product);
db.SubmitChanges();
}
.. you can see the DeleteDisconnected
method is designed to accept the whole entity object from the 'ObjectDataSource'. I was using the
.. 您可以看到该DeleteDisconnected
方法旨在接受来自“ObjectDataSource”的整个实体对象。我正在使用
ConflictDetection="CompareAllValues"
..and
..和
OldValuesParameterFormatString="original_{0}"
..params on the 'ObjectDataSource' for this case but you may not need to do that. I think i could still get concurrency exceptions on deletes with the above approach - which is what i wanted at the time - to make use of LinqToSql's built in conflict detection/concurrency features.
..params 在这种情况下的“ObjectDataSource”上,但您可能不需要这样做。我想我仍然可以使用上述方法在删除时获得并发异常 - 这就是我当时想要的 - 以利用 LinqToSql 的内置冲突检测/并发功能。
Your architecture depends on how you handle data, etc in lower layers of your app (which you don't seem mention much about in the question). It is definitely more mature design to pass around the business objects rather than all the fields as method params. But occasionly (for delete functions) only the int
ID is necessary.. but it depends on your underlying implementation.
您的架构取决于您如何处理应用程序较低层中的数据等(您在问题中似乎没有提及太多)。将业务对象而不是所有字段作为方法参数传递绝对是更成熟的设计。但有时(对于删除功能)只有int
ID 是必需的……但这取决于您的底层实现。
回答by sairam
Here is solution and it is working for me for update operation then delete far easier than update :
这是解决方案,它适用于我的更新操作,然后删除比更新容易得多:
Create Class to pass parameter. For Example : I am passing paramter for Usermaster table : UserMasterDT :
public UserMasterDT(int userid, int roleid, string Firstname, string ) { this.muserid = userid; this.mroleid = roleid; this.mfirstname = Firstname; this.mlastname = Lastname; } //Here set this prop as DUMMY output variable, that u used everywhere to get output value public int iRecUpdated { get { return mrecupdated; } set { mrecupdated = value; } }
I am binding objectdatasource to formview for update uperation.Set objectdatasource as follows :
<asp:ObjectDataSource ID="odsUser" runat="server" TypeName="MMCTaxINTLXMLValidation.UserBLL" SelectMethod="GetUserRoleByUserId" DataObjectTypeName="MMCTaxINTLXMLValidation.UserMasterDT" OldValuesParameterFormatString="original_{0}" UpdateMethod="UpdateUserWithTransaction" onupdated="odsUser_Updated"> <SelectParameters> <asp:QueryStringParameter Name="iUId" QueryStringField="UserId" Type="Int32" DefaultValue="-1" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="UserId" Type="Int32" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="RoleId" Type="Int32" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Firstname" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Lastname" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="BirthDate" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="MMCEmail" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Homecontact" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Officecontact" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Cellcontact" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Logon" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Password" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="PasswordQ1" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="PasswordA1" Type="String" ConvertEmptyStringToNull="true" /> </UpdateParameters> </asp:ObjectDataSource>
NOTICE THERE IS NO ANY OUTPUT PARAMTER HERE
Now, IN U R BLL In update method, pass and return UserMasterDT as input and output. DO NOT PASS SEPERATE PARAMETER AND OUT VARAIBLE . Write code like :
HERE I AM PASSING UserMasterDT AS IN AND OUT
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, true)] public UserMasterDT UpdateUserWithTransaction(UserMasterDT tempuser) { int iRecUpdated = -1; Adapter.BeginTransaction(); try { string sFlag = "UpdateUserRole"; int iUserId = tempuser.UserId; int iRoleId = tempuser.RoleId; string sFirstname = tempuser.Firstname; string sLastname = tempuser.Lastname; int? iReturnData; //THIS OUT IS FROM MY SQL STORED PROCE NOT WITH OBJECTDATASOURCE OUT PARAMETER. IT HAS NOTHING TO DO WITH OBJECT DATA SOUCE. Adapter.UpdateUserRole(sFlag,iUserId,iRoleId,sFirstname,sLastname,out iReturnData); if (iReturnData == 1) { iRecUpdated = 1; this.Adapter.CommitTransaction(); } else if (iReturnData == null) iRecUpdated = -1; //What ever is return, set it back to UserMasterDT's iRecUpdated prop tempuser.iRecUpdated = iRecUpdated; return tempuser; } catch (Exception ex) { if (ex != null) { Adapter.RollbackTransaction(); //CustomEX objCUEx = new CustomEX(ex.Message, ex); ex = null; iRecUpdated = -1; //-1 : Unexpected Error //What ever is return, set it back to UserMasterDT's iRecUpdated prop tempuser.iRecUpdated = iRecUpdated; return tempuser; } } finally { tempuser.iRecUpdated = iRecUpdated; } //Return tempuser back return tempuser; }
then in aspx.cs page read property of UserMasterDT as follows :
if (e.Exception == null) { UserMasterDT tempuser = (UserMasterDT) e.ReturnValue; lblMsg.Text = "Record Updated : " + tempuser.iRecUpdated.ToString(); }
My stored proc is :
set ANSI_NULLS ON create PROCEDURE [dbo].[UpdateUserRole] ( @sFlag varchar(50), @iUserId int, @iRoleId int, @sFirstname varchar(50), @sLastname varchar(50), @iReturnData int output ) as Begin Declare @errnum as int Declare @errseverity as int Declare @errstate as int Declare @errline as int Declare @errproc as nvarchar(100) Declare @errmsg as nvarchar(4000) ----------------------- if @sFlag = upper('UPDATEUSERROLE') begin begin try begin tran --Update User Master Table UPDATE tblUserMaster SET Firstname = @sFirstname, Lastname = @sLastname, WHERE UserId = @iUserId --Update tblUserRolesTran Table update tblUserRolesTran set roleid = @iRoleId where Userid = @iUserId commit tran -- If commit tran execute then trancount will decrease by 1 -- Return Flag 1 for update user record and role record SET @iReturnData = 1 end try begin catch IF @@Trancount > 0 -- Get Error Detail In Variable Select @errnum =@@ERROR, @errseverity = ERROR_SEVERITY(), @errstate = ERROR_STATE(), @errline = ERROR_LINE(), @errproc = ERROR_PROCEDURE(), @errmsg = ERROR_MESSAGE() rollback tran -- To see print msg, keep raise error on, else these msg will not be printed and dislayed print '@errnum : ' + ltrim(str(@errnum )) print '@errseverity : ' + ltrim(str(@errseverity)) print '@errstate : ' + ltrim(str(@errstate)) print '@errline : ' + ltrim(str(@errline)) print '@errproc : ' + @errproc print '@errmsg : ' + @errmsg --Raise error doesn't display error message below 50000 --So we have to create custom message and add to error table using sp_addmessage ( so_addmessage is built-in sp) --In custom error message we have line number and error message Declare @custerrmsg as nvarchar(4000) Select @custerrmsg = 'Proc: UpdateUserRole, Line: ' + ltrim(str(@errline)) + ': ' + @errmsg if (@errnum < 50000) EXEC SP_ADDMESSAGE @msgnum = 50002, @severity = 16, @msgtext = @custerrmsg, @replace = 'REPLACE' --After adding custom error message we need to raise error --Raise error will appear at client (.net application) RAISERROR(50002,16,1) end catch end set nocount off End
创建类传递参数。例如:我正在为 Usermaster 表传递参数: UserMasterDT :
public UserMasterDT(int userid, int roleid, string Firstname, string ) { this.muserid = userid; this.mroleid = roleid; this.mfirstname = Firstname; this.mlastname = Lastname; } //Here set this prop as DUMMY output variable, that u used everywhere to get output value public int iRecUpdated { get { return mrecupdated; } set { mrecupdated = value; } }
我将 objectdatasource 绑定到 formview 以进行更新 uperation.Set objectdatasource 如下:
<asp:ObjectDataSource ID="odsUser" runat="server" TypeName="MMCTaxINTLXMLValidation.UserBLL" SelectMethod="GetUserRoleByUserId" DataObjectTypeName="MMCTaxINTLXMLValidation.UserMasterDT" OldValuesParameterFormatString="original_{0}" UpdateMethod="UpdateUserWithTransaction" onupdated="odsUser_Updated"> <SelectParameters> <asp:QueryStringParameter Name="iUId" QueryStringField="UserId" Type="Int32" DefaultValue="-1" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="UserId" Type="Int32" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="RoleId" Type="Int32" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Firstname" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Lastname" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="BirthDate" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="MMCEmail" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Homecontact" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Officecontact" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Cellcontact" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Logon" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="Password" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="PasswordQ1" Type="String" ConvertEmptyStringToNull="true" /> <asp:Parameter Name="PasswordA1" Type="String" ConvertEmptyStringToNull="true" /> </UpdateParameters> </asp:ObjectDataSource>
注意这里没有任何输出参数
现在,在 IN UR BLL 更新方法中,传递并返回 UserMasterDT 作为输入和输出。不要传递单独的参数和输出变量。编写代码如下:
在这里,我正在传入和传出 UserMasterDT
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, true)] public UserMasterDT UpdateUserWithTransaction(UserMasterDT tempuser) { int iRecUpdated = -1; Adapter.BeginTransaction(); try { string sFlag = "UpdateUserRole"; int iUserId = tempuser.UserId; int iRoleId = tempuser.RoleId; string sFirstname = tempuser.Firstname; string sLastname = tempuser.Lastname; int? iReturnData; //THIS OUT IS FROM MY SQL STORED PROCE NOT WITH OBJECTDATASOURCE OUT PARAMETER. IT HAS NOTHING TO DO WITH OBJECT DATA SOUCE. Adapter.UpdateUserRole(sFlag,iUserId,iRoleId,sFirstname,sLastname,out iReturnData); if (iReturnData == 1) { iRecUpdated = 1; this.Adapter.CommitTransaction(); } else if (iReturnData == null) iRecUpdated = -1; //What ever is return, set it back to UserMasterDT's iRecUpdated prop tempuser.iRecUpdated = iRecUpdated; return tempuser; } catch (Exception ex) { if (ex != null) { Adapter.RollbackTransaction(); //CustomEX objCUEx = new CustomEX(ex.Message, ex); ex = null; iRecUpdated = -1; //-1 : Unexpected Error //What ever is return, set it back to UserMasterDT's iRecUpdated prop tempuser.iRecUpdated = iRecUpdated; return tempuser; } } finally { tempuser.iRecUpdated = iRecUpdated; } //Return tempuser back return tempuser; }
然后在 aspx.cs 页面读取 UserMasterDT 的属性如下:
if (e.Exception == null) { UserMasterDT tempuser = (UserMasterDT) e.ReturnValue; lblMsg.Text = "Record Updated : " + tempuser.iRecUpdated.ToString(); }
我的存储过程是:
set ANSI_NULLS ON create PROCEDURE [dbo].[UpdateUserRole] ( @sFlag varchar(50), @iUserId int, @iRoleId int, @sFirstname varchar(50), @sLastname varchar(50), @iReturnData int output ) as Begin Declare @errnum as int Declare @errseverity as int Declare @errstate as int Declare @errline as int Declare @errproc as nvarchar(100) Declare @errmsg as nvarchar(4000) ----------------------- if @sFlag = upper('UPDATEUSERROLE') begin begin try begin tran --Update User Master Table UPDATE tblUserMaster SET Firstname = @sFirstname, Lastname = @sLastname, WHERE UserId = @iUserId --Update tblUserRolesTran Table update tblUserRolesTran set roleid = @iRoleId where Userid = @iUserId commit tran -- If commit tran execute then trancount will decrease by 1 -- Return Flag 1 for update user record and role record SET @iReturnData = 1 end try begin catch IF @@Trancount > 0 -- Get Error Detail In Variable Select @errnum =@@ERROR, @errseverity = ERROR_SEVERITY(), @errstate = ERROR_STATE(), @errline = ERROR_LINE(), @errproc = ERROR_PROCEDURE(), @errmsg = ERROR_MESSAGE() rollback tran -- To see print msg, keep raise error on, else these msg will not be printed and dislayed print '@errnum : ' + ltrim(str(@errnum )) print '@errseverity : ' + ltrim(str(@errseverity)) print '@errstate : ' + ltrim(str(@errstate)) print '@errline : ' + ltrim(str(@errline)) print '@errproc : ' + @errproc print '@errmsg : ' + @errmsg --Raise error doesn't display error message below 50000 --So we have to create custom message and add to error table using sp_addmessage ( so_addmessage is built-in sp) --In custom error message we have line number and error message Declare @custerrmsg as nvarchar(4000) Select @custerrmsg = 'Proc: UpdateUserRole, Line: ' + ltrim(str(@errline)) + ': ' + @errmsg if (@errnum < 50000) EXEC SP_ADDMESSAGE @msgnum = 50002, @severity = 16, @msgtext = @custerrmsg, @replace = 'REPLACE' --After adding custom error message we need to raise error --Raise error will appear at client (.net application) RAISERROR(50002,16,1) end catch end set nocount off End
Hope this helps you whatever u need to do. sairam
希望这可以帮助你无论你需要做什么。赛拉姆
回答by Stuart
The objects method signature variable names must match the objects property names exactly and it will work. eg edit* also dont forget the DataKeyNames attribute
对象方法签名变量名称必须与对象属性名称完全匹配,并且它会起作用。例如编辑*也不要忘记 DataKeyNames 属性
public class EmployeeDB
{
public int ID{get;set;}
public string Name {get;set;}
public ing Age{get;set;}
//both these would work
public void Insert(Employee emp)
public void Insert(string Name,int Age)
//both these would work
public void Update(Employee emp)
public void Update(int ID,string Name,int Age)
//works
public void Delete(Employee emp)
//WONT work
public bool Delete(int empId)
//will work
public void Delete(int ID)
}
<asp:ObjectDataSource
ID="ObjectDataSource1"
runat="server"
DataObjectTypeName="Employee"
InsertMethod="Insert"
UpdateMethod="Update"
DeleteMethod="Select"
TypeName="EmployeeDB"
DataKeyNames="ID">
<DeleteParameters>
<asp:Parameter Name="ID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
回答by alonso.torres
Got to this question hoping to solve this exact dilemma, and after trying what other answers propose, got to the conclusion that you just CAN'T mix methods. Either all your methods (Insert, Update, Delete) have:
得到了这个问题,希望能解决这个确切的困境,在尝试了其他答案之后,得出的结论是你不能混合方法。您的所有方法(插入、更新、删除)都具有:
- a single custom class parameter (whose type is specified at the
DataObjectTypeName
attribute), or - a list of parameters for each of the properties (specified within each
<InsertParameters>
,<UpdateParameters>
or<DeleteParameters>
).
- 单个自定义类参数(其类型在
DataObjectTypeName
属性中指定),或 - 的各性能参数的列表(在每个指定的
<InsertParameters>
,<UpdateParameters>
或<DeleteParameters>
)。
You can't have Insert
method using the single parameter version and the Delete
method using just an Int32
parameter. My findings got confirmed when I read the ObjectDataSource
documentationwhich clearly says:
您不能Insert
使用单参数版本的Delete
方法和仅使用Int32
参数的方法。当我阅读清楚说明的ObjectDataSource
文档时,我的发现得到了证实:
When the DataObjectTypeName property is set and the ObjectDataSource control is associated with a data-bound control, the methods that are specified by the InsertMethod and DeleteMethod properties must each have one parameter of the type that is specified in the DataObjectTypeName property.
当设置了 DataObjectTypeName 属性并且 ObjectDataSource 控件与数据绑定控件相关联时,由 InsertMethod 和 DeleteMethod 属性指定的方法都必须具有一个在 DataObjectTypeName 属性中指定的类型的参数。
The only exception might be the Update
method which can have one or two parameters depending upon the ConflictDetection
property, but that's a whole different story.
唯一的例外可能是Update
根据ConflictDetection
属性可以有一个或两个参数的方法,但那是完全不同的故事。
The only solution I got to was to overload the Delete
method to trick the ObjectDataSource
, and internally call my Delete
method with the Int32
parameter:
我得到的唯一解决方案是重载Delete
方法来欺骗ObjectDataSource
,并Delete
使用Int32
参数在内部调用我的方法:
// This method gets called by the ObjectDataSource
public Int32 Delete(MyCustomClass foo)
{
// But internally I call the overloaded method
Delete(foo.Id);
}
public Int32 Delete(Int32 id)
{
// Delete the item
}
回答by Jaswinder
In case when DataObjectTypeName is used instead of separate parameters, the business object you are using will work. You just need to add DataKeyNames="Id" where Id is the primary key field in Your Bound control like GridView or something else.
如果使用 DataObjectTypeName 而不是单独的参数,您正在使用的业务对象将起作用。您只需要添加 DataKeyNames="Id" 其中 Id 是您的绑定控件中的主键字段,例如 GridView 或其他内容。
It will populate your type parameter, in your case "Employee" type with "Id" value which can be used to delete entity.
它将填充您的类型参数,在您的情况下,“员工”类型具有可用于删除实体的“Id”值。