locked
How to create a simple custom entity? RRS feed

  • Question

  • I have an ADO.NET entity model created from my database. There are two entities, Linkage and Parcel, associated by a foreign key relationship to each other that I am querying and binding to controls on my Winform. Specifically, I am binding the query result to a BindingNavigator. It lets me do this and it lets me page through the records, but the problem I'm having is that it won't let me add records since my BindingSource is anonymous (or at least that's what I suspect to be the case). How can I query these two entities and cast them to a strongly typed object? Does it make sense to create a new custom entitity, called LinkageParcel for instance, that uses Linkage as a base entity and Parcel as a complex type, and then cast my query as a collection of LinkageParcel objects instead of var? If so, how would I go about doing that?

                var linkages = from l in tte.Linkage
                               where l.TBM.TBMID == tbmid
                               select new
                               {
                                   l.MatchID,
                                   l.Parcel.ParcelNo
                               };
                bsl = new BindingSource();
                bsl.DataSource = linkages;
                bsl.AllowNew = true;

                this.bindingNavigator1.BindingSource = bsl;

     


    Wednesday, August 24, 2011 6:52 PM

Answers

  • I finally fixed it with a workaround. I had to re-query the database after calling tte.SaveChanges() to get my new entity back with the reference properties populated. It seems like this should be built in functionality when using BindingSource.


    tte.SaveChanges();

    bsl.DataSource = from tlinthis.tte.Linkage.Include("Parcel").Include("TBM")where tl.TBM.TBMID == tbmid

    select tl;

    bindingNavigator1.BindingSource = bsl;

    • Marked as answer by BradInDallas Wednesday, August 31, 2011 2:10 PM
    Wednesday, August 31, 2011 2:10 PM

All replies

  • Hi Brad,

    how are you trying to add records?

    I think it wouldn't make sense to create a new entity just for displaying MatchID and Parcel.ParcelNo, you'd just better add new records by writting something like this - tte.Linkage.Add(new Linkage{ ... = ... });

    then refresh the binding source again to get updated collection of your anonymous objects and leave 'var' as is because this doesn't matter

     

    Regards


    clarity vs precision
    Thursday, August 25, 2011 8:14 AM
  • Hi Giorgi,

    I'm going to try this. There is no BindingSource.Refresh() method, though. How do I refresh the BindingSource ?

    Thanks,

    Brad

    Thursday, August 25, 2011 1:30 PM
  • Hi Giorgi, I wanted to add something. Basically, I am able to add new entities successfully. I can see them in the database after I execute DataContext.SaveChanges(). I just need my BindingSource to re-read them. If I execute DataContext.Refresh() I get an error.

                                    //next add the Linkage record, after adding the parcel record
                                    try
                                    {
                                        lnk.TBMReference.EntityKey = new EntityKey("TAXTESTEntities1.TBM", TBMekv);
                                        tte.AddToLinkage(lnk);
                                        tte.SaveChanges();
                                        bsl.ResetBindings(false);
                                        tte.Refresh(System.Data.Objects.RefreshMode.StoreWins, bsl);
                                    }

                               


    {System.InvalidOperationException: The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.
       at System.Data.Objects.ObjectContext.RefreshCheck(Dictionary`2 entities, Object entity, EntityKey key)
       at System.Data.Objects.ObjectContext.AddRefreshKey(Object entityLike, Dictionary`2 entities, Dictionary`2 currentKeys)
       at System.Data.Objects.ObjectContext.RefreshEntities(RefreshMode refreshMode, IEnumerable collection)
       at System.Data.Objects.ObjectContext.Refresh(RefreshMode refreshMode, IEnumerable collection)
       at TaxReporting.Form2.btnSave_Click(Object sender, EventArgs e) in ...}


    Thursday, August 25, 2011 3:42 PM
  • Hi Brad,

    yes you have to refresh the collection, but you should do this so:

    tte.Refresh(RefreshMode.StoreWins, tte.Linkages)

    and then refresh the binding on the control you are binding the data to, e.g. call GridControl.RefreshDataSource on your grid

     

    you could also do this to get the updated collection:

    tte.CreateQuery<Linkage>(esqlQuery).Execute(MergeOption.OverwriteChanges).ToList();

     

    Hope this helps

    Regards


    Clarity VS Precision
    Thursday, August 25, 2011 6:26 PM
  • Hi Giorgi, I wanted to add something. Basically, I am able to add new entities successfully. I can see them in the database after I execute DataContext.SaveChanges(). I just need my BindingSource to re-read them.

    Hello,

    Based on my understanding, you would want to re-bind new context into controls. I made a test on my side. I think BindingSource wasn't the necessary one in this case. I just re-bind entity of context back to datagridview. It works. Please check it. Hope this helps.

      public partial class Form1 : Form
      {
        XueyunEntities2 ctx = new XueyunEntities2();
        public Form1()
        {
          InitializeComponent();
    
          dataGridView1.DataSource = ctx.Parents.ToList();
          
        }
    
        private void button1_Click(object sender, EventArgs e)
        {      
          //AddObject into current context
          Parent parent = new Parent
          {
            Price = 7,
            startDate = 9
          };
          ctx.Parents.AddObject(parent);
          ctx.SaveChanges();
    
          this.dataGridView1.DataSource = ctx.Parents;
        }
      }
    

    Thanks,


    Larcolais Gong[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, August 26, 2011 3:00 AM
  • Thanks for your suggestions. I was actually able to get the DataContext.Refresh() to work by removing one of the referenced entity properties (Parcel) from my query results. But I need to access properties from the other entities. I have three: Linkage, Parcel and TBM. Linkage has an FK that references TBM and an FK that references Parcel. It's in the middle.

    Parcel 0..1 --> * Linkage  //null FK values are allowed
    TBM 0..1 --> * Linkage  //null FK values are NOT allowed

    Before, I was trying this and the .Refresh() was crashing anytime I executed it:

    var linkages = from l in tte.Linkage
    where l.TBM.TBMID == tbmid
    select new
    {
    l.MatchID,
    l.Parcel.ParcelNo
    };
    bsl = new BindingSource();
    bsl.DataSource = linkages;
    bsl.AllowNew = true;

    this.bindingNavigator1.BindingSource = bsl;
    tte.Refresh(RefreshMode.ClientWins,bsl);     //crashed here

    If I retrieve only my Linkage entity it works:

    var linkages = from l in tte.Linkage where l.TBM.TBMID==tbmid select l;

    and I can access properties of the Linkage and TBM entities. However, the Parcel entity shows null even though I know a related record exists in the Parcel entity. So that seems to be what was crashing it (see error message above). I've checked the associations and they look fine to me. I don't know why Parcel entity is null.


    Friday, August 26, 2011 2:08 PM
  • So. you say linkages Parcel property is null, am I right? Did you check LazyLoadingEnabled option in your .edmx file's property window?

    And about refreshing the source, if you don't need to refresh entities in scope of the same context, you'd just rebind your grid to the same source again (like Larcolais said) you need to call ObjectContext.Refresh method if you want to get updates after the changes that occurred beyond the context.


    Clarity VS Precision
    Friday, August 26, 2011 8:22 PM
  • Yes it's null. I can't find anywhere to set the LazyLoadingEnabled property. Set it to true?

    I'm binding to a BindingNavigator, if that makes any difference. And I don't fully understand how to re-bind to it. There is no .Refresh() method on the BindingSource, and I haven't had any luck disposing of my current BindingSource and reattaching it to my BindingNavigator. It throws errors.

    Friday, August 26, 2011 8:35 PM
  •  

    //create entity key for Parcel navigation property
    IEnumerable<KeyValuePair<string, object>> entityKeyValues =new KeyValuePair<string, object>[] {
    new KeyValuePair<string, object>("ParcelNo", txtL_ParcelNo.Text),
    new KeyValuePair<string, object>("MBA", Decimal.Parse(txtL_FIPS.Text + "00000"))};
    EntityKey key = new EntityKey("TAXTESTEntities1.Parcel", entityKeyValues);

    //create entity key for TBM navigation property
    var T = (from t in tte.TBM where t.LineID == _lineid select new { t.OwnerSeqNo, t.AddrSeqNo }).First();
    IEnumerable<KeyValuePair<string, object>> TBMekv = new KeyValuePair<string, object>[] {
    new KeyValuePair<string, object>("TBMID",tbmid),
    new KeyValuePair<string, object>("AddrSeqNo", T.AddrSeqNo),
    new KeyValuePair<string, object>("OwnerSeqNo", T.OwnerSeqNo),
    new KeyValuePair<string, object>("LineID", _lineid)};


    //create new Linkage object
    Linkage lnk = new Linkage();
    lnk.ParcelReference.EntityKey = new EntityKey("TAXTESTEntities1.Parcel", entityKeyValues);
    lnk.TBMReference.EntityKey = new EntityKey("TAXTESTEntities1.TBM", TBMekv);

    //add linkage properties

    lnk.FIPS = Decimal.Parse(txtL_FIPS.Text);
    lnk.AccountNo = txtL_AcctNo.Text;
    lnk.ParentParcelCC = cboPPCC.SelectedText;
    lnk.dtCreate =DateTime.Now;
    lnk.dtLastUpdate =DateTime.Now;
    lnk.LastUpdatedBy = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
    lnk.OrigAddress = txtL_OrigAddress.Text;
    lnk.City = txtL_City.Text;l
    nk.State = txtL_State.Text;
    lnk.Zip = txtL_Zip.Text;
    lnk.Legal = txtL_Legal.Text;
    lnk.Comments = txtL_Comment.Text;

    tte.AddToLinkage(lnk);
    tte.SaveChanges();

    .SaveChanges() saves it in the database, but as soon as I execute this:

    tte.Refresh(System.Data.Objects.RefreshMode.ClientWins, bsl);   //bsl is my BindingSource for my BindingNavigator

    I get this error:

    "The element at index 24 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager".

    The last element in my list has a null value in the EntityKey property, whereas my other elements do not. But, if I expand the "base" property under ParcelReference in the variable watcher, I can dig down and find the values added when I created the entity key.




    Friday, August 26, 2011 9:15 PM
  • Hello again,

    From your post, did you mean "Comments" field was passed null into context? Your application was a winform app. In the web one, normally we can check if it want to operate Add or Update. Perhaps you can check stub technology here. http://stackoverflow.com/questions/4430236/update-exception-with-entity-framework

    Hope this helps.

    Thanks,


    Larcolais Gong[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Sunday, August 28, 2011 1:07 PM
  • Interesting!
    JP
    Sunday, August 28, 2011 1:59 PM
  • I finally fixed it with a workaround. I had to re-query the database after calling tte.SaveChanges() to get my new entity back with the reference properties populated. It seems like this should be built in functionality when using BindingSource.


    tte.SaveChanges();

    bsl.DataSource = from tlinthis.tte.Linkage.Include("Parcel").Include("TBM")where tl.TBM.TBMID == tbmid

    select tl;

    bindingNavigator1.BindingSource = bsl;

    • Marked as answer by BradInDallas Wednesday, August 31, 2011 2:10 PM
    Wednesday, August 31, 2011 2:10 PM