首页 文章

OData v4 - 选择实体中的特定字段

提问于
浏览
0

我正在使用ODAP v4和Web API 2.2 .

我有一个名为“Person”的实体,其复合键为“FirstName”和“LastName” . 看起来像这样:

public class Person {
    public string FirstName {get; set;}
    public string LastName {get; set;}
    public double Age {get; set;}
}

为了支持复合键,我在默认约定之上添加了一个uri约定,它看起来像这样:

public class CompositeKeyRoutingConvention : EntityRoutingConvention
{
    public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
    {
        var action = base.SelectAction(odataPath, controllerContext, actionMap);

        if (action != null)
        {
            var routeValues = controllerContext.RouteData.Values;
            if (routeValues.ContainsKey(ODataRouteConstants.Key))
            {
                var keyRaw = (string)routeValues[ODataRouteConstants.Key];

                var compoundKeyPairs = keyRaw.Split(',');

                if (!compoundKeyPairs.Any())
                {
                    return action;
                }

                foreach (var compoundKeyPair in compoundKeyPairs)
                {
                    var pair = compoundKeyPair.Split('=');
                    if (pair.Length != 2)
                    {
                        continue;
                    }
                    var keyName = pair[0].Trim();
                    var keyValue = pair[1].Trim();

                    routeValues.Add(keyName, keyValue);
                }
            }
        }

        return action;
    }

我的呼叫代码试图访问这样一个人的年龄:

http://localhost:46028/Person(firstName='Blah',LastName='Blu')/Age

我收到此错误:

{“error”:{“code”:“”,“message”:“找不到与请求URI匹配的HTTP资源'http://:46028 / Person(firstName ='Blah',LastName ='Blu') / Age' . “,”innererror“:{”message“:”找不到路由约定来为OData路径选择一个动作,模板'〜/ entityset / key / property' . “,”type“:”“, “堆栈跟踪”:”” } } }

我的控制器有两种方法:

public IQueryable<Person> Get()
    {
        return _db.People;
    }

    public Person Get([FromODataUri] string firstName, [FromODataUri] string lastName)
    {
        var person = _db.People
            .FirstOrDefault(x => x.FirstName == firstName && x.LastName== lastName);

        if (person == null)
        {
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
        }

        return person;
    }

1 回答

  • 0

    原来有一个简单的解决方案 . Web API不能很好地支持复合键,它们有一个PropertyRoutingConvention,它正是我正在寻找的,除了它不适用于复合键 .

    通过创建下面给出的“CompositeKeyPropertyRoutingConvention”修复它:

    public class CompositeKeyPropertyRoutingConvention : PropertyRoutingConvention
    {
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            var action = base.SelectAction(odataPath, controllerContext, actionMap);
    
            return new CompositeKeyRoutingBehaviour().SelectAction(action, odataPath, controllerContext, actionMap);
        }
    }
    
    public class CompositeKeyRoutingBehaviour
    {
        public string SelectAction(string action, ODataPath odataPath, HttpControllerContext controllerContext,
                                   ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (action != null)
            {
                var routeValues = odataPath.Segments.FirstOrDefault(x => x.SegmentKind == ODataRouteConstants.Key);
    
                if (routeValues != null)
                {
                    var keyRaw = routeValues.ToString();
    
                    var formatter = new KeyValueFormatter();
    
                    var keyPairs = formatter.FormatRawKey(keyRaw);
    
                    if (!keyPairs.Any())
                    {
                        return action;
                    }
    
                    foreach (var pair in keyPairs)
                    {
                        controllerContext.RouteData.Values.Add(pair.Key, pair.Value);
                    }
                }
            }
    
            return action;
        }
    }
    
    public class KeyValueFormatter
    {
        public IDictionary<string, string> FormatRawKey(string rawKey)
        {
            var formattedKeys = new Dictionary<string, string>();
    
            if (string.IsNullOrWhiteSpace(rawKey))
                return formattedKeys;
    
            var keyBuilder = new StringBuilder();
            var valueBuilder = new StringBuilder();
    
            var keyBuilding = true;
    
            var keys = new List<string>();
            var values = new List<string>();
    
            for (var i = 0; i < rawKey.Length; i++)
            {
                var currentChar = rawKey[i];
                var nextChar = i < rawKey.Length - 1 ? rawKey[i + 1] : (char?)null;
                var prevChar = i > 0 ? rawKey[i - 1] : (char?)null;
    
                if (currentChar == '=' && keyBuilding)
                {
                    keyBuilding = false;
    
                    keys.Add(keyBuilder.ToString());
    
                    keyBuilder.Clear();
    
                    continue;
                }
    
                if (!keyBuilding && currentChar == ',' && prevChar.HasValue && prevChar.Value == '\'')
                {
                    keyBuilding = true;
    
                    values.Add(valueBuilder.ToString());
    
                    valueBuilder.Clear();
    
                    continue;
                }
    
                if (keyBuilding)
                {
                    keyBuilder.Append(currentChar);
                }
                else
                {
                    valueBuilder.Append(currentChar);
                }
    
                if (!keyBuilding && !nextChar.HasValue)
                {
                    values.Add(valueBuilder.ToString());
    
                    valueBuilder.Clear();
                }
            }
    
            if (keys.Count != values.Count)
            {
                throw new InvalidDataException("The key could not be formatted into valid pairs. key was: " + rawKey);
            }
    
            for (var i = 0; i < keys.Count; i++)
            {
                formattedKeys.Add(keys[i].Trim(), values[i].Trim());
            }
    
            return formattedKeys;
        }
    }
    

    最后一个类做了一些mumbo jumbo来处理“,”在密钥本身的数据里面,还没有在数据中用“'”进行测试,我建议你这样做 .

相关问题