c# - 检索CRM 4 C#中的自定义实体

标签 c# dynamics-crm-4 entities

在我们的CRM环境中,用户与可用时间实体具有1:N的关系,该实体代表其一周中的实际可用时间。我正在寻找一种使用C#的方法来检索特定团队本周用户的所有可用时间。

我是CRM开发的新手,但我环顾四周,但似乎有很多方法可以做到这一点,但我不确定哪种方法最合适。

语言为C#,CRM版本为MS CRM 4.0

最佳答案

我将介绍这3位内容:常规方法,代码本身,然后是代码上的一些注释(对代码进行注释以引起对某些事情的注意,尽管其中有些会在代码外进行进一步的解释)。

一般的做法

如您所见,有两种处理方法,但是对于通过Web服务与CRM交互的外部应用程序,它可以归结为3个主要选项:


使用从在Web服务调用中添加Web引用中获得的强类型方法来检索您的自定义实体(我认为您在上一个问题中提到您看到了方法的爆炸式增长...当您有很多自定义实体时,情况会变得更糟)
在网络服务调用中使用DynamicEntity
提取XML


如果您的系统非常简单,通常可以不用(1),但我建议(2)或(3)中的任何一个。使用(2)意味着,当您进入插件或工作流程序集时,您真的只需要记住一些Web服务方法及其优点,因为这些概念可以很好地进行。 (3)如果您知道FetchXML并可以形成适当的查询,则很好。

我通常使用(2)来处理这些事情,这是很不错的方法,而且就像我说的那样,您的代码将很容易转换为插件或工作流程序集。 FetchXML具有很好的传递性,但是我从来都不擅长构成查询-稍后我将介绍一些技术,但让我们继续(2)。

另外,如果使用DynamicEntity,则无需刷新Web引用,因为它的使用方式及其Property对象数组(基本上,您会获得灵活性,但会牺牲强类型输入),如您所见在代码中。如果您选择(1),则会对自定义实体进行强类型输入,但您必须根据人们对实体进行更改的节奏不断刷新WebReference。

代码

这是在一个小型控制台应用程序中,在该应用程序中,我向CRM服务中添加了WebReference,并进行了一些调用来模拟您的方案。该代码应传递给其他应用程序,例如网络应用程序。我已尝试对其进行评论,因此在进入下一部分之前可能值得通读。

(注意。我并不是说这是世界上最好的代码,但是它确实可行,应该让您入门)

(NB2。我犯了一个错误,即为Web参考CrmService调用我的命名空间-请不要犯与我相同的错误。...)

