Cascade save on OrientDB Document API when using embedded types - orientdb

Given the following test:
// setup
OClass driver = getDatabase().getMetadata().getSchema().createClass(DRIVER);
OClass car = getDatabase().getMetadata().getSchema().createClass(CAR);
car.createProperty(DRIVERS, OType.EMBEDDEDLIST, driver);
OClass team = getDatabase().getMetadata().getSchema().createClass(TEAM);
team.createProperty(CARS, OType.EMBEDDEDSET, car);
// exercise
ODocument alonso = new ODocument(DRIVER).field("name", "Fernando Alonso").field("nationality", "Spanish")
.field("yearOfBirth", 1981);
ODocument button = new ODocument(DRIVER).field("name", "Jenson Button").field("nationality", "british")
.field("yearOfBirth", 1980);
ODocument mp30 = new ODocument(CAR).field(DRIVERS, Arrays.asList(new ODocument[] { alonso, button }));
Set<ODocument> cars = new HashSet<>();
cars.add(mp30);
ODocument mclarenF1Team = new ODocument(TEAM).field(CARS, cars);
mclarenF1Team.save();
// verify
assertEquals(1, getDatabase().countClass(TEAM));
assertEquals(1, getDatabase().countClass(CAR));
assertEquals(2, getDatabase().countClass(DRIVER));
The second assertion fails:
java.lang.AssertionError: expected:<1> but was:<0>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)
at foo.orientdb.dataaccessapi.StoreJSonIT.testSchemaFull(StoreJSonIT.java:68)
Why does it fail?
The properties CAR and DRIVER are created as embedded list and embedded set, shouldn't a single save in mclarenF1Team do a cascade save for the embedded documents?

Embedded List/Set means that the documents you create will be embedded (Saved) in the parent document and not in his own class/cluster.
IF you want to achieve that behaviour you should use links
See here
http://orientdb.com/docs/2.1/Concepts.html#relationships

Related

OrientDB - exception when I try to save an embeddedlist

