RavenDB throws a JSON deserialisation error when retrieving document - c#

I've just completed a round of refactoring of my application, which has resulted in my removing a project that was no longer required and moving its classes into a different project. A side effect of this is that my User class, which is stored in RavenDB, has a collection property of a type moved to the new assembly. As soon as I attempt to query the session for the User class I get a Json deserialisation error. The issue is touched upon here but the answers don't address my issue. Here's the offending property:
{
"OAuthAccounts": {
"$type": "System.Collections.ObjectModel.Collection`1[
[Friendorsement.Contracts.Membership.IOAuthAccount,
Friendorsement.Contracts]], mscorlib",
"$values": []
},
}
OAuthAccounts is a collection property of User that used to map here:
System.Collections.ObjectModel.Collection`1[[Friendorsement.Contracts.Membership.IOAuthAccount, Friendorsement.Contracts]]
It now maps here:
System.Collections.ObjectModel.Collection`1[[Friendorsement.Domain.Membership.IOAuthAccount, Friendorsement.Domain]]
Friendorsement.Contracts no longer exists. All of its types are now in Friendorsement.Domain
I've tried using store.DatabaseCommands.StartsWith("User", "", 0, 128) but that didn't return anything.
I've tried looking at UpdateByIndex but not got very far with it:
store.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Users"},
new[]
{
new PatchRequest { // unsure what to set here }
});
I'm using Raven 2.0

Below is a simple sample application that shows you the patching Metadata. While your example is a little different this should be a good starting point
namespace SO19941925
{
internal class Program
{
private static void Main(string[] args)
{
IDocumentStore store = new DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "SO19941925"
}.Initialize();
using (IDocumentSession session = store.OpenSession())
{
for (int i = 0; i < 10; i++)
{
session.Store(new User {Name = "User" + i});
}
session.SaveChanges();
}
using (IDocumentSession session = store.OpenSession())
{
List<User> users = session.Query<User>().Customize(x => x.WaitForNonStaleResultsAsOfNow()).ToList();
Console.WriteLine("{0} SO19941925.Users", users.Count);
}
Operation s = store.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Users"},
new ScriptedPatchRequest
{
Script = #"this['#metadata']['Raven-Clr-Type'] = 'SO19941925.Models.User, SO19941925';"
}, true
);
s.WaitForCompletion();
using (IDocumentSession session = store.OpenSession())
{
List<Models.User> users =
session.Query<Models.User>().Customize(x => x.WaitForNonStaleResultsAsOfNow()).ToList();
Console.WriteLine("{0} SO19941925.Models.Users", users.Count);
}
Console.ReadLine();
}
}
internal class User
{
public string Name { get; set; }
}
}
namespace SO19941925.Models
{
internal class User
{
public string Name { get; set; }
}
}
UPDATE: Based on the initial answer above, here is the code that actually solves the OP question:
store.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Users"},
new ScriptedPatchRequest
{
Script = #"this['OAuthAccounts']['$type'] =
'System.Collections.ObjectModel.Collection`1[
[Friendorsement.Domain.Membership.IFlexOAuthAccount,
Friendorsement.Domain]], mscorlib';",
}, true
);

Here are two possible solutions:
Option 1: Depending on what state your project is in, for example if you are still in development, you could easily just delete that collection out of RavenDB from the Raven Studio and recreate all those User documents. All the new User documents should then have the correct class name and assembly and should then deserialize correctly. Obviously, if you are already in production, this probably won't be a good option.
Option 2: Depending on how many User documents you have, you should be able to manually edit each one to specify the correct C# class name and assembly, so that they will be deserialized correctly. Again, if you have too many objects to manually modify, this may not be a good option; however, if there are just a few, it shouldn't be too bad to open each one up go to the metadata tab and paste the correct value for "Raven-Entity-Name" and "Raven-Clr-Type".

I ended up doing this:
Advanced.DatabaseCommands.UpdateByIndex(
"Raven/DocumentsByEntityName",
new IndexQuery {Query = "Tag:Album"},
new []{ new PatchRequest() {
Type = PatchCommandType.Modify,
Name = "#metadata",
Nested= new []{
new PatchRequest{
Name= "Raven-Clr-Type",
Type = PatchCommandType.Set,
Value = "Core.Model.Album, Core" }}}},
false);

Related

Google AdWords library doesn't create an access token