static void Main(string[] args)
{
        CrmService.CrmService svc = new CrmService.CrmService();
        svc.CrmAuthenticationTokenValue = GetToken();
        svc.UseDefaultCredentials = true;

        #region 1 - Retrieve users in team
        RetrieveMembersTeamRequest teamMembersReq = new RetrieveMembersTeamRequest()
        {
            EntityId = new Guid("D56E0E83-2198-E211-9900-080027BBBE99"), //You'll need the team GUID
            ReturnDynamicEntities = true
        };

        ColumnSet teamMembersReqColumnSet = new ColumnSet();
        teamMembersReqColumnSet.Attributes = new string[] { "systemuserid", "domainname" };

        teamMembersReq.MemberColumnSet = teamMembersReqColumnSet; //Don't use: teamMembersReq.MemberColumnSet = new AllColumns()

        List<Guid> userIdList = new List<Guid>();
        RetrieveMembersTeamResponse teamMembersResp = svc.Execute(teamMembersReq) as RetrieveMembersTeamResponse;
        if (teamMembersResp != null)
        {
            BusinessEntity[] usersInTeamAsBusinessEntity = teamMembersResp.BusinessEntityCollection.BusinessEntities;
            List<DynamicEntity> usersInTeamAsDynEntity = usersInTeamAsBusinessEntity.Select(be => be as DynamicEntity).ToList(); //BusinessEntity not too useful, cast to DynamicEntity

            foreach (DynamicEntity de in usersInTeamAsDynEntity)
            {
                Property userIdProp = de.Properties.Where(p => p.Name == "systemuserid").FirstOrDefault();
                Property domainNameProp = de.Properties.Where(p => p.Name == "domainname").FirstOrDefault();

                if (userIdProp != null)
                {
                    KeyProperty userIdKeyProp = userIdProp as KeyProperty; //Because it is the unique identifier of the entity
                    userIdList.Add(userIdKeyProp.Value.Value); //Chuck in a list for use later
                    Console.Write("Key: " + userIdKeyProp.Value.Value.ToString());
                }

                if (domainNameProp != null)
                {
                    StringProperty domainNameStringProp = domainNameProp as StringProperty; //Because its data type is varchar
                    Console.Write("| Domain Name: " + domainNameStringProp.Value);
                }

                Console.WriteLine();
            }
        }
        #endregion

        /*
         * For this example I have created a dummy entity called new_availablehours that is in a 1:N relationship with use (i.e. 1 user, many new_available hours). 
         * The test attributes are :
         *      - the relationship attribute is called new_userid...this obviously links across to the GUID from systemuser
         *      - there is an int data type attribute called new_hours
         *      - there is a datetime attribute called new_availabilityday
         */
        #region Retrieve From 1:N
        RetrieveMultipleRequest req = new RetrieveMultipleRequest();
        req.ReturnDynamicEntities = true; //Because we love DynamicEntity

        //QueryExpression says what entity to retrieve from, what columns we want back and what criteria we use for selection
        QueryExpression qe = new QueryExpression();
        qe.EntityName = "new_availablehours"; //the entity on the many side of the 1:N which we want to get data from

        qe.ColumnSet = new AllColumns(); //Don't do this in real life, limit it like we did when retrieving team members

        /*
         * In this case we have 1 x Filter Expression which combines multiple Condition Operators
         * Condition Operators are evaluated together using the FilterExpression object's FilterOperator property (which is either AND or OR)
         * 
         * So if we use AND all conditions need to be true and if we use OR then at least one of the conditions provided needs to be true
         * 
         */
        FilterExpression fe = new FilterExpression();
        fe.FilterOperator = LogicalOperator.And;

        ConditionExpression userCondition = new ConditionExpression();
        userCondition.AttributeName = "new_userid"; //The attribute of qe.EntityName which we want to test against
        userCondition.Operator = ConditionOperator.In; //Because we got a list of users previously, the appropriate check is to get records where new_userid is in the list of valid ones we generated earlier
        userCondition.Values = userIdList.Select(s => s.ToString()).ToArray(); //Flip the GUID's to strings (seems that CRM likes that) then set them as the values we want to evaulate
        //OK - so now we have this userCondition where valid records have their new_userid value in a collection of ID's we specify

        ConditionExpression dateWeekBound = new ConditionExpression();
        dateWeekBound.AttributeName = "new_availabilityday";
        dateWeekBound.Operator = ConditionOperator.ThisWeek; //ConditionOperator has a whole bunch of convenience operators to deal with dates (e.g. this week, last X days etc) - check them out as they are very handy

        /*
         * As an aside, if we didn't want to use the convenience operator (or if none was available) we would have to create a ConditionExpression like:
         * 
         * ConditionExpression dateLowerBound = new ConditionExpression();
         * dateLowerBound.AttributeName = "new_availabilityday";
         * dateLowerBound.Operator = ConditionOperator.OnOrAfter;
         * dateLowerBound.Values = new object[] { <Your DateTime object here> };
         * 
         * And a corresponding one for the upper bound using ConditionOperator.OnOrBefore
         * 
         * Another alternative is to use ConditionOperator.Between. This is flexible for any sort of data, but the format of the Values array will be something like:
         *      ce.Values = new object[] { <lower bound>, <upper bound> };
         */

        fe.Conditions = new ConditionExpression[] { userCondition, dateWeekBound }; //Add the conditions to the filter
        qe.Criteria = fe; //Tell the query what our filters are
        req.Query = qe; //Tell the request the query we want to use

        RetrieveMultipleResponse resp = svc.Execute(req) as RetrieveMultipleResponse;
        if (resp != null)
        {
            BusinessEntity[] rawResults = resp.BusinessEntityCollection.BusinessEntities;
            List<DynamicEntity> castedResults = rawResults.Select(r => r as DynamicEntity).ToList();

            foreach (DynamicEntity result in castedResults)
            {
                Property user = result.Properties.Where(p => p.Name == "new_userid").FirstOrDefault();
                Property hours = result.Properties.Where(p => p.Name == "new_hours").FirstOrDefault();

                if (user != null)
                {
                    LookupProperty relationshipProperty = user as LookupProperty; //Important - the relationship attribute casts to a LookupProperty
                    Console.Write(relationshipProperty.Value.Value.ToString() + ", ");
                }

                if (hours != null)
                {
                    CrmNumberProperty hoursAsCrmNumber = hours as CrmNumberProperty; //We also have CrmFloatProperty, CrmDecimalProperty etc if the attribute was of those data types
                    Console.Write(hoursAsCrmNumber.Value.Value);
                }

                Console.WriteLine();
            }
        }
        #endregion

        Console.ReadLine();
    }

    static CrmAuthenticationToken GetToken()
    {
        CrmAuthenticationToken token = new CrmAuthenticationToken();
        token.AuthenticationType = 0; //Active Directory
        token.OrganizationName = "DevCRM";

        return token;
    }


所以..那是什么?

我不打算一吹而过,而是介绍以下要点:


使用服务时的关键方法是Execute()方法,我们向它传递一个请求对象,然后返回一个响应对象。请求将全部为类<Operation>Request的对象,响应将为类<Operation>Response的对象。
通常,您希望使用DynamicEntity-<Operation>Request对象通常会公开一个名为ReturnDynamicEntities的属性,您应该将其设置为true
大多数<Operation>Request对象都有一个ColumnSet属性,您可以在其中指定要返回的属性。指定AllColumns()通常是不好的做法,相反,您应明确说明要返回的数据。属性需要与CRM中的名称(以<prefix>_<field name>形式)匹配,且所有名称均小写
将用户吸引到团队中并不是很有趣,因为这是CRM中的预定义操作,没什么特别的...在这种情况下,SDK是您的朋友,因为它将向您展示这些工作原理
检索一堆自定义实体是一个更有趣的用例,我们通常可以通过使用RetrieveMultipleRequestRetrieveMultipleResponse方法将其删除(如果只需要一条记录,则可以只使用RetrieveRequestRetrieveResponse。 。,但您需要知道要查找的内容的GUID,以将其输入RetreiveRequest对象。
对于RetrieveMultipleRequest,我们向它提供一个查询(QueryExpression),该查询指出要获取的实体(EntityName),要返回的实体的属性(ColumnSet)和过滤器()用于选择我们想要的实际记录
专注于CriteriaQueryExpressionFilterExpression的用法。要知道的重要一件事是您在ConditionExpression中可以使用哪些运算符-我试图在代码中调用一些运算符,但是SDK还是您最好的朋友,可以了解可用的运算符
我没有涉及的是更复杂的过滤,例如(x OR y)AND z。有一个很好的例子here。这是使用ConditionExpressionFilterExpression的另一种方式
请注意,ConditionExpression包含一个RetrieveMultipleResponse数组。 BusinessEntity本身是没有用的,因此我们将其转换为BusinessEntity列表-LINQ在这里确实是您的朋友,并且对于CRM方面的很多混乱,LINQ派上了用场
注意我们如何检查属性-DynamicEntity,然后检查它是否为de.Properties.Where(p => p.Name == "systemuserid").FirstOrDefault();。这是因为,如果在CRM中记录的属性为NULL,则不会从服务调用中返回该记录-仅仅是因为您在NULL中请求属性,请不要自动假定该属性在那里(除非您已将其设置为CRM中的必填项-然后可能就可以了)...对其进行测试,您的应用程序将不那么脆弱。
ColumnSet类本身的值是有限的-要真正使用某个属性,必须将其强制转换为实际的对象。我一直在加紧努力,但SDK会告诉您类型是什么,但过一会儿就开始变得自然起来,例如记录的GUID在Property中,整数在KeyProperty中,浮点数在CrmNumberProperty中,字符串是CrmFloatProperty等。请注意弱类型(我在前面提到过)必须通过以下方式获取属性名称,转换为适当的值等


其他外卖


通常,您将不得不非常热心于服务调用-从我看到的经验来看,在开发CRM时这是很正常的(尽管我只能谈谈自己的经验)
在代码编写过程中要真正具有防御性,这一点很重要-检查属性是否存在,检查是否要转换为正确的类型,等等。
如果必须捕获一个异常,它将是一个StringProperty并且通常需要的信息将位于SoapException属性中-记住这一点非常重要,否则您将查看该异常并认为它不会告诉您整堆
请咨询CRM中的自定义项,以找出关系属性,数据类型等的名称。我希望在进行开发时可以打开我需要打开的实体的自定义项窗口,以便于参考。
Detail确实很强大,但很有趣。如果您擅长于此,那么您将获得很多不错的成绩-this之类的工具很有用。同样,这是一个方便的技巧-如果您可以通过CRM UI将所需的内容(或所需的示例)构建为高级查找,则可以使用此trick获取使用的FetchXML。可能需要调整GUID之类的东西,但是如果您想在代码中使用FetchXML(因为大部分查询是为您编写的),它会为您提供一个构建块。
根据您的部署环境,您可能必须弄乱使用什么凭据,是否通过代理等...典型的Web参考资料。值得一提的是,CRM不能幸免于此-我在这里没有任何真正的建议,只是一条注释,因为它过去曾给我带来一些“乐趣”

关于c# - 检索CRM 4 C#中的自定义实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15575502/

相关文章:

workflow - 为什么使用插件注册工具注册自定义工作流程后需要重新启动 CRM 服务器才能正常工作

cocoa - 核心数据: can NSFetchedResultsController fetch two different entities?

c# - 需要 C# 函数将灰度 TIFF 转换为黑白(单色/1BPP)TIFF

C# 亚马逊产品广告 API

dynamics-crm - 在 MS CRM 4 中使用 JavaScript 使字段对用户不可用

java - 使用 NetBeans JPA 额外的 "entityPK"类从数据库生成实体?

javascript - 使用 JavaScript 正则表达式将数字 HTML 实体替换为其实际字符

c# - 匿名类型的 IEqualityComparer

c# - 如何通过从另一个表单调用它来将数据库中的数据显示到另一个表单

dynamics-crm-2011 - 如何获取crm 2011的用户名?