I have a class named CalculationFunctionGroup where I have an attribute like this:
List<CalculationFunction> functions;
on my OrientDB I have a table named CalculationFunctionGroup with a property functions definaed as EMBEDDEDLIST and linked class CalculationFunction.
When I try to save an object of type CalculationFunctionGroup an exception raises.
The exception tell me:
The field 'functions' has been declared as EMBEDDEDLIST with linked class 'CalculationFunction' but the record has no class.
I try to find this exception in OrientDB source code, and I find this:
There's a check in ODocument class in the method validateEmbedded where there are these code lines:
if (doc.getImmutableSchemaClass() == null)
throw new OValidationException("The field '" + p.getFullName() + "' has been declared as " + p.getType()
+ " with linked class '" + embeddedClass + "' but the record has no class");
So, I don't understand how can I valued the immutableschemaclass property.
Where I try to set my field value from Java, I use this command line:
this.data.field(fieldName, value, OType.EMBEDDEDLIST);
where data is my ODocument instance, fieldName is functions, value is my List of CalculationFunction and OType is EMBEDDEDLIST.
Used Orient version is 2.2.0
EDIT #1
I try this after Alessandro Rota answer, but the error is the same:
ODocument myEntity = new ODocument("CalculationFunctionGroup");
myEntity.field("referenceItem", object.getReferenceItem().getData());
db.save(myEntity);
db.commit();
In this code snippet I've changed the nature of my objct (the original is a typied object as CalculationFunctionGroup and now is an ODocument). But the error is the same.
Another try I've done, the ODocument myEntity has not attached functions (list of CalculationFunction) but the error raises too
EDIT #2
I've tried with code snippet of Alessandro Rota and works fine.
But when I add as field of CalculationFunction a link field I've the same error! Why?
If I add a link field instead of object.getData() with object.getRawField("#rid") it works fine too.
I don't understand because raises that error message, and the reason of different behaviour when I use only #rid field instead complete object
EDIT #3
Latest news:
This is my test scenario:
I have this table:
CalculationFunction with these property (schemafull):
referenceItem LINK
functions EMBEDDEDLIST
When I try to save, I write this code:
ODocument myGroup = new ODocument("CalculationFunctionGroup");
Object rid = null;
if (object.getField("referenceItem") instanceof RegistryMetadata) {
rid = ((RegistryMetadata)(object.getField("referenceItem"))).getRawField("#rid");
} else if (object.getField("referenceItem") instanceof PayrollRegistryMetadata) {
rid = ((PayrollRegistryMetadata)(object.getField("referenceItem"))).getRawField("#rid");
} else if (object.getField("referenceItem") instanceof PreCalculationMetadata) {
rid = ((PreCalculationMetadata)(object.getField("referenceItem"))).getRawField("#rid");
} else if (object.getField("referenceItem") instanceof CalculationMetadata) {
rid = ((CalculationMetadata)(object.getField("referenceItem"))).getRawField("#rid");
}
myGroup.field("referenceItem", rid, OType.LINK);
myGroup.field("scenario", ((Scenario)object.getField("scenario")).getRawField("#rid"));
List<ODocument> lstFunctions = new ArrayList<ODocument>();
if (object.getField("functions") != null) {
Iterable<ODocument> lstFun = (Iterable<ODocument>) object.getField("functions");
Iterator<ODocument> itFun = lstFun.iterator();
while(itFun.hasNext()) {
ODocument currFun = itFun.next();
ODocument oFun = new ODocument("CalculationFunction");
oFun.field("name", currFun.field("name"));
oFun.field("code", currFun.field("code"));
oFun.field("language", currFun.field("language"));
lstFunctions.add(oFun);
}
}
myGroup.field("functions", lstFunctions, OType.EMBEDDEDLIST);
myGroup.save();
db.commit();
This code goes in error, but... If I write this:
myGroup.field("referenceItem2", rid, OType.LINK);
The code works fine.
The difference: referenceItem is a schemafull property, referenceItem2 is a schemaless field.
You could use this code
ODocument cf1= new ODocument("CalculationFunction");
cf1.field("name","Function 1",OType.STRING);
ODocument cf2= new ODocument("CalculationFunction");
cf2.field("name","Function 2",OType.STRING);
List<ODocument> list=new ArrayList<ODocument>();
list.add(cf1);
list.add(cf2);
ODocument p = new ODocument("CalculationFunctionGroup");
p.field("functions", list, OType.EMBEDDEDLIST);
p.save();
Edit 2
If you want add a link you could use this code
ODocument cf1= new ODocument("CalculationFunction");
cf1.field("name","Function 1",OType.STRING);
ODocument p = new ODocument("CalculationFunctionGroup");
p.field("mylink", cf1, OType.LINK);
p.save();
EDIT 3
I have created schemafull property mylink2
I have used this code
ODocument cf1= new ODocument("CalculationFunction");
cf1.field("name","Function 1",OType.STRING);
cf1.save();
ODocument p = new ODocument("CalculationFunctionGroup");
p.field("mylink", cf1.getIdentity(), OType.LINK);
p.field("mylink2", cf1.getIdentity(), OType.LINK);
p.save();
and I got
Hope it helps.

