I have a requirement to sync some entities (account, lead, contact etc) to a database table outside of the crm database but on the same server. I am looking for a supported way for doing this. Here's what I have tried, that don't work:
I first created table in the outside database that matches the schema from dbo.account (view). Then I wrote post create, post update, post assign and post delete plugins to create, update or delete the record in the outside table (using ADO.Net). I have written the plugin in the most generic way so that it can be registered for any entity with minimum changes to the plugin (by not hardcoding the field names). Doing it this way, the problem I am running into is with the fields that are foreign key to other tables. Eg. in dbo.account, there are fields like PrimaryContactId and PrimaryContactIdName, PreferredSystemUserId and PreferredSystemUserIdName, ParentAccountId and ParentAccountIdName etc. In the input parameters for the plugin, the xxxxId fields are available when they are updated, but not the 'xxxxIdName' fields. Because of which I am not able to 'sync' the table as is.
Is there a solution to make my plugin solution work?
Is there a better supported way for having a sync table?
Thanks in advance,
PS: 1. The data sync has to be in real time
PS: 2. Here is my function to get the query that does the update
private static string PrepareUpdateQuery(ITracingService tracingService, IEnumerable<KeyValuePair<string, object>> attributeCollection, string entityName, string entityIdName)
{
var query = "Update MainDb.MSCRM." + entityName + " set ";
foreach (KeyValuePair<string, object> keyValuePair in attributeCollection)
{
tracingService.Trace("Key: {0}", keyValuePair.Key);
if (keyValuePair.Key != entityIdName && keyValuePair.Key != "modifiedonbehalfby")
{
query = query + keyValuePair.Key + " = ";
if (keyValuePair.Value == null)
query = query + "null, ";
else
{
var typeOfValue = keyValuePair.Value.GetType().Name;
tracingService.Trace("typeOfValue: {0}", typeOfValue);
switch (typeOfValue)
{
case "EntityReference":
query = query + "'" + ((EntityReference)keyValuePair.Value).Id + "', ";
break;
case "OptionSetValue":
query = query + ((OptionSetValue)keyValuePair.Value).Value + ", ";
break;
case "BooleanManagedProperty":
query = query + (((BooleanManagedProperty)keyValuePair.Value).Value ? "1" : "0") + ", ";
break;
default:
query = query + "'" + keyValuePair.Value + "', ";
break;
}
}
}
}
return query;
}
If all you're after is the name of the entity that is an attribute on your currently executing plugin, the EntityReference object has a Name property that should contain that name. If it doesn't you you can query CRM with the id and logical name to get any value that you're looking for on the referenced entity.
Edit 1
If you're just moving the data, why even bother setting the referenced name? I'd removed those names from your database table, and just create a view that looks up the corresponding entity's name. It's what CRM is doing. It also makes your other database more normalized. IE. If you update the name of an entity that is referenced by another entity, you will have to search for and update all of those names...
the xxxIdName fields are just a helper for the views really, you can easily figure out what they
should contain.
For example, say you have an account 'some company' with a primary contact called 'bob bobson'.
when processing the account entity the primarycontactId will be a guid and the primarycontactIdName will be 'bob bobson', the accountIdName will be 'some company'.
easiest way to do this in your plugin is to look up the related entity and get the value from there - 90% of the time it's just the name field.
you also need to consider however if you are doing the right thing in using the CRM schema, perhaps it would be better to copy only the fields you need and use your own schema for the sync table.
UPDATE: just saw your code, you are overwritting the value contained in query and not setting it back to the base query, so you will get odd results/errors on the second pass through the foreach
If you're dead set on putting the related entity name in the primary entity table you can always grab it like this:
var entityEntityRef = (EntityReference)keyValuePair.Value;
var relatedEntity = service.Retrieve(entityRef.LogicalName, entityRef.Id, new ColumnSet(true));
Now relatedEntity as all the attributes available. You'll mostly be looking for the Name field, but some entities are different, like contact which uses the full name field I believe.
You can, in fact, register a single plugin for all entities (checking, of course, that the one that's firing the message is in the list of treated ones).
IEnumerable<String> supportees = new String[]{ "account", "contact" };
if(!supportees.Any(element
=> element == targetLogicalName))
return;
As for the linked entities, you have three choices.
Just skip them. Not full data sync but easies to implement.
Store the guids only. Data sync is instance-wide - limited but moderately easy.
Get all the linked data. Full information but a recursive PIA to develop.
Related
I need to make a generic exporter/importer from a Microsoft CRM 2011 system and I cant find out if a LookupAttribute is a one-to-one- or one-to-many-relation.
E.x.
I have an Entity called an "E-mail".
And e-mail can have one "From" (which is a lookup attribute that does its lookup into multiple other Entity-lists)
It can then have multiple "To" (also referencing out into multiple entity-lists as a lookup attribute).
The conclusions I just made are stuff that i figured out by using the web portal for MS CRM. I just cant find any way to see in the metadata of these Entities or Attributes if its a one-to-one- or a on-to-many-relation.
https://msdn.microsoft.com/en-us/library/gg509035.aspx#BKMK_CreateLookupAttribute
I've looked at this example on how to create these kinds of relations, but it hasnt gotten me any futher.
Does anyone have any experience with generic export/import with MS CRM 2011 that can help me?
CRM has a two types of relationships:
One to many: a lookup on one entity, a grid on the other.
Many to many: a grid on both entities.
There is no such thing as a one to one relationship. You are looking at a lookup. 90% of the lookups you see are simple, you can select one record of one entity type.
However, there are special system field lookups, named activity party. These muddy the waters a little. Some activity party lookups allow multiple record selections, some link to entities of multiple types. Which means you can have an activity party lookup with multiple records of multiple entity types. I suppose you could call this a "one to many (records) of many (entities)".
For example on an email; the To field can be populated with multiple account and contact records. Whilst the From field can only have one record, but that can be a system user or queue. The To and From are both examples of activity party lookups.
So in terms of what you are trying to do, you need to examine:
AttributeMetadata.AttributeType to see if its a party list.
LookupAttributeMetadata.Targets to see what record types are allowed in the lookup.
The following code shows how to do this for a couple of different fields.
RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = "email",
LogicalName = "to",
RetrieveAsIfPublished = true
};
RetrieveAttributeResponse result = Service.Execute(attributeRequest) as RetrieveAttributeResponse;
Trace.WriteLine("Email - To");
Trace.WriteLine("AttributeMetadata.AttributeType: " + result.AttributeMetadata.AttributeType);
Trace.WriteLine("LookupAttributeMetadata.Targets: " + ((LookupAttributeMetadata)result.AttributeMetadata).Targets.CollectionToString(", "));
attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = "email",
LogicalName = "from",
RetrieveAsIfPublished = true
};
result = Service.Execute(attributeRequest) as RetrieveAttributeResponse;
Trace.WriteLine("Email - From");
Trace.WriteLine("AttributeMetadata.AttributeType: " + result.AttributeMetadata.AttributeType);
Trace.WriteLine("LookupAttributeMetadata.Targets: " + ((LookupAttributeMetadata)result.AttributeMetadata).Targets.CollectionToString(", "));
attributeRequest = new RetrieveAttributeRequest
{
EntityLogicalName = "account",
LogicalName = "parentaccountid",
RetrieveAsIfPublished = true
};
result = Service.Execute(attributeRequest) as RetrieveAttributeResponse;
Trace.WriteLine("Account - Parent Account Id");
Trace.WriteLine("AttributeMetadata.AttributeType: " + result.AttributeMetadata.AttributeType);
Trace.WriteLine("LookupAttributeMetadata.Targets: " + ((LookupAttributeMetadata)result.AttributeMetadata).Targets.CollectionToString(", "));
And the output:
Email - To
AttributeMetadata.AttributeType: PartyList
LookupAttributeMetadata.Targets: account, contact, lead, queue, systemuser
Email - From
AttributeMetadata.AttributeType: PartyList
LookupAttributeMetadata.Targets: queue, systemuser
Account - Parent Account Id
AttributeMetadata.AttributeType: Lookup
LookupAttributeMetadata.Targets: account
I am creating a custom module in Orchard , I would like to create a query programmatically.
string queryName= "Product";
var item = _orchardServices.ContentManager.New("Query");
item.As<TitlePart>().Title =queryName;
_orchardServices.ContentManager.Create(item, VersionOptions.Draft);
if (!item.Has<IPublishingControlAspect>() && !item.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
_orchardServices.ContentManager.Publish(item);
var queryPart = item.As<QueryPart>();
queryPart.ContentItem.ContentType = queryName;
string desc =" filter for the query";
string contentType = "CommonPart.ChannelID.";
var filterGroupRecord = new FilterGroupRecord();
var filterRecord = new FilterRecord()
{
Category = "CommonPartContentFields",
Type = contentType,
Position = 0,
};
filterRecord.State = "<Form><Description>" + desc + "</Description><Operator>Equals</Operator><Value>ChannelId</Value></Form>";
filterGroupRecord.Filters.Add(filterRecord);
queryPart.FilterGroups.Insert(0, filterGroupRecord);
the problem is that:I want set a filters of the query,not a filters group.
could you tell me how to improve my code?
Database structure and class declarations make it impossible. Why do you need it?
Update:
I means that you must use FilterGroupRecord at least one.
But when Query published that Filter Group will be created automatically if query have not yet Filter Group (see at QueryPartHandler). You should add your filters to this group. And not needed to create new group.
var existingFilterGroup = queryPart.FilterGroups[0];
existingFilterGroup.Filters.Add(filterRecord);
Update 2:
To avoid problems with draftable query (and several other potential problems Orchard CMS: Adding default data to fields and then querying them) it is better to move the calling Publish method to the end of your code and other part of your code should be left unchanged. And in your case would be better if you will always publish your query without checking IPublishingControlAspect and Draftable.
Objective:
I have a table called Publication that contains Id, RecordId, EntityType and a couple other columns. I select all the records that need to be published to another database from that table. I then loop that collection to process the records and move the records to the other db.
Background:
The EntityType column is used to Identify the Set that the context needs to retrieve. I also use reflection to create a object of that type to see if it implements a certain type of interface. If the record being processed does implement that interface then I know that the RecordId for that record in the Publication table is not a PK in the Set() but rather a FK.
this code works fine when I am going after the PK values for EntityTypes that do not inherit the specific interface.
object authoringRecordVersion = PublishingFactory.AuthoringContext.Set(recordType.Entity.GetType()).Find(record.RecordId);
Problem:
DbContext.Set(EntityType).Find(PK) goes after the PrimaryKey value. How can I tell Set() to search like this sudo code example since 'Where' is not allowed
object authoringRecordVersion = PublishingFactory.AuthoringContext.Set(recordType.Entity.GetType()).Where(c => c.HeaderRecordId == record.RecordId)
Update:
I am working on Implementing the following. Will advise results tomorrow
var sql = "SELECT * from " + record.Entity + " WHERE HeaderRecordId = '" + record.RecordId + "'";
authoringRecordVersion = PublishingFactory.AuthoringContext.Set(recordType.Entity.GetType()).SqlQuery(sql).AsNoTracking();
.SqlQuery(sql).AsNoTracking();
does work effectively. Don't know why I didn't see this earlier.
I've an entity with assigned string Id on NHibernate and I've a little problem when get an entity by Id.
Example...
Suppose that have a database record like this...
Id Description
-------------------
AAA MyDescription
now, if I use "Get" method using search id "aaa"...
MYENTITYTYPE entity = Session.Get<MYENTITYTYPE>("aaa")
return right entity but Id field (entity.Id) is "aaa", while I wish it were equal to "AAA".
In summary I would like that "Get" method return the id identical to the one stored in the database...with the same case.
Is possible? How can I do?
Interesting question. My guess is that it's not possible, because the Id might exist before the DB call. Consider the following:
var foo = session.Load<Foo>("aaa"); //no DB call, foo is a proxy
Console.WriteLine(foo.Id); //Prints "aaa";
var bar = foo.Bar; //Forces loading
Console.WriteLine(foo.Id); //No matter what, the Id can't change at this point
This illustrates another reason why primary keys with meaning are usually a bad idea, especially if their input is not controlled.
Now, if instead of Get you use a query, you will get the right-cased Id:
//example with LINQ; you can use HQL, Criteria, etc
var foo = session.Query<Foo>().Single(x => x.Id == "aaa");
The drawback is that you will always go to the DB, even if the entity is already loaded.
Now, if you defined your entity as {Id, Code, Description}, where Id is a synthetic POID (I recommend Hilo or Guid) and Code is the existing string Id, you will avoid potential bugs caused by using Get instead of a query with the code.
Hi I have an existing database with a table with 30 fields, I want to split the table into many models so I could retrieve/save fields that I need and not every time retrieve/save the whole object from the db. using c#.
I think I should be using Code-First. Could someone provide an example or a tutorial link?
thanks,
You don't need to split table to be able to load a subset of field or persist subset of fields. Both operations are available with the whole table mapped to single entity as well.
For selection you simply have to use projection:
var data = from x in context.HugeEntities
select new { x.Id, x.Name };
You can use either anonymous type in projection or any non-mapped class.
For updates you can simply use:
var data = new HugeEntity { Id = existingId, Name = newName };
context.HugeEntities.Attach(data);
var dataEntry = context.Entry(data);
dataEntry.Property(d => d.Name).IsModified = true; // Only this property will be updated
context.SaveChanges();
Or:
var data = new HugeEntity { Id = existingId };
context.HugeEntities.Attach(data);
data.Name = newName;
context.SaveChanges(); // Now EF detected change of Name property and updated it
Mapping multiple entities to single table must follows very strict rules and it is possible only with table splitting were all entities must be related with one-to-one relation (and there are some problems with more than two entities per split table in code first) or with table-per-hierarchy inheritance. I don't think that you want to use any of them for this case.