ASP.Net WebForms Model Binding GridView Using Entity Framework 5.0.0.0 - asp.net

im binding List<T> to my GridView where T is the object created by EntityFramework from my database. There is a foreign key in my said table and i want to display its corresponding text value in the GridView.
<asp:TemplateField HeaderText="Foreign Key Type">
<ItemTemplate>
<asp:Label ID="LabelID" Visible="true" runat="server"
Text="<%# Item.<foreignKeyTable>.Text %>" >
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
when i do that i get the following error
The operation cannot be completed because the DbContext has been disposed.
how can i get pass that

Most likely, you have query code that does the following:
using (var ctx = new SomeContext())
{
var data = ctx.Data.Where(..).ToList();
return data;
}
You may not have a using, but either way, the context has it's Dispose() method on it, and any unloaded navigation properties will always fail, because there is no context alive for it to attach to. Even though you may use the context within a method, and may not be storing it globally, as long as you aren't explicitly disposing the context, by calling the method or with a using statement, you will be fine.

Related

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

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.

Set TemplateField HeaderText dynamic for localization

I am trying to create localization for my ASP.NET code, but I have issues setting the TemplateField's HeaderText
I have this that works
<asp:TemplateField HeaderText="Description">
<ItemTemplate>
<%# Eval("Description") %>
</ItemTemplate>
<FooterTemplate>
<asp:Panel ID="Panel5" runat="server" DefaultButton="EditSubmission">
<asp:TextBox runat="server" ID="Submission_DescriptionTxtBox" TextMode="MultiLine"
ToolTip='<%# GetById("atforbedringsforslag_description_tooltip") %>'/>
</asp:Panel>
</FooterTemplate>
</asp:TemplateField>
But I want to change
<asp:TemplateField HeaderText="Description">
To
<asp:TemplateField HeaderText='<%# GetById("atforbedringsforslag_description_title") %>'>
But then I get
Databinding expressions are only supported on objects that have a DataBinding event. System.Web.UI.WebControls.TemplateField does not have a DataBinding event.
How should I set this field? I can find some that uses OnRowCreated, but then you access the fields with an index number, and then it becomes easy to make mistakes or forgot to change indexes if new fields are added later on
EDIT My solution:
Created the custom expression builder
using System.Web.Compilation;
using System;
using System.CodeDom;
public class LocalizationExpressionBuilder : ExpressionBuilder
{
public override CodeExpression GetCodeExpression(System.Web.UI.BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
{
CodeExpression[] inputParams = new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()),
new CodeTypeOfExpression(entry.DeclaringType),
new CodePrimitiveExpression(entry.PropertyInfo.Name) };
// Return a CodeMethodInvokeExpression that will invoke the GetRequestedValue method using the specified input parameters
return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(this.GetType()),
"GetRequestedValue",
inputParams);
}
public static object GetRequestedValue(string key, Type targetType, string propertyName)
{
// If we reach here, no type mismatch - return the value
return GetByText(key);
}
//Place holder until database is build
public static string GetByText(string text)
{
return text;
}
}
Added the prefix to my web.config
<system.web>
<compilation debug="true" defaultLanguage="c#" targetFramework="4.0">
<expressionBuilders>
<add expressionPrefix="localizeByText" type ="LocalizationExpressionBuilder"/>
</expressionBuilders>
</compilation>
</system.web>
And I can now get my text like this
<asp:TemplateField HeaderText='<%$ localizeByText:Some text %>'>
You can build your own custom Expression Builder which calls your GetById method. Look at the following link for an old but good article explaining how to build an expression builder and how to use it:
https://web.archive.org/web/20210304125044/https://www.4guysfromrolla.com/articles/022509-1.aspx
When you have an expression builder, you use it with the <%$ syntax. This is different from the databinding syntax <%#.
For the HeaderText field, it is not allowed to use DataBinding syntax (not sure why, but that's how MS made it). Using expression syntax IS allowed and will work once you have your custom expression builder done.
Do go through the page I linked to, it's quite a lot of text, but in the end making you expression builder will not take much effort...
Also, the page has a link at the bottom to a library of expression builder that the author has made. Have a look at them, maybe one of them could be used directly to solve your problem (specifically, the CodeExpressionBuilder).

How can I use Client ID within a server control?

I was able to do the following in a repeater and it works just fine:
<asp:TextBox ID="txtOtherPartyName" Text='<%# Eval("Name") %>' runat="server" />
<asp:RequiredFieldValidator ID="reqLoss" ControlToValidate="txtOtherPartyName"
data-valmsg-for='<%# Container.FindControl("txtOtherPartyName").ClientID%>'
Display="Dynamic" ErrorMessage="Name of party involved is required." runat="server" />
The data-valmsg-for attribute in the required validator rendered the server control id just fine. How can I do the same for a control that sits on a page and is not within a repeater? I tried similar things without any luck.
Update
So here's what I'm looking to do exactly:
<asp:Textbox id="txtTest" runat="server" />
<asp:RequiredFieldValidator id="reqTest" data-valmsg-for=**[i want the html rendered id of "txtTest" here]** runat="server" />
You can bind that custom attribute to the control's client id in the code behind.
butthead.Attributes.Add("data-custom-prop", beavis.ClientID);
In the Repeater you used databinding syntax (<%# ... %>), but that doesn't apply in this case. You should be able to set the attribute like this:
data-valmsg-for='<%= TextBox1.ClientID %>'
Why are you adding custom attributes to the validators in the first place though, if you don't mind me asking? What purpose does it serve that can't be achieved through other means?
EDIT
Have you considered building your list of control IDs in code-behind beforehand, and storing it somewhere? Could something like this be a starting point?:
var controlList = Page.Validators.OfType<BaseValidator>()
.Select(v => Page.FindControl(v.ControlToValidate).ClientID).ToList();
EDIT
Expanding on that idea, you can serialize the list to JSON using the JavaScriptSerializer (System.Web.Script.Serialization):
//initialize the javascript serializer
var serializer = new JavaScriptSerializer();
//retrieve all of the validators on the page and store the client ids in a list
var controlList = Page.Validators.OfType<BaseValidator>()
.Select(v => Page.FindControl(v.ControlToValidate).ClientID).ToList();
//serialize the control id list and register the script block on the page
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "data", string.Format("var data = {0}", serializer.Serialize(controlList)), true);
Depending on how complex the form is, you may need to create a recursive function to find all of the controls, but hopefully this demonstrates the concept.

How do I bind a GridView to a custom object?

If I have the following ASP.NET code (it's not complete - obviously there's a lot missing, but none of it matters):
<asp:GridView>
<Columns>
<asp:TemplateField>
<ItemTemplate>
My Label: <asp:Label />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
My Text Box: <asp:TextBox />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
And if I have something like this in the CodeBehind:
Private MyListOfObjects As List(Of MyObject)
...
Public Class MyObject
Public MyTextBoxString as String
Public MyLabelString as String
End Class
How can I bind the GridView so that one row is equivalent to one item in my MyListOfObjects list, and so that the data will populate and persist across page loads or postbacks? I've never done custom databinding like this before, so a full explanation would be very helpful. All the tutorials I've come across so far only talk about using GridViews directly with Database query results, and that's not what I need.
Thanks!
Just set the datasource of the gridview to your object.
MyGridView.DataSource = myList
MyGridView.DataBind()
Here's a very similiar post:
Binding a method that returns List<employee> to a gridview
Looks like you are using a list in vb.net. Remember lists can hold integers, strings, dates, objects (these include user defined types (your object)).
So you can bind a gridview to a list object by setting the datasource property to your list.
In the above example, myList, might hold a ton of employee objects, etc. So assign it to the datasource and .DataBind() and voila a gridview with each row containing your object.
You can do something like
My Label: <asp:Label id="myLabel" runat="server" Text='<%# Eval("MyTextBoxString") %>' />
in the markup and similar stuff for your textbox.
GridView1.DataSource = MyListOfObjects
GridView1.DataBind()
First remember any binding controls like GridView, DropdownList e.t.c bind to public properties, so first make your public members to public properties.
Then create objects of MyObject class and add them to your List<MyObject> object
Finally you can persist this list object by saving it in Session or ViewState to maintain it after postbacks.
I hope you can do it now!!! you can ask for more help if neccessary.

Using a FormView with LINQ

I am using a FormView control to edit a record fetched using a LinqDataSource. In essence, the markup for the FormView and the data source looks like this:
<asp:FormView ID="RuleInstancePropertiesFormView" runat="server" DataKeyNames="RuleInstanceId"
DataSourceID="RuleInstanceDataSource" DefaultMode="Edit" Visible="false"
CssClass="PropertiesTable">
<EditItemTemplate>
<asp:Label ID="RuleInstanceId" runat="server" Text='<%# Eval("RuleInstanceId") %>' />
<telerik:RadTextBox ID="RuleInstanceNameTextBox"
runat="server"
Text='<%# Bind("Rule.Name") %>' />
<telerik:RadTextBox ID="LimitIndexTextBox"
runat="server"
Text='<%# Bind("LimitIndex") %>' />
</EditItemTemplate>
</asp:FormView>
<asp:LinqDataSource ID="RuleInstanceDataSource" runat="server"
ContextTypeName="Questionnaire.QuestionnairesDataContext"
TableName="RuleInstances" EnableUpdate="true">
</asp:LinqDataSource>
The record I am editing has a foreign key reference to another table, namely the "Rule" table. In the FormView, I need to edit fields in this foreign key table (I have a binding to the Rule.Name field in the above code).
When I try to save my changes, the local field (LimitIndex) is saved correctly in the database, but the foreign field (Rule.Name) isn't. I'm a bit puzzled by this, as the FormView correctly fetches the current value of Rule.Name, but refuses to persist it back to the LINQ object.
I hope anyone can help, thanks :)
You're specifying which table to use as a datasource using the TableName property. This controls which table Linq2Sql will update and so is limiting the update to just the RulesInstances table.
Okay, I found an alternative (but not elegant) solution. I use an ObjectDataSource instead of a LinqDataSource. I have then defined an class
public class RuleInstanceProjection {
public RuleInstanceProjection(){}
// Bind properties from the LINQ object.
public RuleInstanceProjection(RuleInstance instance) { ... }
public int RuleInstanceId {get; set;}
public string Rule_Name {get;set;}
...
}
The object above takes a RuleInstance as constructor, and updates its own properties with all the values that I need in the FormView. I then configure the ObjectDataSource to use another custom class, RuleInstanceProjectionDS, as data source. This class handles updates and inserts of my custom RuleInstanceProjection class:
public class RuleInstanceProjectionDS
{
public RuleInstanceProjectionDS() { }
public RuleInstanceProjection GetRuleInstance(int ruleInstanceId)
{ /* Retrieve the RuleInstance, and instantiate
a RuleInstanceProjection object */ }
public void SaveInstanceProjection(InstanceProjection projection)
{ /* Map fields back to the LINQ objects and submit. */ }
}
I can therefore handle all mappings in these two classes. Now, I only have to decide if this is actually better than updating the GUI controls directly...
I must say it isn't the most elegant solution, and I hope there will be a better data binding framework in later versions of ASP.NET. The current one only seems to handle the most trivial cases :(.

Resources