I'm trying to make a targetingIdeaService API call to Google AdWords. This is my code so far:
[HttpGet]
public IEnumerable<string> Get()
{
var user = new AdWordsUser();
using (TargetingIdeaService targetingIdeaService = (TargetingIdeaService)user.GetService(AdWordsService.v201802.TargetingIdeaService))
{
// Create selector.
TargetingIdeaSelector selector = new TargetingIdeaSelector();
selector.requestType = RequestType.IDEAS;
selector.ideaType = IdeaType.KEYWORD;
selector.requestedAttributeTypes = new AttributeType[] {
AttributeType.KEYWORD_TEXT,
AttributeType.SEARCH_VOLUME,
AttributeType.AVERAGE_CPC,
AttributeType.COMPETITION,
AttributeType.CATEGORY_PRODUCTS_AND_SERVICES
};
// Set selector paging (required for targeting idea service).
var paging = Paging.Default;
// Create related to query search parameter.
var relatedToQuerySearchParameter =
new RelatedToQuerySearchParameter
{ queries = new String[] { "bakery", "pastries", "birthday cake" } };
var searchParameters = new List<SearchParameter> { relatedToQuerySearchParameter };
var page = new TargetingIdeaPage();
page = targetingIdeaService.get(selector);
return new string[] { "value1", "value2" };
}
}
it looks ok, compiles at last, and so on. But then I went into debug mode. And I saw this:
So as you can see, the variable doesn't have the access token. The other data comes from app.config file.
I am quite certain the keys passed in are correct.
Then the code throws the famous invalid_grand error. In my case, I believe that's because the access token is not being generated. I'm new to AdWords and ASP.NET, so I probably missed something, but I have no idea what.
I used the
docs,
Code Structure instructions, and
code examples to put it all together.
I had my configuration wrong. I had to recreate all the credentials using incognito window. If you have any issues just open a thread here: https://groups.google.com/forum/#!forum/adwords-api

Create ActivityParty in CRM without early bound Entities

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;
}

Silverlight -CRM2011 : The Currency Cannot Be null

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.

Display sharepoint people/group field list's value in people editor

i want to display value of sharepoint people/group value in people editor(web part) when the page is loaded. This is the code that i use to get the value displayed in web part
if(SPContext .Current .ListItem .ID >= 1)
using (SPSite site = new SPSite("sitename"))
{
using (SPWeb web = site.OpenWeb())
{
var id = SPContext.Current.ListItem.ID;
SPList lists = web.Lists["DDClist"];
SPListItem item = lists.GetItemById(id);
{
string test = Convert.ToString(item["Project No"]);
tb_pno.Text = test;
string test2 = Convert.ToString(item["Project Title"]);
tb_pname.Text = test2;
string test3 = Convert.ToString(item["DDC No"]);
tb_idcno.Text = test3;
string test4 = Convert.ToString(item["Date In"]);
TextBox3.Text = test4;
}
}
}
is there a way to do the same thing with people editor?
This is all a little tricky; when I've had to do it before, I use the following to get SPUser object out of a field:
SPUser singleUser = new SPFieldUserValue(
item.Web, item["Single User"] as string).User;
SPUser[] multipleUsers = ((SPFieldUserValueCollection)item["MultipleUsers"])
.Cast<SPFieldUserValue>().Select(f => f.User);
I'm not sure why one user is stored as a string, but multiple users are stored as a specific object; it may also not be consistent in this so you might have to debug a bit and see what the type in your field is.
Once you have these SPUsers, you can populate your PeopleEditor control
using the account names as follows (quite long-winded):
ArrayList entityArrayList = new ArrayList();
foreach(SPUser user in multipleUsers) // or just once for a single user
{
PickerEntity entity = new PickerEntity;
entity.Key = user.LoginName;
entity = peMyPeople.ValidateEntity(entity);
entityArrayList.Add(entity);
}
peMyPeople.UpdateEntities(entityArrayList);
This also performs validation of the users of some kind.
If the page this control appears on may be posted-back, you need the following to be done during the postback in order for the values to be correctly roundtripped; I put it in PreRender but it could happen elsewhere in the lifecycle:
protected override void OnPreRender(EventArgs e)
{
if (IsPostBack)
{
var csa = peMyPeople.CommaSeparatedAccounts;
csa = peMyPeople.CommaSeparatedAccounts;
}
}
If you want to check any error messages that the control generates for you (if the user input is incorrect), you need to have done this switchout already:
var csa = usrBankSponsor.CommaSeparatedAccounts;
csa = usrOtherBankParties.CommaSeparatedAccounts;
//ErrorMessage is incorrect if you haven't done the above
if (!String.IsNullOrEmpty(usrBankSponsor.ErrorMessage))
{
...
}
It's really not very nice and there may be a much better way of handling it, but this is the result of my experience so far so hopefully it will save you some time.

