Here is the code I'm using to create a new Student.
public class StudentRepository
{
SchoolEntities db = new SchoolEntities();
public IQueryable<Student> FindAllStudents()
{
return db.Students;
}
public Student FindStudent(int id)
{
return db.Students.SingleOrDefault(c => c.ID == id);
}
public void Add(Student Student)
{
db.AddToStudents(Student);
}
public void Save()
{
db.SaveChanges();
}
}
Here's how I'm using it:
private void SaveInformation()
{
Student student = new Student();
Int64 gradeId = Convert.ToInt64(cmbGradeParalelo.SelectedValue);
student.IDGrade = gradeId;
student.RUDE = Convert.ToInt64(txtRude.Text);
/*Parents information.*/
student.FatherName = txtNombrePadre.Text;
student.FatherProfession = txtProfesionPadre.Text;
student.MobilePhoneFather = FormatPhoneNumber(txtCelularPadre.Text);
student.PlaceofWorkFather = txtLugarDeTrabajoPadre.Text;
student.MotherName = txtNombreMadre.Text;
student.MotherProfession = txtProfesionMadre.Text;
student.MobilePhoneMother = FormatPhoneNumber(txtCelularMadre.Text);
student.PlaceofWorkMother = txtLugarDeTrabajoMadre.Text;
/*Student information*/
student.Name = txtNombre.Text;
student.FatherLastName = txtApellidoPaterno.Text;
student.MotherLasteName = txtApellidoMaterno.Text;
student.DateOfBirth = dtpFechaNacimiento.Value.ToShortDateString();
student.PlaceOfBirth = txtLugarNacimiento.Text;
student.Sex = sexoMasculino.Checked ? sexoMasculino.Text : sexoFemenino.Text;
student.Telephone = FormatPhoneNumber(txtTelefono.Text);
student.MobilePhone = FormatPhoneNumber(txtCelular.Text);
student.Address = txtDireccion.Text;
student.Carnet = FormatPhoneNumber(txtCarnet.Text);
student.Observations = txtObservaciones.Text;
StudentRepository repo = new StudentRepository();
repo.Add(student);
repo.Save();
MessageBox.Show("Se guardo el registro exitosamente.",
"Exito!",
MessageBoxButtons.OK,
MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1);
ClearForm();
}
The problem is when I load the information on a form, and hit the save again, a new Student is created. I'd like to modify the students information.
Any suggestions?
The easiest solution would be to get the Student first, then make the changes.
var existingStudent = StudentRepository.FindStudent(1); // object now in EF graph
student.RUDE = Convert.ToInt64(txtRude.Text); // object now set to EntityState.Modified
// other fields
repo.Save(); // object saved to database
On a side note, don't do this:
db.AddToStudents(Student);
The recommended way in EF4 is:
db.Students.AddObject(Student);
By the looks of the code, i'm guessing this is either a WinForms or WPF app? Maybe your better of using a control with EntityDataSource, as opposed to the painstaking task of manually updating the model.
You're creating a new Student instance yourself whenever you click save. That's correct if you want to create a new Student record, but if you want to edit an existing record, you will have to modify a Student record that you've retrieved from the database instead.
A better OO approach would be to have the calling form pass the Student object to your student editor form. This way, the calling form would instantiate a new Student object itself if creating a new one, then if the DialogResult of the form is OK, add it to the context and save it. In the case of opening an existing record, the calling form would pass the existing Student record to the editor, then call Save() once it closes.
On an unrelated note, you really need to Dispose of the context once you're done with it.
Related
As a requirement I cannot use the early bound context created with "CrmSvcUtil". The problem is that a new phonecall activity expects two fields ('from' and 'to') which are Entities of type activityparty. The standard XRM/CRM namespace does not contain a class similar to ActivityParty created with the Utility.
I tried filling it with an EntityCollection but then the field will be empty. Next I tried to recreate the structure of a working phonecall activity. EntityCollection "activityparty" -> with one Entity "activityparty" -> with EntityReference attribute "partyid" -> the entity ref (e.g. "contact" and the contact's id). But it simply does not work.
How can I create an ActivityParty (or better a phonecall Activity) with the "normal" Entitiy classes?
If I'm right you don't need to use an EntityCollection but an array of Entity
To create a phone call with late bound syntax will be:
Entity from1 = new Entity("activityparty");
Entity to1 = new Entity("activityparty");
Entity to2 = new Entity("activityparty"); // two contacts inside the to field
from1["partyid"]= new EntityReference("systemuser", userId);
to1["partyid"]= new EntityReference("contact", contact1Id);
to2["partyid"]= new EntityReference("contact", contact2Id);
Entity phonecall = new Entity("phonecall");
phonecall["from"] = new Entity[] { from1 };
phonecall["to"] = new Entity[] { to1, to2 };
// other phonecall fields
Guid phonecallId = service.Create(phonecall);
Even though I upvoted the answer but I had simmilar problem with serialization of ActivityParty. I came to solution that doesn't require you to give up on early bound entities.
what you need to do is something like this:
IEnumerable<ActivityParty> party = new [] { new ActivityParty { PartyId="", EntityLogicalName="..." } };
phonecall["to"] = new EntityCollection(party.Select(x => x.ToEntity<Entity>).ToList());
(I didn't test the code and wrote it from the air but you should feel the idea)
I vote for TrN because i was looking for any kind of example, and it's the only early bound example that i could find.
His example Actually helped me create an PhoneCall entity that had the Attribute "From" pointing to the Lead that actually made the call. I never fully understood the IEnumerable<ActivityParty> enumerator. Thanks to TrN i understand it enough to use it.
Here is my code regarding the PhoneCall activity that I've tested and it works. Everytime an existing Lead calls. The PhoneCall activity gets saved with the correct Attribute values linked to the correct Lead.
IEnumerable<ActivityParty> party = new[] { new ActivityParty { LogicalName = ActivityParty.EntityLogicalName , PartyId = eref2 } };
Console.WriteLine("Logging activity to {0}", firstName);
Console.WriteLine("... \n" );
PhoneCall newCall = new PhoneCall { Description = "Missed phone call from this lead", DirectionCode = true, RegardingObjectId = eref2,
Subject = "Missed Call", PhoneNumber = MissedCall, OwnerId = User, From = party };
Guid newCallId = service.Create(newCall);
Console.WriteLine("Log successfully created \n \n ");
As i said, For Kirschi this isnt the real solution given his requirement of not having any context. But anyone who wants/can use provided context and is curious how the IEnumerable<ActivityParty> works, this might help them to create a proper PhoneCall Activity.
Here is working code for the same. Feel free to reach out if anyone faces any issue.
private static void fetchRelatedPhoneCalls(IPluginExecutionContext context, IOrganizationService service, Guid yourGuid, Entity opp)
{
string strFetchPhoneCalls = string.Format(FetchQuery.bringFetchQueryForPhoneCalls(),yourGuid);
EntityCollection entPhoneCalls = (EntityCollection)service.RetrieveMultiple(new FetchExpression(strFetchPhoneCalls));
if (entPhoneCalls != null && entPhoneCalls.Entities.Count > 0)
{
for (int i = 0; i < entPhoneCalls.Entities.Count; i++)
{
Entity entPhoneCall = (Entity)entPhoneCalls.Entities[i];
string[] strAttributesPCtoRemove = new string[] { "createdon", "createdbyname", "createdby"
,"modifiedon", "modifiedby" ,"regardingobjectid","owninguser"
,"activityid", "instancetypecode", "activitytypecode" // PhoneCall Skip
};
Entity entNewPhoneCall = this.CloneRecordForEntity("phonecall", entPhoneCall, strAttributesPCtoRemove);
entNewPhoneCall["regardingobjectid"] = new EntityReference(context.PrimaryEntityName, context.PrimaryEntityId);
entNewPhoneCall["to"] = this.getActivityObject(entNewPhoneCall, "to");
entNewPhoneCall["from"] = this.getActivityObject(entNewPhoneCall, "from");
service.Create(entNewPhoneCall);
}
}
}
private static Entity CloneRecordForEntity(string targetEntityName, Entity sourceEntity, string[] strAttributestoRemove)
{
Entity clonedEntity = new Entity(targetEntityName);
AttributeCollection attributeKeys = sourceEntity.Attributes;
foreach (string key in attributeKeys.Keys)
{
if (Array.IndexOf(strAttributestoRemove, key) == -1)
{
if (!clonedEntity.Contains(key))
{
clonedEntity[key] = sourceEntity[key];
}
}
}
return clonedEntity;
}
private static EntityCollection getActivityObject(Entity entNewActivity, string activityFieldName)
{
Entity partyToFrom = new Entity("activityparty");
partyToFrom["partyid"] = ((EntityReference)((EntityCollection)entNewActivity[activityFieldName]).Entities[0].Attributes["partyid"]);
EntityCollection toFrom = new EntityCollection();
toFrom.Entities.Add(partyToFrom);
return toFrom;
}
Good day, stackoverflow.
My question is: when the entity from DataContextModel is instantiated somewhere in the code, does it already have references to the database record? Or can it be used as common class ? For example:
public void SomeMethod()
{
var FirstEntity = new DBEntity(); //DBEntity is some entity from the database
var SecondEntity = new DBEntity();
var ThirdEntity = new DBEntity();
DbSet<DBEntity>.Add(SecondEntity);
DbSet<DBEntity>.Add(ThirdEntity);
DbContext.SaveChanges();
}
So, will FirstEntity be affected somehow, or it will be removed, when the SomeMethod exits?
Thanks in advance.
It's just a plain class if outside the context.
But be careful, suppose this FirstEntity is referenced inside another object, as in:
var FirstEntity = new DBEntity();
var SecondEntity = new DBEntity();
var ThirdEntity = new DBEntity();
DbSet<DBEntity>.Add(SecondEntity);
DbSet<DBEntity>.Add(ThirdEntity);
SecondEntity.Sibling = FirstEntity;
When you save changes, if Sibling in the example is a foreign key reference, it will automatically add FirstEntity to the database.
Another example to be clear:
var Computer = new ComputerEntity();
var Motherboard = new MotherboardEntity();
Computer.Motherboard = Motherboard;
DbSet<ComputerEntity>.Add(Computer);
DbContext.SaveChanges();
This will save both Computer and Motherboard to the DB.
Im wanting to use Entity Framework POCO in a disconnected (from context) mode. In my scenario I'm creating a new Parent object and want to attach an existing child object to it and then save it to the db.
The code below undesirably inserts a new Course record when saving a new Student record, when instead I want the existing Course record linked to the new Student record.
How can I do this in Entity Framework where...
the objects can be disconnected from the context. (i.e. Queried in one context and then saved in a different context)
I dont need to re-query the child record from the DB just so I can attach it to the parent when I'm saving to db. I really want to avoid doing extra trips to the db when I already have it as an object in memory.
This page shows a database diagram that the code below is based on http://entityframeworktutorial.net/EF4_EnvSetup.aspx#.UPMZ4m-UN9Y
class Program
{
static void Main(string[] args)
{
//get existing course from db as disconnected object
var course = Program.getCourse();
//create new student
var stud = new Student();
stud.StudentName = "bob";
//assign existing course to the student
stud.Courses.Add(course);
//save student to db
using (SchoolDBEntities ctx = new SchoolDBEntities())
{
ctx.Students.AddObject(stud);
ctx.SaveChanges();
}
}
static Course getCourse()
{
Course returnCourse = null;
using (var ctx = new SchoolDBEntities())
{
ctx.ContextOptions.LazyLoadingEnabled = false;
returnCourse = (from s in ctx.Courses
select s).SingleOrDefault();
}
return returnCourse;
}
}
I believe there are few ways of accomplishing this.
You can specify that course entity is unchanged rather than added, along these lines:
ctx.Entry(course).State = EntityState.Unchanged;
Or instruct your context, that you are working with existing entity:
ctx.Courses.Attach(course);
More info here:
http://msdn.microsoft.com/en-us/data/jj592676.aspx
EDIT
There are some running samples from my solution, I verified they work as expected.
In all cases we have Publisher record in database with ID = 2 and Name = "Addison Wesley" (irrelevant to the example, but just for good measure).
Approach 1 - Setting Entity State
using (var context = new Context())
{
var book = new Book();
book.Name = "Service Design Patterns";
book.Publisher = new Publisher() {Id = 2 }; // Only ID is required
context.Entry(book.Publisher).State = EntityState.Unchanged;
context.Books.Add(book);
context.SaveChanges();
}
Approach 2 - Using Attach method
using (var context = new Context())
{
var book = new Book();
book.Name = "Service Design Patterns";
book.Publisher = new Publisher() { Id = 2 }; // Only ID is required
context.Publishers.Attach(book.Publisher);
context.Books.Add(book);
context.SaveChanges();
}
Approach 3 - Setting Foreign Key value
using (var context = new Context())
{
var book = new Book();
book.Name = "Service Design Patterns";
book.PublisherId = 2;
context.Books.Add(book);
context.SaveChanges();
}
For this last approach to work I needed to add extra property PublisherId, it has to be named according to NavigationPropertyName + 'Id" convention to be picked up by EF auotmatically:
public int PublisherId { get; set; }
public Publisher Publisher { get; set; }
I am using here EF5 Code First, but it is very similar to POCO.
Entity Framework does not allow relationships that cross contexts.
If you place the reading of the course and connecting the course to the student within the same using statement, it would work.
I also tried the second option it worked for me. I did like the parent->child relationship happening at an object level first and save to db. Maybe I should just remove all the relationships between the entities that EF generates and manually control this myself.
It is necessary that after the creation of records in the table "Clients" took up ID. Later ID used to create a new entry in the "Clients_details".
var user = GetUsers();
var userdet = GetclientsDetails();
string hashedpass = getMd5Hash(UIPassword.Text);
var newreg = new Clients
{
login = UILogin.Text,
password = hashedpass,
subscribeid = Convert.ToInt32(UIId.Text)
};
user.InsertOnSubmit(newreg);
user.Context.SubmitChanges();
var details = new Clients_details
{
city = UICity.Text,
first_name = UIFirst_name.Text,
last_name = UIFamiliya.Text,
name = UIName.Text,
Clients = newreg
};
userdet.InsertOnSubmit(details);
userdet.Context.SubmitChanges();
After this code fails:
"An attempt was made to perform an operation Attach or Add in relation to an object that is not new, and possibly loaded from another DataContext. This operation is not supported."
How to properly create a record that does not appear a mistake? Thank you!
private static Table<Clients> GetUsers()
{
var dce = new BaseDBMLDataContext();
return dce.Clients;
}
private static Table<Clients_details> GetclientsDetails()
{
var dce = new BaseDBMLDataContext();
return dce.Clients_details;
}
Looks like userdet.Context and user.Context was built using a different dataContext and that needs to be created using the same dataContext rather than instantiating a new one.
I think you need to only call the SubmitChanges only once in the end, and also you need to make sure the user and userdet you are using share the same context
As the error clearly states, you're using different contexts (user and userdet) for each entity to add. You should have one DataContext and use that one to add the entities.
Yes looks like you're using two different instances of the same context:
user.Context.SubmitChanges();
userdet.Context.SubmitChanges();
A good approach to build up your entities should be something like :
//Create your client details entity
var details = new Clients_details
{
city = UICity.Text,
first_name = UIFirst_name.Text,
last_name = UIFamiliya.Text,
name = UIName.Text
};
//Create your client entity
var newreg = new Clients
{
login = UILogin.Text,
password = hashedpass,
subscribeid = Convert.ToInt32(UIId.Text),
//Assigning the details entity (FK) to the client
ClientDetails = details
};
//Saving both the client and its details
user.InsertOnSubmit(newreg);
user.Context.SubmitChanges();
So, here is my hopefully unique spin on this common problem.
I do my query, get my objects then pass the object into a form where it populates the form with the data from the object (this is not passed in by reference).
I then edit the values of the object that was queried (via the form) and then return a new object constructed from the values in the form.
I then want to update this to the database. Attach does nothing (runs but does not update). SubmitChanges also does nothing (and both do nothing when used together).
What am I missing?
Update: here is the code I am using:
// In constructor
_dataMap = new DataMapDataContext();
_addresses = _dataMap.AddressItems
.Where(address => address.InsertUserName == _currentUser.Name).ToList();
public void EditButtonClick()
{
using (AddAddressForm form = new AddAddressForm(_addresses[_currentAddress]))
{
form.Text = "Edit Address";
if (DialogResult.OK == form.ShowDialog())
{
_addresses[_currentAddress] = form.Item;
_dataMap.SubmitChanges();
DisplayItem();
}
}
}
You'll need to get the record from the database, update it's values and then call SubmitChanges()
using(MyDataContext db = new MyDataContext())
{
// get the record
Product dbProduct = db.Products.Single(p => p.ID == 1);
// set new values
dbProduct.Quantity = 5;
dbProduct.IsAvailable = false;
// save them back to the database
db.SubmitChanges();
}
Turns out I was doing almost everything right.
I just needed to pass in the object I was editing by reference. That way when it got changed, it was not a new object that was returned, but the same one (that Linq-to-SQL already knew about.)
These are the two lines from the code above that got changed:
AddressItem itemToEdit = _addresses[_currentAddress];
using (AddAddressForm form = new AddAddressForm(ref itemToEdit))