OrientDB fails to persist a Vertex, returns ORID but with null vertex eg v(null)[#3:4]

I cant get OrientDB to persist an OrientVertex or an oDocument using the java API.
although its works when I use SQL and the OCommand.
trying to persist a vertex with foll code
val factory: OrientGraphFactory = new OrientGraphFactory("plocal:localhost/database", "admin", "admin");
val graph = factory.getTx
val oDocument: ODocument = UserEntityMapper.toODocument(user)
println("ODocument = = "+ oDocument)
val orientVertex = new OrientVertex(graph, oDocument)
println("BEFORE orientVertex = "+ orientVertex)
graph.commit()
println("AFTER orientVertex = "+ orientVertex)
I get debug printouts as
ODocument = {phoneNumber:45435345,email:moses#email.com,dateOfBirth:-766198800000,lastName:Johnson,firstName:Moses,avatarURL:null,loginInfo:{providerID:credentials,providerKey:moses#email.com},userID:a8d96be9-1d09-4e8f-bf8d-6a0d32e1e5aa}
BEFORE orientVertex = v(null)[#3:-2]
AFTER orientVertex = v(null)[#3:4]
As you can see, the vertex is null. Nothing is persisted. I get an ORID that changes the cluster position number.
When I repeat the save the cluster position number is incremented
BEFORE orientVertex = v(null)[#3:-3]
AFTER orientVertex = v(null)[#3:7]
Why does this happen and how can I fix this please? There are no exceptions so i cant tell whats wrong. The cluster/table for User exists on the DB with all teh right fields.
I am using orientdb-community-2.2.0 and Scala.
regards
Sorted it out at last. I was instantiating the ODocument without the class name. In
UserEntityMapper.toODocument(user)
I was doing the equivalent of this
new ODocument(Map("name"->"john", "city" -> "London"))
instead of this
new ODocument("User").fromMap(Map("name"->"john", "city" -> "London"))
So the DB did not pick up the User class.
Thanks actual for pointing me in the right direction

How do I add a linked document to a linked list using the Java API for OrientDB?

I have an OType.LINKLIST field children in a class.
I can use the following command to update it:
update <parent_rid> add children = <child_rid>
But I don't know how to do this without using SQL, which is my goal.
code
ODocument doc=new ODocument("ClassA");
ODocument parentDoc=db.load(new ORecordId(rid));
How do I add doc to parentDoc's field children without using SQL?
create class Doc
create class ParentDoc
create property ParentDoc.children LINKLIST
insert into Doc set name = 'doc1' #12:0
insert into Doc set name = 'doc2' #12:1
insert into ParentDoc set name = 'pd', children = [#12:0] #13:0
update #13:0 add children = #12:1
For what I understood you want a piece of code that replaces the last four commands using Java Document API.
try (ODatabaseDocument db = new ODatabaseDocumentTx("remote:localhost/DB")) {
db.open("admin", "admin");
ODocument doc1 = new ODocument("Doc");
doc1.field("name", "doc1");
doc1.save();
List<OIdentifiable> linklist = new ArrayList();
linklist.add(doc1);
ODocument parent = new ODocument("ParentDoc");
parent.field("children", linklist, OType.LINKLIST);
parent.save();
// ...
ODocument doc2 = new ODocument("Doc");
doc2.field("name", "doc2");
doc2.save();
List children = parent.field("children");
children.add(doc2);
parent.field("children", children);
parent.save();
}

Changing schema name on runtime - Entity Framework

I need to change the storage schema of the entities on runtime.
I've followed a wonderful post, available here:
http://blogs.microsoft.co.il/blogs/idof/archive/2008/08/22/change-entity-framework-storage-db-schema-in-runtime.aspx?CommentPosted=true#commentmessage
This works perfectly, but only for queries, not for modifications.
Any idea why?
Well, I was looking for this piece of code all around the Internet. In the end I had to do it myself. It's based on Brandon Haynes adapter, but this function is all you need to change the schema on runtime - and you don't need to replace the autogenerated context constructors.
public static EntityConnection Create(
string schema, string connString, string model)
{
XmlReader[] conceptualReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".csdl")
)
};
XmlReader[] mappingReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".msl")
)
};
var storageReader = XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".ssdl")
);
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl";
var storageXml = XElement.Load(storageReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet"))
{
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null)
{
schemaAttribute.SetValue(schema);
}
}
storageXml.CreateReader();
StoreItemCollection storageCollection =
new StoreItemCollection(
new XmlReader[] { storageXml.CreateReader() }
);
EdmItemCollection conceptualCollection = new EdmItemCollection(conceptualReader);
StorageMappingItemCollection mappingCollection =
new StorageMappingItemCollection(
conceptualCollection, storageCollection, mappingReader
);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories
.GetFactory(connectionData.Provider)
.CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}
The resulting EntityConnection should be passed as a parameter when instantiating the context. You can modify it, so all ssdl models are modified by this function, not only the specified one.
I've managed to resolve this issue by using a brilliant library, located in CodePlex (courtesy of Brandon Haynes), named "Entity Framework Runtime Model Adapter", available here:
http://efmodeladapter.codeplex.com/
I've tweaked it a bit, to fit our needs and without the need of replacing the designer code at all.
So, I'm good.
Thanks anyways, and especially to Brandon, amazing job!
I need import data from postgres database. It by default use schema "public". So I use Entity Framework CTP 4 "Code first". It by default use schema "dbo". To change it in runtime I use:
public class PublicSchemaContext : DbContext
{
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder builder)
{
builder.Entity<series_categories>().MapSingleType().ToTable("[public].[series_categories]");
}
public DbSet<series_categories> series_categories { get; set; }
}
It work for select, insert, update and delete data. So next test in pass:
[Test]
public void AccessToPublicSchema()
{
// Select
var db = new PublicSchemaContext();
var list = db.series_categories.ToList();
Assert.Greater(list.Count, 0);
Assert.IsNotNull(list.First().series_category);
// Delete
foreach (var item in db.series_categories.Where(c => c.series_category == "Test"))
db.series_categories.Remove(item);
db.SaveChanges();
// Insert
db.series_categories.Add(new series_categories { series_category = "Test", series_metacategory_id = 1 });
db.SaveChanges();
// Update
var test = db.series_categories.Single(c => c.series_category == "Test");
test.series_category = "Test2";
db.SaveChanges();
// Delete
foreach (var item in db.series_categories.Where(c => c.series_category == "Test2"))
db.series_categories.Remove(item);
db.SaveChanges();
}
Not an answer per se but a followup on Jan Matousek's Create[EntityConnection] method showing how to use from a DbContext. Note DB is the DbContext type passed to the generic repository.
public TxRepository(bool pUseTracking, string pServer, string pDatabase, string pSchema="dbo")
{
// make our own EF database connection string using server and database names
string lConnectionString = BuildEFConnectionString(pServer, pDatabase);
// do nothing special for dbo as that is the default
if (pSchema == "dbo")
{
// supply dbcontext with our connection string
mDbContext = Activator.CreateInstance(typeof(DB), lConnectionString) as DB;
}
else // change the schema in the edmx file before we use it!
{
// Create an EntityConnection and use that to create an ObjectContext,
// then that to create a DbContext with a different default schema from that specified for the edmx file.
// This allows us to have parallel tables in the database that we can make available using either schema or synonym renames.
var lEntityConnection = CreateEntityConnection(pSchema, lConnectionString, "TxData");
// create regular ObjectContext
ObjectContext lObjectContext = new ObjectContext(lEntityConnection);
// create a DbContext from an existing ObjectContext
mDbContext = Activator.CreateInstance(typeof(DB), lObjectContext, true) as DB;
}
// finish EF setup
SetupAndOpen(pUseTracking);
}
I was able to convert the solution from Jan Matousek to work in vb.net 2013 with entity framework 6. I will also try to explain how to use the code in vb.net.
We have a JD Edwards Database which uses different Schema's for each environment (TESTDTA, CRPDTA, PRODDTA). This makes switching between environments cumbersome as you have to manually modify the .edmx file if you want to change environments.
First step is to create a partial class that allows you to pass a value to the constructor of your entities, by default it uses the values from your config file.
Partial Public Class JDE_Entities
Public Sub New(ByVal myObjectContext As ObjectContext)
MyBase.New(myObjectContext, True)
End Sub
End Class
Next create the function that will modify your store schema .ssdl file in memory.
Public Function CreateObjectContext(ByVal schema As String, ByVal connString As String, ByVal model As String) As ObjectContext
Dim myEntityConnection As EntityConnection = Nothing
Try
Dim conceptualReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".csdl"))
Dim mappingReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".msl"))
Dim storageReader As XmlReader = XmlReader.Create(Me.GetType().Assembly.GetManifestResourceStream(model + ".ssdl"))
Dim storageNS As XNamespace = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl"
Dim storageXml = XDocument.Load(storageReader)
Dim conceptualXml = XDocument.Load(conceptualReader)
Dim mappingXml = XDocument.Load(mappingReader)
For Each myItem As XElement In storageXml.Descendants(storageNS + "EntitySet")
Dim schemaAttribute = myItem.Attributes("Schema").FirstOrDefault
If schemaAttribute IsNot Nothing Then
schemaAttribute.SetValue(schema)
End If
Next
storageXml.Save("storage.ssdl")
conceptualXml.Save("storage.csdl")
mappingXml.Save("storage.msl")
Dim storageCollection As StoreItemCollection = New StoreItemCollection("storage.ssdl")
Dim conceptualCollection As EdmItemCollection = New EdmItemCollection("storage.csdl")
Dim mappingCollection As StorageMappingItemCollection = New StorageMappingItemCollection(conceptualCollection, storageCollection, "storage.msl")
Dim workspace = New MetadataWorkspace()
workspace.RegisterItemCollection(conceptualCollection)
workspace.RegisterItemCollection(storageCollection)
workspace.RegisterItemCollection(mappingCollection)
Dim connectionData = New EntityConnectionStringBuilder(connString)
Dim connection = DbProviderFactories.GetFactory(connectionData.Provider).CreateConnection()
connection.ConnectionString = connectionData.ProviderConnectionString
myEntityConnection = New EntityConnection(workspace, connection)
Return New ObjectContext(myEntityConnection)
Catch ex As Exception
End Try
End Function
Make sure that the storageNS namespace hardcoded value matches the one used in your code, you can view this by debugging the code and examining the storageXML variable to see what was actually used.
Now you can pass a new schema name, and different database connection info at runtime when you create your entities. No more manual .edmx changes required.
Using Context As New JDE_Entities(CreateObjectContext("NewSchemaNameHere", ConnectionString_EntityFramework("ServerName", "DatabaseName", "UserName", "Password"), "JDE_Model"))
Dim myWO = From a In Context.F4801 Where a.WADOCO = 400100
If myWO IsNot Nothing Then
For Each r In myWO
Me.Label1.Text = r.WADL01
Next
End If
End Using
These were the .net libraries used:
Imports System.Data.Entity.Core.EntityClient
Imports System.Xml
Imports System.Data.Common
Imports System.Data.Entity.Core.Metadata.Edm
Imports System.Reflection
Imports System.Data.Entity.Core.Mapping
Imports System.Data.Entity.Core.Objects
Imports System.Data.Linq
Imports System.Xml.Linq
Hope that helps anyone out there with the same issues.
I had a lot of problems getting this to work when using EF6 with an OData Data Service, so I had to find an alternate solution. In my case, I didn't really need to do it on the fly. I could get away with changing the schema when deploying to some test environments, and in the installer.
Use Mono.Cecil to rewrite the embedded .ssdl resources straight in the DLLs. This works just fine in my case.
Here is a simplified example of how you can do this:
var filename = "/path/to/some.dll"
var replacement = "Schema=\"new_schema\"";
var module = ModuleDefinition.ReadModule(filename);
var ssdlResources = module.Resources.Where(x => x.Name.EndsWith(".ssdl"));
foreach (var resource in ssdlResources)
{
var item = (EmbeddedResource)resource;
string rewritten;
using (var reader = new StreamReader(item.GetResourceStream()))
{
var text = reader.ReadToEnd();
rewritten = Regex.Replace(text, "Schema=\"old_schema\"", replacement);
}
var bytes = Encoding.UTF8.GetBytes(rewritten);
var newResource = new EmbeddedResource(item.Name, item.Attributes, bytes);
module.Resources.Remove(item);
module.Resources.Add(newResource);
}

