This project is read-only.

Better way?

Sep 2, 2010 at 10:47 PM

While it does seem to work, hopefully this is an example of how NOT to persist changes to a many-to-many relation using NHDAL. I've created a simple example project which has a Request table that has an Id, and a RequestTitle.  Then there is a State Type_Table which has an Id and a Name field which contains static data with the 2 letter state abbreviations.  Then, I've created a Request_State many-to-many associative table to join them together.  Finally, I've created a simple one page asp.net form to populate a request which has a Label for the RequestId (in the case of updates), a TextBox for the Title and a multiple selection ListBox bound to the states list and a Submit button.  After submitting the first time, it posts back to the same page passing the new RequestId as a query string, subsequent submits would then be edits to that request.  Below is the codebehind file for the Form which does all the processing.  I'd welcome suggestions on better ways to perfoem these actions using NHDAL, but I'm particularly interested in better ways to handle the updates done inside the ProcessStates1 routine, it seems very inelegant.  Should I somehow be using Subdomains? I'm using auditing, hence all the passing around of Request.ServerVariables["LOGIN_USER"].  Thanks for any input.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using Company.Example.Business.Objects;

namespace WebFormsApp
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Company.Example.Business.ConfigurationValues.GetInstance().ConnectionString = ConfigurationManager.ConnectionStrings["Company.Example"].ConnectionString;
            if (!IsPostBack)
            {
                BindStates();
                if (!string.IsNullOrEmpty(Request.QueryString["id"]))
                {
                    ViewState["Mode"] = "Update";
                    LoadForm();
                }
                else
                {
                    ViewState["Mode"] = "Create";
                }
            }
        }

        protected void LoadForm()
        {
            Guid RequestId = new Guid(Request.QueryString["id"]);
            RequestCollection rCol = RequestCollection.RunSelect(Request.ServerVariables["LOGON_USER"]);
            Company.Example.Business.Objects.Request req = rCol.SingleOrDefault(x => x.Id == RequestId);
            lblRequestId.Text = req.Id.ToString();
            txtRequestTitle.Text = req.Title;
            foreach (State item in req.StateList)
            {
                lbStatesAffected.Items.FindByText(item.Name).Selected = true;
            }
        }

        protected void BindStates()
        {
            lbStatesAffected.DataSource = Enum.GetNames(typeof(StateConstants));
            lbStatesAffected.DataBind();
        }

        protected void btnSubmit_Click(object sender, EventArgs e)
        {
            RequestCollection rCol;
            Company.Example.Business.Objects.Request req;
            if (ViewState["Mode"].ToString() == "Update")
            {
                Guid RequestId = new Guid(Request.QueryString["id"]);
                rCol = RequestCollection.RunSelect(Request.ServerVariables["LOGON_USER"]);
                req = rCol.SingleOrDefault(x => x.Id == RequestId);  
            }
            else
            {
                rCol = new RequestCollection(Request.ServerVariables["LOGON_USER"]);
                req = rCol.NewItem(Guid.NewGuid());
            }
            req.Title = txtRequestTitle.Text;
            if (ViewState["Mode"].ToString() == "Create")
            {
                rCol.AddItem(req);
            }
            rCol.Persist();
            ProcessStates1(req.Id);
            
            Response.Redirect("Default.aspx?id=" + req.Id.ToString(), false);
        }

        protected void ProcessStates1(Guid RequestId)
        {
            Request_StateCollection rsCol = Request_StateCollection.RunSelect(x => x.RequestId == RequestId, Request.ServerVariables["LOGON_USER"]);
            Request_State rs;
            foreach (ListItem item in lbStatesAffected.Items)
            {
                if (item.Selected)
                {
                    rs = rsCol.SingleOrDefault(r => r.State == (StateConstants)Enum.Parse(typeof(StateConstants), item.Value));
                    if (rs == null)
                    {
                        rs = rsCol.NewItem(RequestId, ((StateConstants)Enum.Parse(typeof(StateConstants), item.Value)).GetHashCode());
                        rsCol.AddItem(rs);
                        rs.Persist();
                    }
                }
                else
                {
                    rs = rsCol.SingleOrDefault(r => r.State == (StateConstants)Enum.Parse(typeof(StateConstants), item.Value));
                    if (rs != null)
                    {
                        rs.Delete();
                        rs.ParentCollection.Persist();
                    }
                }       
            }
            rsCol.Persist();
        }
    }
}
Sep 5, 2010 at 7:50 PM

After reading the code, this looks like it would work perfectly well, though it is a bit "wordy". There is nothing wrong with this implementation but we can shorten it a bit. First off you do have a N:M relationship conceptually and you have marked it in your model as such. That is good. In your model, you have marked the "RequestState" table as Associative so you can access the opposite collections of objects on both sides of your relationship. For example a "State" object will have a "RequestList" and a "Request" object will have a "StateList". You should never need to directly access the "RequestState" collection. So in the "ProcessStates1" method I would change the code to this.

protected void ProcessStates1(Request request)
{
StateCollection stateCollection = StateCollection.RunSelect(); //Load the State collection
request.StateList.Clear(); //Clear the state collection for this request
foreach (ListItem item in lbStatesAffected.Items)
{
if (item.Selected)
request.StateList.AddItem(stateCollection.First(x => x.State == item.Value)); //not sure about your table structure but the state, name, id or whatever your field of selection is
}
request.SubDomain.Persist();
}

This may not exactly compile because I am not sure about every facet of your model, but this should be very close. I simply clear all associative states and then repopulate the list. I know some purists will say this is not optimal but we are talking about a handful of state objects keep in mind. I load the state collection from the database use it to get the actual state object I need to associate with a request object.