I use the following code to insert a new record to my Users table:
public bool CreateUser(User obj)
{
obj.Id = Guid.NewGuid();
using (_db = new CMSDataContext())
{
obj.SiteId = SiteID;
_db.Users.InsertOnSubmit(obj);
_db.SubmitChanges();
}
return true;
}
I do not get any errors, and everything seems fine. I can read a record from database with same DataContext. But after the above method runs completely, I see nothing new in my Users table. Why?
Is the id column truly a PK in the sql server database?
Related
I have a GiftCards table in my DBML that has a related property called Audit. The Audits are stored in a separate table. Each Audit has a related Person associated to it. There is also a Persons table. The relationships are set up and are valid in my DBML.
The problem is that when I instantiate a new Gift Card I also create a new related Audit in the OnCreated() method. But at the same time, I also create a related Person when I instantiate a new Audit. The Person is the current user. Actually the Audit's OnCreated method checks if the user already exists.
The problem is that when I instantiate a new gift Card, it also creates an associated Audit, which is fine, and the Audit creates an associated Person. But the Person already exists in the database. When I look at the data context's GetChangeSet(), it shows 3 inserts. The Persion should not show as an insert because he already exists in the database.
Here is how I implemented this. It is an MVC application where the Controller receives a gift card:
[HttpPost]
public ActionResult Save(GiftCardViewModel giftCard)
{
if (ModelState.IsValid)
{
GiftCard gc = GiftCardViewModel.Build(giftCard);
repository.InsertOrUpdate(gc);
repository.Save();
return View("Consult", new GiftCardViewModel(repository.Find(gc.GiftCardID)));
}
else
SetupContext();
return View("_Form", giftCard);
}
The Gift Card has:
partial class GiftCard
{
partial void OnCreated()
{
// Set up default audit.
this.Audit = new Audit();
}
}
The Audit class has:
partial void OnCreated()
{
// Setup timestamp
this.Timestamp = DateTime.Now;
this.Person = Person.GetPerson(Membership.GetUser().UserName);
}
And finally, my Person class has:
public static Person GetPerson(String username)
{
using (GiftCardDBDataContext database = new GiftCardDBDataContext())
{
// Try to get the person from database
Person person = database.Persons.SingleOrDefault(personData => SqlMethods.Like(personData.Username, username));
if (person == null)
{
person = new Person()
{
Username = username,
FullName = "Full name TBD"
};
database.Persons.InsertOnSubmit(person);
database.SubmitChanges();
}
// Return person data
return person;
}
}
When I create a new gift card, I always get an error saying that it's attempting to insert a duplicate person in the Persons table. I don't understand because my static class specifically checks if the Person already exists, if yes, I return the Person and I don't create a new one. Yet, the GetChangeSet() shows three inserts including the Person, which is wrong.
What am I doing wrong here?
I believe your issue here is that you're using multiple contexts. You have one being created by your repository, and another is created in the static method on your Person object. You also aren't making any effort to attach the Person created/retrieved from the other context to the context of your Audit class.
You should look at a single unit of work, a single DataContext class, and perform all your work in that.
I am using linq to sql and trying to insert new objects. Here's an example of my code:
public class Farm(){
public List<FarmAnimals> FarmAnimals ();
public string FarmName;
}
Public class FarmAnimal(){
public string name;
}
public void Insert(FarmModel farm)
{
using (var context = new FarmDataClassesDataContext())
{
context.Farms.InsertOnSubmit(new Farm { FarmName = farm.FarmName });
foreach (var animal in farm.FarmAnimals)
{
context.Responses.InsertOnSubmit(new FarmAnimal {name = animal.name, farmID = farm.Id });
}
context.SubmitChanges();
}
}
I get a FK constraint error when it tries to insert a farm animal, referencing the farmID (which equals 0). Since the farm hasn't been inserted yet, it doesn't have an ID for the farmanimals to refer to. How do I get the farm submitted so that the farm animals FK can be properly set?
Thanks,
The problem is you are thinking SQL way, and not ORM way.
The SQL way assigns a foreign key:
InsertOnSubmit(new FarmAnimal {name = animal.name, farmID = farm.Id });
The ORM way assigns entities. Notice the part between ** ** in the following code sample.
var myFarm = new Farm { FarmName = farm.FarmName };
Con...InsertOnSubmit(myFarm)
Con...InsertOnSubmit(new FarmAnimal {name = animal.name, **farm = myFarm**});
Because you assign the entity, proper insertions will be handled and as a bonus in one transaction.
You have to submitChanges before inserting the FarmAnimals, and you need to have the column auto creating the key with autoincrement. Also make sure that the column in the table object in the DBML-file auto updated on insert.
public class Farm(){
public List<FarmAnimals> FarmAnimals ();
public string FarmName;
}
Public class FarmAnimal(){
public string name;
}
public void Insert(FarmModel farm)
{
using (var context = new FarmDataClassesDataContext())
{
Farm newFarm = new Farm { FarmName = farm.FarmName }; <--- New
context.Farms.InsertOnSubmit(newFarm); <---Edited
context.SubmitChanges(); <--- New
foreach (var animal in farm.FarmAnimals)
{
context.Responses.InsertOnSubmit(new FarmAnimal {name = animal.name, farmID = newFarm.Id }); <--- Edited
}
context.SubmitChanges();
}
}
To expand on Pleun's answer: You need to assign entities rather than IDs. The property that you're trying to assign to is mapped to a column with a foreign-key constraint, so it won't work for assigning an entity--to do that you instead need a property that maps to the relationship between two tables. How you do that varies by the tool you're using.
For the purposes of this explanation, I'll assume that you have a Farm table with a primary-key column called ID and another column called Name; and a FarmAnimal table with a foreign-key column named FarmFK that points to the Farm table and another column called Name.
Based on the DataContext part of the name I assume you're using the O/R Designer tool built in to Visual Studio, right? If so, go to the O/R Designer by opening your dbml file, select the association (represented as an arrow) between Farm and FarmAnimal (if there's not already an arrow, select the Association tool from the Toolbox and drag from Farm to FarmAnimal), and view the association's properties. You'll see properties called "Child Property" and "Parent Property". (The parent table is the table with the primary key in the relationship.) Expand those to see the "Name" sub-property of each. Those are the property names you'd use in code to access the two ends of the relationship. Typically they have poorly-chosen names based on the automatic generation, so rename them as needed. In this case let's rename the parent property's name to Animals and the child property's name to 'Farm'. You'd then be able to do the following in your code:
public void Insert(FarmModel farmModel)
{
using (var context = new FarmDataClassesDataContext())
{
var farm = new Farm
{
Name = farmModel.FarmName
};
context.Farms.InsertOnSubmit(farm);
foreach (var animalModel in farmModel.FarmAnimals)
{
var critter = new FarmAnimal
{
Name = animalModel.name,
Farm = farm
}
context.Responses.InsertOnSubmit(critter);
}
context.SubmitChanges();
}
}
Does that answer your need?
I have this going me crazy,
I'm attaching a List with 1 Customer and 1 Address child record row.
Everything seems OK while debugging. 1 customer Row and 1 Address Row should inserted.
But instead I get 2 Customer Records and 1 Address Row.
I don't know why. When Attaching and looping inside the List only 1 record seen.
Any points?
[EDITED]
Code Attached:
public bool InsertUpdateCustomers(List<Customer> customerList, List<Customer> originalCustomers)
{
using (DbContext db = new DbContext(DbContext.ConnectionString))
{
db.Log = Console.Out;
List<Customer> customerCloned = new List<Customer>();
customerList.ForEach(p => customerCloned.Add(p.CloneObjectGraph()));
customerCloned.ForEach(p => p.Address =
customerList.Where(pe => pe.Id == p.Id).Single().Address.CloneObjectGraph());
customerCloned.ForEach(p =>
{
if (p.Id > 0)
{
db.Customer.Attach(p,
originalCustomers.Single(
x => x.Id == p.Id));
db.Address.Attach(p.Address,
originalCustomers.Single(
x => p.AddressId== x.AddressId).
Address);
}
});
customerCloned.ForEach(p =>
{
if (p.Id == 0)
db.Customer.InsertOnSubmit(p);
});
try
{
db.SubmitChanges(ConflictMode.ContinueOnConflict);
return true;
}
catch (Exception ex)
{
return false;
}
}
}
I have checked the Log in the output and I see indeed 2 Inserts in the table.
I don't see nothing about the Address, but inserts correctly.
It could be the foreign key problem i don't get it.
I guess you've solved this for now but I ran into a similar issue and wanted to report back my understanding of this issue for future users.
The issue, I believe, is that you are using an existing list of Customer objects retrieved from the DB using a particular DataContext. You are then creating a new DataContext in your method and with this new DataContext, you are attaching an Address object.
This Address object (assuming has a foreign key relation with Customer) creates a new Customer object in the DB since the DataContext for which SubmitChanges is called, the originalCustomer is also treated as a new record.
In other words, to avoid these problems, you must re-use the existing DataContext using which the originalCustomer List was fetched so that inserting the child record of Address doesn't trigger an entry into the parent table.
Hope this helps.
Its a simple question, but I'm not aware of the answer and I couldn't get it to work.
Can I update only one entity on the entire DataContext? Or should I follow plain ADO.NET for this operation only?
Edit:
public MyObject GetMyObjectById(int selectedId)
{
DataContext db = _dbManager.GetContext();
return db.MyObject.SingleOrDefault(p => p.Id == selectedId);
}
I am getting an object with the above query...
I am querying then for an integer...on another table/object
public int GetMyInteger()
{
DataContext db = _dbManager.GetContext();
return db.MyAnotherObject.FirstOrDefault().MyInteger;
}
Everything is fine for all my operations...but now i just want to update only the integer i got from the database...
public void SetMyInteger(int updInteger)
{
DataContext db = new DataContext(ConnectionString);
MyAnotherObject theEntity = db.MyAnotherObject.FirstOrDefault();
atheEntity.MyInteger = updInteger;
db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
The above method deleted MyObject i got from the first query!!! Of course if i use the static context DataContext tries to update MyObject and MyAnotherObject which seems the correct behaviour.
Edit:
I have changed the method getting the integer with a new datacontext as well and seems to working correctly, i have a strange thought on why called the delete method, because it was the method that was called, but again .. is working now...
Thank you all for your time.
Yes it's possible. What have you tried? It should be as simple as this:
using (var dc = new YourDataContext())
{
Person p = dc.Persons.Take(1).Single();
p.FirstName = "Ahmad";
dc.SubmitChanges();
}
Yes, you can:
Foo foo = dc.Foos.Where(foo => foo.Id == 345).Single();
foo.Name = "foo";
dc.SubmitChanges();
This one i had today is a strange one.
I have this query in an assembly method.
public Order[] SelectAllOrders()
{
Order[] orders;
using (MyDataContext context = new MyDataContext())
{
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(order => order.OrderDetails);
context.LoadOptions = dlo;
orders = context.Orders.Select(p => p).ToArray();
}
return orders;
}
Supposed i already called the ToArray() the SQL Command executed and gave me the objects i need and i give them to a new Order[] array this should not need the DataContext instance.
While im serializing the Order[] i get from the method return, serializer tries to access the DataContext again and i get an exception that cannot access disposed object.
Tried without the using() statement and works like it should. But, why i get this behavior?
Anyone could give an explanation why deferred loading still remains while I'm calling .ToArray() and assigning new variable with the contents?
The Select(p=>p) achieves very little; you might as well just call:
orders = context.Orders.ToArray();
Re the problem - I would guess that either OrderDetails hasn't really loaded, or it is trying to load some other data lazily. I would suggest investigating by (in a dev session):
Order[] orders;
using (MyDataContext context = new MyDataContext())
{
context.Log = Console.Out; // show me
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(order => order.OrderDetails);
context.LoadOptions = dlo;
Console.WriteLine("> Calling ToArray");
orders = context.Orders.ToArray();
Console.WriteLine("> ToArray complete");
// TODO: your extra code that causes serialziation, probably
// involving `DataContractSerializer`
Console.WriteLine("> Calling Dispose");
}
With this, you should be able to see any extra database trips that are happning after the ToArray but before the Dispose(). The point being: this data is needed for serialization, so either a: ensure it gets loaded, or b: exclude it from serialization.