I'm using LINQ to SQL and C#. I have two LINQ classes: User and Network.
User has UserID (primary key) and NetworkID
Network has NetworkID (primary key) and an AdminID (a UserID)
The following code works fine:
user.Network.AdminID = 0;
db.SubmitChanges();
However, if I access the AdminID before making the change, the change never happens to the DB. So the following doesn't work:
if(user.Network.AdminID == user.UserID)
{
user.Network.AdminID = 0;
db.SubmitChanges();
}
It is making it into the if statement and calling submit changes. For some reason, the changes to AdminID never make it to the DB. No error thrown, the change just never 'takes'.
Any idea what could be causing this?
Thanks.
I just ran a quick test and it works fine for me.
I hate to ask this, but are you sure the if statement ever returns true? It could be you're just not hitting the code which changes the value.
Other than that we might need more info. What are the properties of that member? Have you traced into the set statement to ensure the value is getting set before calling SubmitChanges? Does the Linq entity have the new value after SubmitChanges? Or do both the database AND the Linq entity fail to take the new value?
In short, that code should work... so something else somewhere is probably wrong.
Here's the original post.
Here's a setter generated by the LinqToSql designer.
Code Snippet
{
Contact previousValue = this._Contact.Entity;
if (((previousValue != value)
|| (this._Contact.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Contact.Entity = null;
previousValue.ContactEvents.Remove(this);
}
this._Contact.Entity = value;
if ((value != null))
{
value.ContactEvents.Add(this);
this._ContactID = value.ID;
}
else
{
this._ContactID = default(int);
}
this.SendPropertyChanged("Contact");
}
}
This line sets the child's property to the parent.
this._Contact.Entity = value;
This line adds the child to the parent's collection
value.ContactEvents.Add(this);
The setter for the ID does not have this second line.
So, with the autogenerated entities...
This code produces an unexpected behavior:
myContactEvent.ContactID = myContact.ID;
This code is good:
myContactEvent.Contact = myContact;
This code is also good:
myContact.ContactEvents.Add(myContactEvent);
I had this issue. The reason was one dumb line of code:
DBDataContext db { get { return new DBDataContext(); } }
obviously it should be:
DBDataContext db = new DBDataContext();
Related
I am using entity framework with repository pattern and unit of work objects..
I have an entity Request with properties "RequestId", "OldRequestId", which can be accessed using requestRepository object.
eg: requestRepostiory.GetAll(), requestRepository.GetFiltered(r=> r.Requestid =10)
If I pass a RequestId, it should retrieve me the specific record.
If the OldRequestId is not null in the retrieved record, it should bring the old request data as well.
It should go on until the OldRequestId is null.
Simple way would be something like this:
public static IEnumerable<Data> GetRecursive(int id)
{
while (true)
{
var tmp = GetFiltered(x => x.Requestid == id);
yield return tmp;
if (tmp.OldRequestId.HasValue)
id = tmp.OldRequestId.Value;
else
yield break;
}
}
Please note, that this code would run make multiple queries towards the database. Performance won't be the best, but it might work for your scenario.
I need to add a record to a database using the Entity Framework. Since I'm brand new to using this syntax I am not sure how to properly write the code (Below is my best guess).
First, the agent must have their info inserted into the Agent table. This table produces a self-incrementing primary key known as a SymNumber. I then need to take that SymNumber and use it as a primary key for an insert into the AgentIdentification table.
I have run this code a couple of times, and I do not come up with an error, however since I am using a unit test to test the code I cannot tell for sure if the agent is being added properly. Secondly, I know for a fact that I am not correctly grabbing the SymNumber as generated by the Agent table after the first insert. The SymNumber is an int value in the Linq code set to 0, and this does not change during the AgentIdentification insert.
Any help would be greatly appreciated!
AgentResourcesEntities _db = new AgentResourcesEntities();
try
{
Agent agent = new Agent();
agent.EntityType = "P";
agent.FirstName = agentNewTraining.FirstName;
agent.LastName = agentNewTraining.LastName;
agent.LastChangeOperator = agentNewTraining.Requestor;
agent.LastChangeDate = DateTime.Now;
if (!String.IsNullOrEmpty(agentNewTraining.NameSuffix)) agent.NameSuffix = agentNewTraining.NameSuffix;
_db.Agent.AddObject(agent);
AgentIdentification agentIdentification = new AgentIdentification();
agentIdentification.SymNumber = agent.SymNumber;
agentIdentification.ReferenceType = "S";
agentIdentification.DummyReference = 0;
agentIdentification.LastChangeOperator = agentNewTraining.Requestor;
agentIdentification.LastChangeDate = DateTime.Now;
_db.AgentIdentification.AddObject(agentIdentification);
return true;
}
catch (Exception)
{
return false;
}
First you need to call
_db.SaveChanges();
to get your changed persisted.
But if you want also synchronize (get the new generated value) your agent.SymNumber you will need to call SaveChanges() right after adding it to context.
So the code will be like:
/// ...... ////
_db.Agent.AddObject(agent);
_db.SaveChanges();
AgentIdentification agentIdentification = new AgentIdentification();
agentIdentification.SymNumber = agent.SymNumber; // sym number is now synchronized from DB
///...../////
_db.AgentIdentification.AddObject(agentIdentification);
_db.SaveChanges();
But if SymNumber is foreign key so the AgentIdentification has could have reference to some Agent instance, you can just tie those instances with that reference and would not need to call that additional SaveChanges() in the middle.
Call _db.SaveChanges() after inserting.
I have the following test case
[TestMethod()]
[DeploymentItem("Courses.sdf")]
public void RemoveCourseConfirmedTest()
{
CoursesController_Accessor target = new CoursesController_Accessor();
int id = 50;
ActionResult actual;
CoursesDBContext db = target.db;
Course courseToDelete = db.Courses.Find(id);
List<CourseMeet> meets = courseToDelete.meets.ToList<CourseMeet>();
actual = target.RemoveCourseConfirmed(courseToDelete);
foreach (var meet in meets)
{
Assert.IsNull(db.Meets.find(meet));
}
Assert.IsNull(db.Courses.Find(courseToDelete.courseID));
}
Which tests the following method from my controller.
[HttpPost, ActionName("RemoveCourse")]
public ActionResult RemoveCourseConfirmed(Course course)
{
try
{
db.Entry(course).State = EntityState.Deleted;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException)
{
return RedirectToAction("RemoveMeet", new System.Web.Routing.RouteValueDictionary { { "concurrencyError", true } });
}
catch (DataException)
{
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again.");
return View(course);
}
}
I know i should be using a Mock db .... but for this project I have decided to go with this approach.
So this what happens. When I run the actual web site this function works perfectly fine and removes the course and all the meets that belong to it.
But when I run the test i get the following exception
System.InvalidOperationException: The operation failed: The relationship could not be
changed because one or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null value. If the
foreign-key does not support null values, a new relationship must be defined, the foreign-
key property must be assigned another non-null value, or the unrelated object must be
deleted.
Here is the even more interesting part if I comment out the following line from the test
List<CourseMeet> meets = courseToDelete.meets.ToList<CourseMeet>();
and replace the loop with the following:
foreach (var meet in db.Meets.ToList())
{
Assert.IsFalse(meet.courseID == courseToDelete.courseID);
}
I dont get any exceptions and the test case passess.
Am I missing something about Entity Framework or is this a bug?
Well this has been open for a while now. I still haven't been able to find a definite answer but working more with MVC and EF i think what is happening is that once i execute the line
List<CourseMeet> meets = courseToDelete.meets.ToList<CourseMeet>();
the meets get loaded into the object manager and hence when the parent object is deleted the no longer have a reference to the parent course.
I have code that generates records based on my DataGridView. These records are temporary because some of them already exist in the database.
Crop_Variety v = new Crop_Variety();
v.Type_ID = currentCropType.Type_ID;
v.Variety_ID = r.Cells[0].Value.ToString();
v.Description = r.Cells[1].Value.ToString();
v.Crop = currentCrop;
v.Crop_ID = currentCrop.Crop_ID;
Unfortunately in this little bit of code, because I say that v.Crop = currentCrop,
now currentCrop.Crop_Varieties includes this temporary record. And when I go to insert the records of this grid that are new, they have a reference to the same Crop record, and therefore these temporary records that do already exist in the database show up twice causing duplicate key errors when I submit.
I have a whole system for detecting what records need to be added and what need to be deleted based on what the user has done, but its getting gummed up by this relentless tracking of references.
Is there a way I can stop Linq-To-Sql from automatically adding these temporary records to its table collections?
I would suggest revisiting the code that populates DataGridView (grid) with records.
And then revisit the code that operates on items from a GridView, keeping in mind that you can grab bound item from a grid row using the following code:
public object GridSelectedItem
{
get
{
try
{
if (_grid == null || _grid.SelectedCells.Count < 1) return null;
DataGridViewCell cell = _grid.SelectedCells[0];
DataGridViewRow row = _grid.Rows[cell.RowIndex];
if (row.DataBoundItem == null) return null;
return row.DataBoundItem;
}
catch { }
return null;
}
}
It is also hard to understand the nature of Crop_Variety code that you have posted. As the Crop_Variety seems to be a subclass of Crop. This leads to problems when the Crop is not yet bound to database and potentially lead to problems when you're adding Crop_Variety to the context.
For this type of Form application I normally have List _dataList inside form class, then the main grid is bound to that list, through ObjectBindingList or another way. That way _dataList holds all data that needs to be persisted when needed (user clicked save).
When you assign an entity object reference you are creating a link between the two objects. Here you are doing that:
v.Crop = currentCrop;
There is only one way to avoid this: Modify the generated code or generate/write your own. I would never do this.
I think you will be better off by writing a custom DTO class instead of reusing the generated entities. I have done both approaches and I like the latter one far better.
Edit: Here is some sample generated code:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="RssFeed_RssFeedItem", Storage="_RssFeed", ThisKey="RssFeedID", OtherKey="ID", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
public RssFeed RssFeed
{
get
{
return this._RssFeed.Entity;
}
set
{
RssFeed previousValue = this._RssFeed.Entity;
if (((previousValue != value)
|| (this._RssFeed.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._RssFeed.Entity = null;
previousValue.RssFeedItems.Remove(this);
}
this._RssFeed.Entity = value;
if ((value != null))
{
value.RssFeedItems.Add(this);
this._RssFeedID = value.ID;
}
else
{
this._RssFeedID = default(int);
}
this.SendPropertyChanged("RssFeed");
}
}
}
As you can see the generated code is establishing the link by saying "value.RssFeedItems.Add(this);".
In case you have many entities for wich you would need many DTOs you could code-generate the DTO classes by using reflection.
I have a couple of tables with similar relationship structure to the standard Order, OrderLine tables.
When creating a data context, it gives the Order class an OrderLines property that should be populated with OrderLine objects for that particular Order object.
Sure, by default it will delay load the stuff in the OrderLine property but that should be fairly transparent right?
Ok, here is the problem I have: I'm getting an empty list when I go MyOrder.OrderLines but when I go myDataContext.OrderLines.Where(line => line.OrderId == 1) I get the right list.
public void B()
{
var dbContext = new Adis.CA.Repository.Database.CaDataContext(
"<connectionString>");
dbContext.Connection.Open();
dbContext.Transaction = dbContext.Connection.BeginTransaction();
try
{
//!!!Edit: Imortant to note that the order with orderID=1 already exists
//!!!in the database
//just add some new order lines to make sure there are some
var NewOrderLines = new List<OrderLines>()
{
new OrderLine() { OrderID=1, LineID=300 },
new OrderLine() { OrderID=1, LineID=301 },
new OrderLine() { OrderID=1, LineID=302 },
new OrderLine() { OrderID=1, LineID=303 }
};
dbContext.OrderLines.InsertAllOnSubmit(NewOrderLines);
dbContext.SubmitChanges();
//this will give me the 4 rows I just inserted
var orderLinesDirect = dbContext.OrderLines
.Where(orderLine => orderLine.OrderID == 1);
var order = dbContext.Orders.Where(order => order.OrderID == 1);
//this will be an empty list
var orderLinesThroughOrder = order.OrderLines;
}
catch (System.Data.SqlClient.SqlException e)
{
dbContext.Transaction.Rollback();
throw;
}
finally
{
dbContext.Transaction.Rollback();
dbContext.Dispose();
dbContext = null;
}
}
So as far as I can see, I'm not doing anything particularly strange but I would think that orderLinesDirect and orderLinesThroughOrder would give me the same result set.
Can anyone tell me why it doesn't?
You're just adding OrderLines; not any actual Orders. So the Where on dbContext.Orders returns an empty list.
How you can still find the property OrderLines on order I don't understand, so I may be goofing up here.
[Edit]
Could you update the example to show actual types, especially of the order variable? Imo, it shoud be an IQueryable<Order>, but it's strange that you can .OrderLines into that. Try adding a First() or FirstOrDefault() after the Where.