EntityReference has an EntityKey property value that does not match?

I am attempting to add some entities that I have created. When I try and add the entity in question to the set (see code below) I get the following error:
"The object could not be added or attached because its EntityReference has an EntityKey property value that does not match the EntityKey for this object."
I can't tell what entitykey it's referring to though. Here is the code, there is probably a much better way to pull this off as well:
public Internship CreateInternship(Internship internshipToCreate)
{
try
{
Contact contactToCreate = new Contact();
contactToCreate.Fax = internshipToCreate.contacts.Fax;
contactToCreate.Extension = internshipToCreate.contacts.Extension;
contactToCreate.FirstName = internshipToCreate.contacts.FirstName;
contactToCreate.MiddleName = internshipToCreate.contacts.MiddleName;
contactToCreate.LastName = internshipToCreate.contacts.LastName;
contactToCreate.PhoneNumber = internshipToCreate.contacts.PhoneNumber;
contactToCreate.StreetAddress = internshipToCreate.contacts.StreetAddress;
contactToCreate.PostalCode = internshipToCreate.contacts.PostalCode;
contactToCreate.ContactEmail = internshipToCreate.contacts.ContactEmail;
contactToCreate.statesReference.EntityKey =
new EntityKey("InternshipEntities.StateSet", "ID", internshipToCreate.contacts.states.ID);
contactToCreate.countriesReference.EntityKey =
new EntityKey("InternshipEntities.CountrySet", "ID", internshipToCreate.contacts.countries.ID);
_internshipEntities.AddToContactSet(contactToCreate);
_internshipEntities.SaveChanges();
try
{
Availability availabilityToCreate = new Availability();
availabilityToCreate.StartDate = internshipToCreate.availability.StartDate;
availabilityToCreate.EndDate = internshipToCreate.availability.EndDate;
availabilityToCreate.Negotiable = internshipToCreate.availability.Negotiable;
_internshipEntities.AddToAvailabilitySet(availabilityToCreate);
_internshipEntities.SaveChanges();
try
{
internshipToCreate.contactsReference.EntityKey =
new EntityKey("InternshipEntities.ContactSet", "ID", contactToCreate.ID);
internshipToCreate.availabilityReference.EntityKey =
new EntityKey("InternshipEntities.AvailabilitySet", "ID", availabilityToCreate.ID);
internshipToCreate.classificationsReference.EntityKey =
new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
internshipToCreate.educationReference.EntityKey =
new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);
_internshipEntities.AddToInternshipSet(internshipToCreate); //exception here
_internshipEntities.SaveChanges();
return internshipToCreate;
}
catch(Exception e)
{
throw e;
}
}
catch(Exception e)
{
throw e;
}
}
catch(Exception e)
{
throw e;
}
}
There is no other information given besides the error when I trace through so I'm not even sure which Key is the issue.
EDIT: Here is the version that ended up working:
using (TransactionScope scope = new TransactionScope())
{
try
{
Contact contactToCreate = new Contact();
Availability availabilityToCreate = new Availability();
Internship i = new Internship();
// Set the contact entity values;
contactToCreate.Fax = internshipToCreate.contacts.Fax;
//...
//ommited for brevity
//...
contactToCreate.ContactEmail = internshipToCreate.contacts.ContactEmail;
// Set the contact entity references to existing tables
contactToCreate.statesReference.EntityKey =
new EntityKey("InternshipEntities.StateSet", "ID", internshipToCreate.contacts.states.ID);
contactToCreate.countriesReference.EntityKey =
new EntityKey("InternshipEntities.CountrySet", "ID", internshipToCreate.contacts.countries.ID);
// Add contact
_internshipEntities.AddToContactSet(contactToCreate);
// Set the availability entity values;
availabilityToCreate.StartDate = internshipToCreate.availability.StartDate;
availabilityToCreate.EndDate = internshipToCreate.availability.EndDate;
availabilityToCreate.Negotiable = internshipToCreate.availability.Negotiable;
// Add availability
_internshipEntities.AddToAvailabilitySet(availabilityToCreate);
//Add contact and availability entities to new internship entity
i.contacts = contactToCreate;
i.availability = availabilityToCreate;
// Set internship entity values;
i.UserID = internshipToCreate.UserID;
//...
//ommited for brevity
//...
i.Created = DateTime.Now;
// Set the internship entity references to existing tables
i.classificationsReference.EntityKey =
new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
i.educationReference.EntityKey =
new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);
// Add internship and save
_internshipEntities.AddToInternshipSet(i);
_internshipEntities.SaveChanges();
//commit transaction
scope.Complete();
return internshipToCreate;
}
catch (Exception e)
{
throw e;
}
}
Hallo,
although I'm not sure what the problem is I have a suggestion. The Internship object that you are passing into method (internshipToCreate) is used to transfer values to other entities (Contact, Availability) that you instantiated inside of the method, and their creation works just fine.
Maybe you should try to do the same with Internship? Create new Internship object and set all values you have by taking them from internshipToCreate object, and than that newly created object pass to the _internshipEntities.AddToInternshipSet method.
It is possible that you've set some values on internshipToCreate object that you needed for other purposes, and that some of those is actually causing the exception.
And, I don't know what you business logic is, but it would be better if you put all under one transaction, because like this it may happen that first two entities are created, and third one not.
This code isn't making a lot of sense to me. In two cases, you're going through an EntityKey when you could just assign an object reference. I.e, change this:
internshipToCreate.contactsReference.EntityKey =
new EntityKey("InternshipEntities.ContactSet", "ID", contactToCreate.ID);
internshipToCreate.availabilityReference.EntityKey =
new EntityKey("InternshipEntities.AvailabilitySet", "ID", availabilityToCreate.ID);
...to:
internshipToCreate.contacts = contactToCreate;
internshipToCreate.availability = availabilityToCreate;
In the other two cases you seem to be attempting to assign the ID of the object which is already there. These two lines, even if successful, it seems to me, would do nothing:
internshipToCreate.classificationsReference.EntityKey =
new EntityKey("InternshipEntities.ClassificationSet", "ID", internshipToCreate.classifications.ID);
internshipToCreate.educationReference.EntityKey =
new EntityKey("InternshipEntities.EducationSet", "ID", internshipToCreate.education.ID);
So you can just get rid of them.
What happens when you make these two changes?