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;
}
Related
Working with CRM 2013, how can I get a list of all entities in the CRM via the connectionManager class? I want to get all the entities for the current connection.
Thank you for your comment and answer it work now,
this is my function
public static EntityMetadata[] GetEntities ( IOrganizationService organizationService)
{
Dictionary<string, string> attributesData = new Dictionary<string, string>();
RetrieveAllEntitiesRequest metaDataRequest = new RetrieveAllEntitiesRequest();
RetrieveAllEntitiesResponse metaDataResponse = new RetrieveAllEntitiesResponse();
metaDataRequest.EntityFilters = EntityFilters.Entity;
// Execute the request.
metaDataResponse = (RetrieveAllEntitiesResponse)organizationService.Execute(metaDataRequest);
var entities = metaDataResponse.EntityMetadata;
return entities;
}
and i call my function in the windows app form like this:
var allEntities = CRMHelpers.GetEntities(service);
foreach (EntityMetadata Entity in allEntities)
{
cbxEntity.Items.Add(Entity.LogicalName);
}
If you are looking for getting the entity metadata using code (C#) then we have inbuilt messages to get all entities and if required attribute level information as well. You can use the message "RetrieveAllEntitiesRequest". A sample code would be as follows to achieve the same.
RetrieveAllEntitiesRequest retrieveAllEntityRequest = new RetrieveAllEntitiesRequest
{
RetrieveAsIfPublished = true,
EntityFilters = EntityFilters.Attributes
};
RetrieveAllEntitiesResponse retrieveAllEntityResponse = (RetrieveAllEntitiesResponse)serviceProxy.Execute(retrieveAllEntityRequest);
If you need to get a specific entity information then you may use the message "RetrieveEntityRequest". A sample for the same would be as follows,
RetrieveEntityRequest entityRequest = new RetrieveEntityRequest
{
EntityFilters = EntityFilters.Attributes,
LogicalName = entityName,
RetrieveAsIfPublished = true
};
RetrieveEntityResponse entityResponse = (RetrieveEntityResponse)serviceProxy.Execute(entityRequest);
Hope this is what you were looking for. Let us know if you need any more information on the same.
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.
I use Silverlight o-data services to interact with crm 2011 from my application
When I try to save the data in the entity SalesOrder as follows:
Private void beginSave()
{
SalesOrder orderHeader = new SalesOrder();
orderHeader.TransactionCurrencyId = new EntityReference(){ Id = new Guid("77D695B5-ACB4-E111-97BC-00155D55B216"), LogicalName="transactioncurrency" };
orderHeader.AccountId = new EntityReference() { Id = new Guid(MyClassGeneralOrder.customerId), LogicalName = "account" };
orderHeader.Name = "My Name";
Money totalAmount = new Money(); Money totalAmountBase = new Money();
Money totalTaxe = new Money(); Money totalAmountLessFreight = new Money();
totalAmount.Value = (decimal)MyClassGeneralOrder.InvoiceTotal;
totalAmountBase.Value = (decimal)MyClassGeneralOrder.totalRetail;
totalTaxe.Value = (decimal)MyClassGeneralOrder.totalCharges;
totalAmountLessFreight.Value = (decimal)MyClassGeneralOrder.totalNet;
orderHeader.TotalAmount = totalAmount;
orderHeader.TotalAmount_Base = totalAmountBase;
orderHeader.TotalTax = totalTaxe;
orderHeader.TotalAmountLessFreight = totalAmountLessFreight;
orderHeader.Description = element.Name;
orderHeader.PriceLevelId = new EntityReference() { Id = new Guid("03C5C4CB-EBD0-E111-8140-00155D55B216"), LogicalName="pricelevel" };
_context.AddToSalesOrderSet(orderHeader);
_context.BeginSaveChanges(SaveCallback, orderHeader);
}
private void SaveCallback(IAsyncResult result)
{
_context.EndSaveChanges(result);
}
In my function EndSaveChanges (result), I receive this error message : : « The Currency Cannot Be null ».
I don't understand why, because my "orderHeader.TransactionCurrencyId" field is not null.
I assuming that all of your other Currency fields are populated?
Any chance you have another plugin that is firing as a result of yours that is throwing the exception. That always seems to bite me. Try disabling all other plugins except for the one you're working on...
If you're still having issues, turn on crm server side tracing. You'll get much better error information. Use the CRM diagnostic tool to turn on trace logging: http://crmdiagtool2011.codeplex.com
Mostly your Guid is wrong and it's resulting in null. Make sure it's the correct GUID you are using or not. Run an advanced find against the entity and find the correct GUID. It's not a good idea to hard code the GUID. If you deploy your solutions to some other org it won't work.
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();