当我在我的任务中遇到这个问题时,我被谷歌搜索了。我从这个 thread 中得到了干净的实现
首先您需要以正确的方式验证 edm modelbuilder 以进行 expand
对象
你必须为博客和外键releations注册edm模型。然后才能成功
示例
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Blog>("blog");
builder.EntitySet<Profile>("profile");//ForeignKey releations of blog
builder.EntitySet<user>("user");//ForeignKey releations of profile
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
那么你需要开发这个格式化程序..example源代码我们here
申请我的英语..
首先,我们需要创建一个派生自 MediaTypeFormatter 抽象类的类。这是带有构造函数的类:
public class CSVMediaTypeFormatter : MediaTypeFormatter {
public CSVMediaTypeFormatter() {
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}
public CSVMediaTypeFormatter(
MediaTypeMapping mediaTypeMapping) : this() {
MediaTypeMappings.Add(mediaTypeMapping);
}
public CSVMediaTypeFormatter(
IEnumerable<MediaTypeMapping> mediaTypeMappings) : this() {
foreach (var mediaTypeMapping in mediaTypeMappings) {
MediaTypeMappings.Add(mediaTypeMapping);
}
}
}
以上,无论您使用哪个构造函数,我们总是添加 text/csv 媒体类型以支持此格式化程序。我们还允许注入自定义 MediaTypeMappings。
现在,我们需要重写两个方法:MediaTypeFormatter.CanWriteType 和 MediaTypeFormatter.OnWriteToStreamAsync。
首先,这里是 CanWriteType 方法的实现。这个方法需要做的是确定这个格式化程序是否支持对象的类型以便编写它。
protected override bool CanWriteType(Type type) {
if (type == null)
throw new ArgumentNullException("type");
return isTypeOfIEnumerable(type);
}
private bool isTypeOfIEnumerable(Type type) {
foreach (Type interfaceType in type.GetInterfaces()) {
if (interfaceType == typeof(IEnumerable))
return true;
}
return false;
}
这里的作用是检查对象是否实现了 IEnumerable 接口。如果是这样,那么它很酷并且可以格式化对象。如果不是,它将返回 false 并且框架将忽略该特定请求的格式化程序。
最后,这是实际的实现。我们需要在这里做一些反射工作,以便从作为对象类型的 value 参数中获取属性名称和值:
protected override Task OnWriteToStreamAsync(
Type type,
object value,
Stream stream,
HttpContentHeaders contentHeaders,
FormatterContext formatterContext,
TransportContext transportContext) {
writeStream(type, value, stream, contentHeaders);
var tcs = new TaskCompletionSource<int>();
tcs.SetResult(0);
return tcs.Task;
}
private void writeStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders) {
//NOTE: We have check the type inside CanWriteType method
//If request comes this far, the type is IEnumerable. We are safe.
Type itemType = type.GetGenericArguments()[0];
StringWriter _stringWriter = new StringWriter();
_stringWriter.WriteLine(
string.Join<string>(
",", itemType.GetProperties().Select(x => x.Name )
)
);
foreach (var obj in (IEnumerable<object>)value) {
var vals = obj.GetType().GetProperties().Select(
pi => new {
Value = pi.GetValue(obj, null)
}
);
string _valueLine = string.Empty;
foreach (var val in vals) {
if (val.Value != null) {
var _val = val.Value.ToString();
//Check if the value contans a comma and place it in quotes if so
if (_val.Contains(","))
_val = string.Concat("\"", _val, "\"");
//Replace any \r or \n special characters from a new line with a space
if (_val.Contains("\r"))
_val = _val.Replace("\r", " ");
if (_val.Contains("\n"))
_val = _val.Replace("\n", " ");
_valueLine = string.Concat(_valueLine, _val, ",");
} else {
_valueLine = string.Concat(string.Empty, ",");
}
}
_stringWriter.WriteLine(_valueLine.TrimEnd(','));
}
var streamWriter = new StreamWriter(stream);
streamWriter.Write(_stringWriter.ToString());
}
我们已经完成了部分工作。现在,我们需要利用它。我在 Global.asax Application_Start 方法中使用以下代码将此格式化程序注册到管道中:
GlobalConfiguration.Configuration.Formatters.Add(
new CSVMediaTypeFormatter(
new QueryStringMapping("format", "csv", "text/csv")
)
);
在我的示例应用程序中,当您导航到 /api/cars?format=csv 时,它会为您提供一个 CSV 文件,但没有扩展名。继续并添加 csv 扩展名。然后,用 Excel 打开它,你应该会看到类似下面的内容: