The EntityKey property can only be set when the current value of the property is null with n-tier web applicaiton - asp.net

I am working with n-tier web application with Entity Framework with ObjectDataSource.
In my web application there are Complains & WorkOrder entities which has many to one or zero relationship.
When a WorkOrder is added user can select for which complain it is for. This is what i tried to do.
<asp:DetailsView ID="DVComplain" runat="server"
DefaultMode="Insert" AutoGenerateInsertButton="True"
AutoGenerateRows="False" DataSourceID="ODSAddWorkOrder"
oniteminserting="DetailsView1_ItemInserting" >
<Fields>
<asp:TemplateField HeaderText="Complain">
<InsertItemTemplate>
<asp:DropDownList ID="DDLComplain" runat="server" DataTextField="ComplainID"
DataValueField="ComplainID" DataSourceID="EDSComplains"
oninit="DDLComplain_Init">
</asp:DropDownList>
<asp:CheckBox ID="CBIsWOOfComplain" runat="server"
oninit="CBIsWOOfComplain_Init" />
<asp:EntityDataSource ID="EDSComplains" runat="server"
ConnectionString="name=MMEntities" DefaultContainerName="MMEntities"
EnableFlattening="False" EntitySetName="ComplainMasters"
EntityTypeFilter="ComplainMaster" Select="it.[ComplainID]" OrderBy="it.ComplainID">
</asp:EntityDataSource>
</InsertItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="WOGeneratedDate" DataField="WOGeneratedDate" DataFormatString="yyyy-MM-dd" />
.......
</Fields>
</asp:DetailsView>
<asp:ObjectDataSource ID="ODSAddWorkOrder" runat="server"
DataObjectTypeName="MiantenanceManager.DAL.WorkOrder" TypeName="MiantenanceManager.BLL.WorkOrderBL"
InsertMethod="addWorkOrder">
</asp:ObjectDataSource>
And add the following code to DetailView ItemInserting method.
protected void DVWorkOrder_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
if (_CBIsWOOfComplain.Checked)
{
using (complainBL)
{
e.Values["Complain"] = complainBL.getComplianByID(_DDLComplains.SelectedValue);
}
}
}
Note: complainBL implements the IDispose.
In WorkOrederBL call it calls the WorkOrderRepository class which invokes the following method which throws the exception mention in the subject.
context.WorkOrder.AddObject(workOrder);
context.SaveChanges();
How can i overcome this problem. Is my approch is wrong??

The reason why you are getting thi serror is thet AddObject is for adding new objects. An object that has an entity key represents an existing row in the database. This assumes that you are using database generated keys for your tables.
There are different ways to fix it, for example:
Option 1: Get the record from the database, update the fields on that record, then save changes.
Option 2: Create a new object with the entity key, attach it to the context with state modified, then save changes.

Related

Display data from one to many relation using ASP.NET Web Forms data controls

I have a relation consisting of ProjectsOverall to Projects. ProjectOverall contains many Projects and a Project is assigned exactly to one ProjectOverall.
One having the detail view a ProjectOverall I want to display all it's Projects. This leaves 2 options in my WebControlling.xsd:
Configure the ProjectsOverall adapter to find all Projects that references it's ID.
Configure the Project adapter to find all entries with a given ProjectOverallID.
I tried both and stumbled upon one of these problems:
Option 1, Conigure ProjectOverall
When returning the rows the data contains multiple instances of the primary key ID of ProjectOverall. This results in an error:
Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints
References: How to resolve common errors, Force DataSet to not enforce constraints
Both options didn't work.
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public WebControlling.ProjectsDataTable GetAllProjectsForID(Guid ProjectOverallID)
{
WebControlling.ProjectsDataTable projects = new WebControlling.ProjectsDataTable();
try
{
projects.Merge(Adapter.GetAllProjectsForID(ProjectOverallID));
}
catch (Exception e)
{
throw e;
}
return projects;
}
The sql (Total overkill, but works when only one entry is returned):
SELECT wc_Countries.Name AS CountryName, wc_OrganisationUnit.Name AS OEName, wc_Projects.ID AS ProjectID, wc_ProjectsOverall.ID AS ProjectOverallID, wc_Projects.Name AS ProjectName,
wc_ProjectsOverall.ProjectName AS ProjectOverallName, wc_ProjectsOverall.*, wc_Countries.*, wc_OrganisationUnit.*, wc_Projects.*
FROM wc_ProjectsOverall INNER JOIN
wc_Countries ON wc_ProjectsOverall.CountryID = wc_Countries.ID INNER JOIN
wc_OrganisationUnit ON wc_ProjectsOverall.OrganisationUnitID = wc_OrganisationUnit.ID INNER JOIN
wc_Projects ON wc_ProjectsOverall.ID = wc_Projects.ProjectOverallID AND wc_Countries.ID = wc_Projects.CountryID AND
wc_OrganisationUnit.ID = wc_Projects.OrganisationUnitID
WHERE (wc_ProjectsOverall.ID = #ProjectOverallID)
In the detail view:
<%-- Sub Project Data Source --%>
<asp:ObjectDataSource ID="ObjectDataSourceProjects" runat="server"
OldValuesParameterFormatString="{0}"
SelectMethod="GetAllProjectsForID"
TypeName="ProjectsOverall"
DataObjectTypeName="System.Guid">
<SelectParameters>
<asp:QueryStringParameter QueryStringField="ID" Name="ProjectOverallID" Type="Object" />
</SelectParameters>
</asp:ObjectDataSource>
The second option does not work, because when I switch the type of the DataSource to TypeName="Projects I don't know how to access the ID present in the view.
The view displays all properties of a ProjectOverall and should display all itsProjects`.
<ContentTemplate>
<%-- Displays all Sub-Projects --%>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridViewProjects" runat="server" AllowPaging="True" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="ID" DataSourceID="ObjectDataSourceProjects" PageSize="5">
<Columns>
No need to show all
</Columns>
<EmptyDataTemplate>
No project matched your filter criteria. <br />
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="<%# Request.RawUrl %>">Return to the project overview.</asp:HyperLink>
</EmptyDataTemplate>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</ContentTemplate>
Is there a clean solution to do either of those things?

Dynamic Data - Selecting a table from a DropDownList to scaffold in a GridView

I have been struggling with this ASP.NET Dynamic Data problem for days now... I have a DropDownList containing the table names of all the tables in my data context (.dbml) file. When I select the DropDownList, it needs to scaffold the selected table in a GridView. My code works 100% in scaffolding the MetaTable in the GridView (it implements all the rules that I applied in my Meta Classes).
However, filtering only seems to work if I explicitly add the DynamicExpression in the declaration of the QueryExtender:
<asp:QueryExtender ID="GridQueryExtender" TargetControlID="GridDataSource" runat="server">
<asp:DynamicFilterExpression ControlID="FilterRepeater" />
</asp:QueryExtender>
This in turn requires me to specify the MetaTable explicitly in the LinqDataSource (linqdsData), either programmatically in the Page_Load or in the ASP.NET syntax.
Since the GridView gets scaffolded in the Page_Load part of the life-cycle, the above approach does not work for me, since it takes place in the Page_Init part of the life-cycle.
So my requirement is that as soon as I select another table to populate the GridView with from the DropDownList, the FilterRepeater needs to reflect the filters of the newly selected MetaTable.
Is there any way for me to programmatically update the FilterRepeater in the Page_Load so that it will contain the filters of the MetaTable that I selected in the DropDownList.
The following is some of my code:
ASP.NET Page Code-Behind:
protected void Page_Load(object sender, EventArgs e)
{
if (ddlTable.SelectedIndex > 0)
{
string tableName = ddlDataType.SelectedValue;
linqdsData.TableName = tableName;
MetaTable mt = ASP.global_asax.DefaultModel.GetTable(tableName);
GridViewData.SetMetaTable(mt, mt.GetColumnValuesFromRoute(Context));
GridViewData.EnableDynamicData(mt.EntityType);
GridViewData.DataSourceID = linqdsData.ID;
}
}
ASP.NET Page:
<asp:Panel runat="server" ID="pnlFilters" CssClass="gridFilterCon" EnableTheming="True">
<div class="filterGridHeading">
Filter the grid by:</div>
<asp:QueryableFilterRepeater runat="server" ID="FilterRepeater">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("DisplayName") %>' OnPreRender="Label_PreRender"
CssClass="gridFilterLbl" />
<asp:DynamicFilter runat="server" ID="DynamicFilter" />
<br />
</ItemTemplate>
</asp:QueryableFilterRepeater>
<asp:Button ID="btnFilter" runat="server" Text="OK"
EnableTheming="False" UseSubmitBehavior="False" OnClick="btnFilter_Click" />
</asp:Panel>
<asp:GridView ID="GridViewData" runat="server" OnSelectedIndexChanged="GridViewData_SelectedIndexChanged"
OnPreRender="GridViewData_PreRender" OnRowDataBound="GridViewData_RowDataBound"
OnPageIndexChanged="GridViewData_PageIndexChanged" AllowPaging="True" PageSize="50" OnInit="GridViewData_Init">
<Columns>
...
</Columns>
<PagerTemplate>
<asp:GridViewPager ID="GridViewPager1" runat="server" />
</PagerTemplate>
<PagerSettings Mode="NumericFirstLast" NextPageText="Next" />
</asp:GridView>
<asp:LinqDataSource ID="linqdsData" runat="server" ContextTypeName="pdcDataContext"
OnSelected="linqdsData_Selected" OnSelecting="linqdsData_Selecting" EnableUpdate="True">
</asp:LinqDataSource>
<asp:QueryExtender ID="GridQueryExtender" TargetControlID="linqdsData" runat="server">
</asp:QueryExtender>
Your help will be greatly appreciated.
It sounds like you are trying to do a lot on one web page. This creates complications of the type you are experiencing: each table requires distinct filters and MetaTables. Trying to keep each item straight requires a bunch of switch and/or if...then statements. I recommend an alternate approach. Instead of doing all of this on one page:
Create a web page for each table
Setup the appropriate filters and MetaTables
Copy the DropDownList containing the table names of all the tables to each web page, and use it to redirect to the appropriate web page.
ASP.net Dynamic Data makes it easy to create web pages for each table. That is what scaffolding is all about. With the approach above, each web page will handle its own set of concerns that are focused on the particular table.
Hope this helps.

Is it possible to have an editable DetailsView for entity objects with subclasses?

Suppose I have two classes, one derived from EntityObject and the other derived from the first:
public class Gizmo : EntityObject { ... }
public class SpecialGizmo : Gizmo { ... }
In the ASP.NET page, the user selects a Gizmo in a list (a GridView) and that Gizmo’s details are then presented in a DetailsView. The goal is for the user to be able to view and edit the details.
Here is the relevant DetailsView and its associated EntityDataSource:
<asp:DetailsView ID="GizmosDetailsView" DataSourceID="dsGizmoDetails"
AutoGenerateEditButton="True" AutoGenerateInsertButton="True"
AutoGenerateRows="False" DataKeyNames="GizmoId" runat="server">
<Fields>
<asp:BoundField DataField="GizmoId" HeaderText="GizmoId" ReadOnly="True" SortExpression="GizmoId" />
<asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" />
<!-- ... etc. --->
</Fields>
</asp:DetailsView>
<asp:EntityDataSource ID="dsGizmoDetails" runat="server" ConnectionString="[...]"
DefaultContainerName="[...]" EnableFlattening="False" EnableUpdate="True"
Where="it.[GizmoId] = #GizmoId">
<WhereParameters>
<asp:ControlParameter ControlID="gvwGizmos" Name="GizmoId" PropertyName="SelectedValue" Type="Int64" />
</WhereParameters>
</asp:EntityDataSource>
The above fails with the following exception:
InvalidOperationException: Either CommandText or EntitySetName must be defined.
That’s understandable. However, both options presented break something:
If I add EntitySetName="Gizmo", then only entities of actual type Gizmo are ever presented. If a SpecialGizmo is selected, the DetailsView comes up blank.
If I add a CommandText (or a Select) attribute, then the DetailsView no longer supports updating the data. A working “edit” button (that makes edit UI appear) is there, but then clicking “Update” after making edits simply does nothing.
Is there a proper solution to this dilemma?
I solved this using the following hack:
Do specify a CommandText on the data source, which makes the DetailsView unable to update the data automatically, but the update UI is still available.
Set the DetailsView’s OnItemUpdating event to something like this:
protected void GizmoDetailsView_Updating(object sender,
DetailsViewUpdateEventArgs e)
{
db.ExecuteStoreCommand(/* use e.Keys["GizmoId"] and e.NewValues */);
db.SaveChanges();
// manually set the DetailsView back to read-only mode
GizmoDetailsView.ChangeMode(DetailsViewMode.ReadOnly);
// need to cancel the event, as otherwise we get the following exception:
// InvalidOperationException: Update is disabled for this control.
e.Cancel = true;
}
Downside of this solution: other controls on the page that rely on the data which is thusly updated, do not refresh until a manual page reload by the user.

PostBack DataBind error: "Ensure that the control is added to the page before calling DataBind."

I have a program that allows the user to use a few dropdowns to pick a topic and attribute, then the data are pulled that match on both of those conditions. In the gridview are a lot of templatefields use textboxes for instant editing (a submit button saves all changes) as well as a template with a dropdown bound to a parameter. This was all working hunky-dory for quite a while.
Then, we changed some of the data in the tables (keeping all the same field names) and now the page loads perfectly fine on launch but then as soon as you select something different in any of the drilldown dropdowns the page fails. I get an error saying
"The DropDownList control 'TagDDL' does not have a naming container.
Ensure that the control is added to the page before calling DataBind."
(TagDDL is the dropdown in the templatefield in gridview). If I simply remove this templatefield, I get a similar (though different) error on a hyperlinkfield, removing this gives me an error in a boundfield, so obviously it's not tied to any one thing.
My idea is that it has something to do with how databinding works on post-back, since the page loads perfectly initially, the dropdowns have 'Enable PostBack' and the error messages refer to DataBind. Any ideas?
The SqlDataSource that builds Gridview (leaving out the drilldown dropdowns for now)
<asp:SqlDataSource ID="MasterTable" runat="server"
ConnectionString="<%$ ConnectionStrings:spvConnectionString %>"
SelectCommand="exec pmtv2.maintable_display 1, #IPG_Assigned, #CompetitorName, #enterprise_zone, #Banner, #BrandName"
<SelectParameters>
<asp:ControlParameter ControlID="ChooseBanner" Name="Banner" PropertyName="SelectedValue" Type="String" />
<asp:ControlParameter ControlID="ChooseIPGs" Name="IPG_Assigned" PropertyName="SelectedValue" Type="Int32" />
<asp:ControlParameter ControlID="ChooseBrands" Name="BrandName" PropertyName="SelectedValue" Type="String" />
<asp:ControlParameter ControlID="ChooseComps" Name="CompetitorName" PropertyName="SelectedValue" Type="String" />
<asp:ControlParameter ControlID="ChooseZone" Name="enterprise_zone" PropertyName="SelectedValue" Type="Int32" />
</SelectParameters>
<div id="MasterDiv" style="width:90%">
<asp:GridView ID="MasterDisplay" runat="server"
AllowSorting="True" AutoGenerateColumns="False"
DataKeyNames="productKey,banner,enterprise_zone,userID" DataSourceID="MasterTable"
OnRowDataBound="MasterDisplay_RowDataBound"
OnSorting="MasterDisplay_Sorting"
class="mGrid" AlternatingRowStyle-CssClass="mGridAlt">
</AlternatingRowStyle>
<Columns>
<asp:TemplateField HeaderText="Description" SortExpression="productdescriptionlong">
<ItemTemplate>
<a href="javascript:openPopup('JustinPractice4.aspx?UPC=<%# Eval("UPC") %>
&banner=<%# Eval("banner") %>&enterprise_zone=<%# Eval("enterprise_zone") %>')"><%# Eval("productdescriptionlong")%></a>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="BrandName" HeaderText="Brand"
SortExpression="BrandName" />
<asp:TemplateField HeaderText="New Price" SortExpression="new_base_retail">
<ItemTemplate>
<asp:TextBox ID="RWNextPrice" runat="server"
Text='<%# Bind("new_base_retail", "{0:N2}") %>' Width="60px"
class="calculate" onchange="lineItemRipple(this)"
Visible='<%# ShowBox %>'></asp:TextBox>
<asp:Label ID="RNextPrice" runat="server" Text='<%# Eval("new_base_retail", "{0:c}") %>'
Visible='<%# ShowLabel %>'></asp:Label>
<asp:HiddenField ID="lineCode" runat="server" Value='<%# Eval("line_code") %>'/>
</ItemTemplate>
</asp:TemplateField>
<asp:ImageField DataImageURLField="unique_flags" HeaderText="Flags"
DataImageURLFormatString="Media/Images/{0}.png" SortExpression="unique_flags"/>
<asp:TemplateField HeaderText="Tag Type" SortExpression="tag_type">
<ItemTemplate>
<asp:DropDownList ID="TagDDL" runat="server"
DataSourceID="dimTags"
DataTextField="Tag_type_name"
DataValueField="Tag_type_name"
SelectedValue='<%#Bind("tag_type") %>'
visible='<%#ShowBox %>'>
</asp:DropDownList>
<asp:Label ID="TagR" runat="server"
Text='<%# Eval("tag_type") %>'
Visible='<%# ShowLabel %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button ID="Commit" runat="server" Text="Commit Changes" OnClick="Commit_Click"
class="button"/>
and the relevant code behind:
protected void Page_Load(object sender, EventArgs e) {
ErrorMsg.Text = "test45";
}
protected void MasterDisplay_RowDataBound(object sender, GridViewRowEventArgs e) {
DataSourceSelectArguments sr = new DataSourceSelectArguments();
DataView dv = (DataView)CheckForCommit.Select(sr);
if (dv.Count != 0) {
CommittedOrNot.Text = dv[0][0].ToString();
}
//pulling results from a SqlDataSource confirming presence of data
//calculations to maintain a running tally of certain fields for later use
}
protected void Commit_Click(Object sender, EventArgs e) {
string tagValue = "FLAG";
foreach (GridViewRow gvr in MasterDisplay.Rows) {
tagValue = ((DropDownList)gvr.FindControl("TagDDL")).SelectedValue;
MasterDisplay.UpdateRow(gvr.RowIndex, false);
} //for every row, update it
MasterDisplay.DataBind();
}
It was a simple error of trying to add to a DDL before I had actually pulled the data needed to bind it. Changing the order of things slightly did the trick
I am glad you found your answer. I had a similar issue on a UserControl (ascx) that was being loaded a run-time. I, too, had made a change to my data source and the corresponding sql data sources. (In my case, I was replacing the sql data sources with an entity model.)
What I found was that one of my controls would bind to the new data source (via the code behind) with no problems. The code looked as follows:
gridSomeData.DataSource = controller.GetListOfAssociatedParts();
gridSomeData.DataBind();
However, in the same method, the next section of code would fail when the DataBind() method was called. The code looked as follows:
drpDataList.DataSource = controller.GetListOfParts();
drpDataList.DataTextField = "PartID"
drpDataList.DataValueField = "PartKey"
drpDataList.DataBind();
It turned out, that when I removed the prior ASCX markup for the SqlDataSource objects, I failed to remove the reference in the DataSourceID attribute of the drop down control. So when I called the DataBind() method, the binding engine checked the attributes of the control, found a name DataSourceID, and immediately went looking for it in the control hierarchy of the UserControl. When the binding engine failed to find the object, it threw the exception "The DropDownList control [...] does not have a naming container..."
I will admit that this particular exception is somewhat misleading, as it is really the binder being confused over which instructions to follow for the data source (the code behind, or the markup in the ascx file).
I hope this helps with some perspective. :)

Using FindControl to get GridView in a Content Page

I would like to find a GridView Control within a separate class and I am having issues doing so. I even tried placing my code in the aspx.cs page to no avail. I keep getting Object reference not set to an instance of an object. I'm sure there is a simple step I'm missing, but in my research I cannot seem to find anything.
Aspx code
<asp:GridView ID="GridView1" EnableViewState="true"
runat="server"
BackColor="White" BorderColor="#CC9966"
BorderStyle="None" BorderWidth="1px" CellPadding="4" Width="933px"
onrowdatabound="GridView1_RowDataBound"
onrowdeleting="GridView1_RowDeleting"
onrowediting="GridView1_RowEditing"
onrowupdating="GridView1_RowUpdating"
onsorting="GridView1_Sorting"
AllowSorting="true"
AutoGenerateColumns="False"
PersistedSelection="true" onrowcancelingedit="GridView1_RowCancelingEdit">
<EditRowStyle Font-Size="Small" Width="100px" />
<FooterStyle BackColor="#FFFFCC" ForeColor="#330099" />
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton runat="server" ID="EditLinkButton" CausesValidation="True"
Text="Edit" CommandName="Edit"/>
<asp:LinkButton runat="server" ID="DeleteLinkButton" CausesValidation="False"
Text="Delete" CommandName="Delete"/>
</ItemTemplate>
<EditItemTemplate>
<asp:LinkButton runat="server" ID="UpdateLinkButton" CausesValidation="True"
Text="Update" CommandName="Update"/>
<asp:LinkButton runat="server" ID="CancelLinkButton" CausesValidation="False"
Text="Cancel" CommandName="Cancel"/>
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
.cs code
protected void Page_Load(object sender, EventArgs e) {
SetDirectory();
System.Web.UI.Page page = (System.Web.UI.Page)System.Web.HttpContext.Current.Handler;
GridView GridViewCopy = (GridView)page.FindControl("GridView1");
Log.WriteLine("SortBindGrid: GridView Row Count: " +
GridViewCopy.Rows.Count, Log.DEBUG_LEVEL.TERSE);
return;
}
I've tried a few variations of using MainContent_GridView for the find to get and Master.FindControl with all the same result.
In one of your comments you state that the GridView isn't on the Master Page, so is it safe to assume that it's on a page that uses a Master Page? And therefore it must be in a ContentPlaceholder control?
The key issue is that FindControl method only looks for direct children (emphasis added):
This method will find a control only if the control is directly contained by the specified container; that is, the method does not search throughout a hierarchy of controls within controls.
So you either need to:
Search for the control within the correct ContentPlaceholder control, rather than from Page.
Loop through each of the Controls in Page.Controls recursively until you find the control you're after.
An example of 2:
private Control FindControlRecursive(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControlRecursive(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
Once you've got your control, you should cast it using as and then check for null just in case it's not quite what you were expecting:
var gridView = FindControlRecursively(Page, "GridView1") as GridView
if (null != gridView) {
// Do Stuff
}
Don't get the page from HttpContext if you are already within the page. Instead, is there a control you can use FindControl from? Instead of use page, use:
parentControl.FindControl("GridView1") as GridView;
Instead. There is an issue with finding the grid from the page level, and using a lower level control closer to the grid will have better success.
Brian got it right but he forgot the essential part.
You won't be able to do use his code unless you add this code on top of your HTML-Code of the file where you want to use it.(Page.aspx)
<%# MasterType VirtualPath="~/Master/Site.master" %>
then you can use the code Brian suggested:
GridView grid = this.Master.FindControl("GridView1");
Edit:
If you want to use the gridview from within another class in the same file i would use the following:
Add this to the class created when you make the page
public partial class YourPageName: System.Web.UI.Page
{
public static Gridview mygrid = this.GridviewnameOFYourASPX
...
}
And to your custom class add this in your method
YourPageName.mygrid.(The changes you want to make);

Resources