I have a CRM function that returns the attribute types of all attributes in a entity. My problem is all though this same method has worked in the past it's now throwing this error regardless of the entity I pass in to it.
There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:ExecuteResult
Here is my code, I'm passing in the "account" entity.
public string GetFieldType(IOrganizationService svc, string entity, string fieldName)
{
RetrieveEntityRequest request = new RetrieveEntityRequest()
{
EntityFilters = EntityFilters.Attributes,
LogicalName = entity
};
RetrieveEntityResponse response = (RetrieveEntityResponse)svc.Execute(request);
string type = "";
foreach (AttributeMetadata attribute in response.EntityMetadata.Attributes)
{
if (attribute.LogicalName == fieldName)
{ type = attribute.AttributeType.ToString(); }
}
return type;
}
If the code was working before, and it's not now, odds are that it is not a problem with your code. Most likely your svc.Execute is failing. Have you changed how your IOrganizationService is being created? Are you running as a user with rights to query the CRM instance? If all of these things check out, then try turning on server side tracing through the diagnostic tool.
Related
We're trying to use the REST API of Administration Service to manage the Configuration Manager
(What is the administration service in Configuration Manager?)
We have successfully queried entities of different types and executed some custom static methods (i.e. MoveMembers Method on SMS_ObjectContainerItem). It's all mostly blind shots as there is barely any documentation, but those basic functionalities seem to work fine.
Now we have hit the wall, trying to add collection rules to a SMS_Collection (existing or new). This was normally done calling the AddMembershipRule on the instance itself, that was previously fetched by e.g. WqlConnectionManager or some other proxy. However, this is clearly a no-go on a plain object fetched from the REST service.
We have tried to use the wmi OData service (by a generated proxy) as it clearly offers similar functionality, but this ends up with a "Not supported exception":
var savedCollection = Proxy.SMS_Collection.Where(c => c.CollectionID == result.CollectionID).FirstOrDefault();
savedCollection.AddMembershipRule(inclusionRule);
Proxy.UpdateObject(savedCollection);
Proxy.SaveChanges(); //EXCEPTION
I have tried running POST request in numerous ways, using urls like:
SMS_Collection.AddMembershipRule?CollectionID=DV000037 -> 404
SMS_Collection/DV000037/AddMembershipRule -> 404
SMS_Collection.DV000037.AddMembershipRule -> 404
SMS_Collection/DV000037.AddMembershipRule -> treated it as post to SMS_Collection/DV000037, and therefore triggers an update
or just
SMS_Collection.AddMembershipRule with collectionID as param
As for the request body I've used (or just the AddCollectionMembershipRuleRequestRule):
public class AddCollectionMembershipRuleRequest
{
public AddCollectionMembershipRuleRequestRule CollectionRule { get; set; }
}
public class AddCollectionMembershipRuleRequestRule
{
public string RuleName { get; set; }
public string IncludeCollectionID { get; set; }
}
I have also tried to Post an existing or new collection, with CollectionRules prefilled, but this ends up with an exception complaining about IncludeCollectionID not being part of CollectionRule (base class) - looks like validation being too strict and not dealing well with the inheritance.
var collectionRequest = new ECMCollectionCreationRequest()
{
Name = collectionName,
CollectionType = 2,
RefreshType = 4,
LimitToCollectionID = DefaultLimitingCollectionID,
CollectionRules = new List<SMS_CollectionRule>()
{
new SMS_CollectionRuleIncludeCollection()
{
RuleName = "MembershipRule",
IncludeCollectionID = "DV100020"
}
}
};
Stil, no luck with any of those. Do you have any idea if such a scenario (modification of CollectionRules) is even supported with the Rest /OData service? If so, what would be the right way to achieve so?
It looks like this part is simply not supported at the moment. Looking at the code it seems that the service is not interpreting the arguments properly and therefore causing validation issues.
However, the same can be achieved, in a bit less clean and structured way, using ManagementScope and ManagementObject
var scope = new ManagementScope(siteAddress);
scope.Connect();
using (ManagementObject collection = new ManagementObject(scope, new ManagementPath($"SMS_Collection.CollectionID='{collectionID}'"), new ObjectGetOptions()))
{
if (collection == null)
throw new Exception($"Unable to find collection with ID '{collectionID}'");
collection.Get();
using (ManagementBaseObject inParams = collection.GetMethodParameters("AddMembershipRule"))
using (ManagementClass ruleClass = new ManagementClass(scope, new ManagementPath("SMS_CollectionRuleDirect"), new ObjectGetOptions()))
using (ManagementObject rule = ruleClass.CreateInstance())
{
rule["ResourceClassName"] = "SMS_R_System";
rule["ResourceID"] = ecmResourceID;
rule["RuleName"] = machineName;
inParams["collectionRule"] = rule;
collection.InvokeMethod("AddMembershipRule", inParams, null);
}
}
One can add and remove all the other rule types in similar way.
Another alternative is of course to use PowerShell. Sill, I hope that with one of the next iterations of the Administration Service, support of those methods will be added.
Similarly, there seems to be no way to add/remove application or package and import/export them, using the admin services or even in the way mentioned above.
$Rule="{'collectionRule':{`"#odata.type`": `"#AdminService.SMS_CollectionRuleDirect`", `"ResourceClassName`": `"SMS_R_System`", `"ResourceID`": $DeviceID,`"RuleName`": `"$MachineName`"}}"
$RuleCreated = (Invoke-RestMethod -Method Post -Uri "https://$($CMProvider)/AdminService/wmi/SMS_Collection('$CollectionID')/AdminService.AddMembershipRule" -Body $Rule -ContentType 'application/json' -Credential $Cred)
I've had to jump in to a complex project and am making unit tests for a particular repository in a WEB API service. The database CRUD is handled by the Entity Framework.
private class IntegrationScope : AutoRollbackTransactionTestScope<DocumentRepository>
{
public IntegrationScope()
{
DependencyResolverMock = MockDependencyResolverFor<IResourceUrlBuilder, ResourceUrlBuilder>(new ResourceUrlBuilder());
LoggingProviderMock = new Mock<ILoggingProvider>();
// use real document micro service AutoMapper & Unity configuration
AutoMapperConfig.RegisterMaps(Mapper.Configuration, DependencyResolverMock);
UnityConfig.RegisterTypes(TestScopeContainer);
//Set the DomainId
TestId = Guid.NewGuid();
TestDocument = BuildDocument(TestId);
// get the real object
DocumentStoreDbContext = TestScopeContainer.Resolve<IDocumentDbContext>();
InstanceUnderTest = new DocumentRepository(DocumentStoreDbContext);
}
public static Web.Service.DocumentStore.Domain.Document BuildDocument(Guid documentId)
{
// Invoke GetBytes method.
byte[] array = Encoding.ASCII.GetBytes("Byte Repository Test");
return Builder<Web.Service.DocumentStore.Domain.Document>
.CreateNew()
.With(t => t.Id = documentId)
.With(t => t.Data = array)
.Build();
}
When it gets to the DocumentStoreDbContext line, there is no compilation error but i get a run-time error saying that the object name is invalid:
After doing some research this error appears to be because the database/table doesn't exist, however I can see that it does exist:
What am I doing wrong and how do I fix it?
InnerException displays the table name as DocumentStore.Documents, but your SSMS screenshot shows dbo.Documents.
Could this be the cause?
I am building a C#/.NET 4.5 client for a REST API using JSON.NET. The API supports partial updates; therefore the presence or lack of an attribute in the json on an update has meaning. If the attribute is in the json, the server will set the value accordingly; the the attribute is not passed the server will not update it. This also applies to null values. I have .NET classes for each model; with properties for each JSON attribute (pretty standard).
As an example lets say I have this account object (name, notes) that already exists on the server:
{
'name':'craig',
'notes:'these are notes'
}
If I pass in this json for an update it will update the name, but will leave the notes set to 'these are notes':
var account = api.GetAccount();
account.Name = "bob";
api.UpdateAccount(account);
{
'name':'bob'
}
If I pass this json in for an update, it will set the name and the notes to null on the server:
var account = api.GetAccount();
account.Name = "bob";
account.Notes = null;
api.UpdateAccount(account);
{
'name':'bob',
'notes':null
}
All good up to this point.
My question is how to you get JSON.NET to play along nicely with this. JSON.NET allows control the NullValueHandling which basically says if null values should be serialized or not. However that is not enough in this case. I need to be able to determine if the calling code explicitly set the value to null. Is there a recommended way to handle this?
Ive tried using a Dictionary internal to my models to store the attributes to be serialized via JSON. This allows me to tell if the attribute has been set to anything (including null) via the presence of the key in the dictionary. I found that this approach has some difficulties and I end up rewriting a lot of code that comes standard to JSON.NET (type serialization, generics, nullables, enums...).
Note: I do realize the above example is a bit contrived. In reality the account object returned back from the server would have both name and notes populated, and that when the update happened it would send both back.
The other case where this applies is during creating objects and handling server generated default. For example, lets say the server defaults the account's notes to 'put notes here' when the account is created. If I pass in the Notes attribute with a null value, the server will think the client wants to set it to null. The reality though is the client is not trying to set the Notes to null, and in this case would want the default to be set.
var account = new Account();
account.Name = "bob";
api.CreateAccount(account);
{
'name':'bob',
'notes':null
}
Im always impressed by JSON.NET...
Here is what I ended up with. I used a combination of a ContractResolver, the ShouldSerialize predicate and the NullValueHandling property. This link was very useful. The properties are stored in a Dictionary in a base class ApiModel; that code is straightforward.
Account Model
[JsonProperty("name")]
public string Name
{
get { return this.GetAttributeValue<string>("name"); }
set { this.SetAttributeValue<string>("name", value); }
}
Json Serialization
ApiModel.JsonSerializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
ApiModel.JsonSerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;
ApiModel.JsonSerializerSettings.ContractResolver = ApiModel.JsonContractResolver;
internal class ApiModelContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override Newtonsoft.Json.Serialization.JsonProperty CreateProperty(System.Reflection.MemberInfo member, Newtonsoft.Json.MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize =
instance =>
{
var apiModel = instance as ApiModel;
var hasAttribute = apiModel.HasAttribute(property.PropertyName);
property.NullValueHandling = hasAttribute ? Newtonsoft.Json.NullValueHandling.Include : Newtonsoft.Json.NullValueHandling.Ignore;
return hasAttribute;
};
return property;
}
}
I'm coming from a SQL Server background, and experimenting with Redis in .NET using ServiceStack. I don't mean for Redis to be a full replacement for SQL Server, but I just wanted to get a basic idea of how to use it so I could see where we might make good use of it.
I'm struggling with what I think is a pretty basic issue. We have a list of items that are maintained in a couple of different data stores. For the sake of simplicity, assume the definition of the item is basic: an integer id and a string name. I'm trying to do the following:
Store an item
Retrieve an item if we only know its id
Overwrite an existing item if we only know its id
Show all the items for that specific type
And here's some of the code I've put together:
public class DocumentRepositoryRedis
{
private static string DOCUMENT_ID_KEY_BASE = "document::id::";
public IQueryable<Document> GetAllDocuments()
{
IEnumerable<Document> documentsFromRedis;
using (var documents = new RedisClient("localhost").As<Document>())
{
documentsFromRedis = documents.GetAll();
}
return documentsFromRedis.AsQueryable();
}
public Document GetDocument(int id)
{
Document document = null;
using (var redisDocuments = new RedisClient("localhost").As<Document>())
{
var documentKey = GetKeyByID(document.ID);
if (documentKey != null)
document = redisDocuments.GetValue(documentKey);
}
return document;
}
public void SaveDocument(Document document)
{
using (var redisDocuments = new RedisClient("localhost").As<Document>())
{
var documentKey = GetKeyByID(document.ID);
redisDocuments.SetEntry(documentKey, document);
}
}
private string GetKeyByID(int id)
{
return DOCUMENT_ID_KEY_BASE + id.ToString();
}
}
It all seems to work - except for GetAllDocuments. That's returning 0 documents, regardless of how many documents I have stored. What am I doing wrong?
The typed Redis client also gives you access to the non-typed methods - since Redis ultimately doesn't know or care about your object types. So when you use the client.SetEntry() method, it bypasses some of the typed client's features and just stores the object by a key. You'll want to use the client.Store method since it goes ahead and creates a SET in Redis with all the object IDs related to your type. This SET is important because it's what the GetAll method relies on to serve back all the objects to you. The client.Store method does infer the ID automatically so you'll want to play around with it.
You'd change your GetDocument(int id) and SaveDocument(Document document) methods to use the client.GetById(string id) method, and you'd use client.Store(T value) method. You won't need your GetKeyByID() method anymore. I believe your Document object will need an "Id" property for the typed client to infer your object ID.
I added stored proc via Entity Framework 4.0 Model.
Case1:
My stored proc returns set of integers(1,2,3,4...) and I chose Int32 as the Return Collection of EntityFramework Model and the generated code now is:
public ObjectResult<Nullable<global::System.Int32>> CopyAssembly(Nullable<global::System.Int32> assemblyId)
{
ObjectParameter assemblyIdParameter;
if (assemblyId.HasValue)
{
assemblyIdParameter = new ObjectParameter("assemblyId", assemblyId);
}
else
{
assemblyIdParameter = new ObjectParameter("assemblyId", typeof(global::System.Int32));
}
return base.ExecuteFunction<Nullable<global::System.Int32>>("CopyAssembly", assemblyIdParameter);
}
When I run it var res = (new SPEntities()).CopyAssembly(1); I get no error. The set of integers is returned as expected. I even can see in SQL Profiler that request is sent.
Case2:
I changed my stored proc so it returns now set of rows(col1,col2). This table maps to EntityA. In Entity Framework Model I chose EntityA as Return Collection. The generated code for this now is:
public ObjectResult<EntityA> CopyAssembly(Nullable<global::System.Int32> assemblyId)
{
ObjectParameter assemblyIdParameter;
if (assemblyId.HasValue)
{
assemblyIdParameter = new ObjectParameter("assemblyId", assemblyId);
}
else
{
assemblyIdParameter = new ObjectParameter("assemblyId", typeof(global::System.Int32));
}
return base.ExecuteFunction<EntityA>("CopyAssembly", assemblyIdParameter);
}
When I run it var res = (new SPEntities()).CopyAssembly(1); I get always following error: An item with the same key has already been added. I even DON'T see the request in SQL Profiler.
Why CASE1 works and CASE2 doesn't? Why in CASE2 even that no request is sent to SQL(I don't see it in profiler) I STILL get error? It will be more logically to get error after the request is sent to SQL
Thank you