"Could not find transactional storage type" error with embedded RavenDB

I was able to successfully run a simple test for RavenDB based on the code found at: http://ravendb.net/tutorials/hello-world
Next I tried to run it in an Embedded Manner, but I keep on getting the following error:
Message: Could not find transactional storage type: Raven.Storage.Esent.TransactionalStorage, Raven.Storage.Esent
StackTrace: at Raven.Database.Config.InMemoryRavenConfiguration.CreateTransactionalStorage(Action notifyAboutWork) in c:\Builds\raven\Raven.Database\Config\InMemoryRavenConfiguration.cs:line 272
at Raven.Database.DocumentDatabase..ctor(InMemoryRavenConfiguration configuration) in c:\Builds\raven\Raven.Database\DocumentDatabase.cs:line 109
at Raven.Client.Client.EmbeddableDocumentStore.InitializeInternal() in c:\Builds\raven\Raven.Client.Embedded\EmbeddableDocumentStore.cs:line 130
at Raven.Client.Document.DocumentStore.Initialize() in c:\Builds\raven\Raven.Client.Lightweight\Document\DocumentStore.cs:line 388
at Tests.RavenEmbedded.RavenDB..ctor() in C:\Users\Pranav\Documents\Projects\Repositories-Clone\Common-clone\Tests\RavenDB.cs:line 114
at Tests.TestRavenDB.Basics() in C:\Users\Pranav\Documents\Projects\Repositories-Clone\Common-clone\Tests\RavenDB.cs:line 170
Setup:
Target framework is .NET Framework 4
I added the following References to my project:
\RavenDB-Build-309\EmbeddedClient\Raven.Client.Embedded.dll
\RavenDB-Build-309\Client\Raven.Client.Lightweight.dll
\RavenDB-Build-309\EmbeddedClient\Raven.Storage.Esent.dll
\RavenDB-Build-309\EmbeddedClient\Raven.Storage.Managed.dll
The code is:
namespace Tests.RavenEmbedded
{
using Raven.Client.Client;
using Raven.Client.Document;
using Raven.Storage.Esent;
using Raven.Storage.Managed;
using Tests.RavenData;
class RavenDB
{
public RavenDB()
{
// EmbeddableDocumentStore store = new EmbeddableDocumentStore { DataDirectory = #"C:\Temp\RavenData" };
//Raven.Storage.Esent.TransactionalStorage
var store = new EmbeddableDocumentStore { DataDirectory = #"C:\Temp\RavenData" };
store.Initialize();
#region Write Data
using (var session = store.OpenSession())
{
var product = new Product
{
Cost = 3.99m,
Name = "Milk",
};
session.Store(product);
session.SaveChanges();
session.Store(new Order
{
Customer = "customers/ayende",
OrderLines =
{
new OrderLine
{
ProductId = product.Id,
Quantity = 3
},
}
});
session.SaveChanges();
}
#endregion
#region Read Data
using (var session = store.OpenSession())
{
var order = session.Load("orders/1");
Debug.Print("Customer: {0}", order.Customer);
foreach (var orderLine in order.OrderLines)
{
Debug.Print("Product: {0} x {1}", orderLine.ProductId, orderLine.Quantity);
}
session.SaveChanges();
}
#endregion
}
}
}
namespace Tests
{
public class TestRavenDB
{
public static void Basics()
{
try
{
//var db = new RavenClientServer.RavenDB();
var db = new RavenEmbedded.RavenDB();
}
catch (Exception ex)
{
Debug.Print("Message: {0} ",ex.Message);
Debug.Print("StackTrace: {0} ",ex.StackTrace);
}
}
}
}
I have tried searching for this for a few days and tried a few different variations too. I am not sure what's going on.
Thanks to Ayende Rahien on groups.google.com/group/ravendb/topics.
The solution was to add "Raven.Storage.Esent" reference to the main project. It's an issue with Visual Studio and indirect references.
Thanks #Derek for suggesting that I post there.
-- Pranav
You need to add a reference to Raven.Storage.Esent.dll

Categories