我正在使用一个不幸设计的数据库,我需要将8列转换为行的“子列表”,我正在使用实体框架。我想在SQL / IQueryable中完成所有操作,因此可以在之后应用过滤等。
我现在有以下代码
public static IQueryable<DTOs.Customer> ToDTO(this IQueryable<Customer> query, DBContext context)
{
return from c in query
select new DTOs.Customer
{
CustomerID = c.CustomerID,
AnalysisCodes = new List<AnalysisCodeWithValue>()
{
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode1))
.Select(
a => new AnalysisCodeWithValue() {Name = a.AnalysisCode.Name, Value = c.AnalysisCode1})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode2))
.Select(a => new AnalysisCodeWithValue {Name = a.AnalysisCode.Name, Value = c.AnalysisCode2})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode3))
.Select(
a => new AnalysisCodeWithValue() {Name = a.AnalysisCode.Name, Value = c.AnalysisCode3})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode4))
.Select(a => new AnalysisCodeWithValue {Name = a.AnalysisCode.Name, Value = c.AnalysisCode4})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode5))
.Select(
a => new AnalysisCodeWithValue() {Name = a.AnalysisCode.Name, Value = c.AnalysisCode5})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode6))
.Select(a => new AnalysisCodeWithValue {Name = a.AnalysisCode.Name, Value = c.AnalysisCode6})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode7))
.Select(
a => new AnalysisCodeWithValue() {Name = a.AnalysisCode.Name, Value = c.AnalysisCode7})
.FirstOrDefault(),
context.AnalysisCodeMapping.Where(a => a.PropertyName == nameof(c.AnalysisCode8))
.Select(a => new AnalysisCodeWithValue {Name = a.AnalysisCode.Name, Value = c.AnalysisCode8})
.FirstOrDefault()
}.Where(a => a != null && a.Value != string.Empty)
};
}
这是有效的,但是你可以看到每个'分析代码'都有很多重复。我尝试使子查询成为一个获取属性名称和值的方法,但EF抱怨它无法将方法调用转换为SQL。有什么办法可以整理一下以避免复杂的重复吗?
我也知道这可能很慢,所以我对任何其他建议持开放态度。遗憾的是,我们无法在服务器上创建任何视图,功能或SP
谢谢:)
答案 0 :(得分:0)
您似乎拥有一系列客户和一系列客户的字符串属性。
对于每个客户,您需要一个新对象。这个新对象包含客户的ID和AnalysisCodeWithValues列表;每个属性一个AnalysisCodeWithValue,包含属性的名称和此属性的客户值
让我们使用扩展方法。见extension methods demystified
一个过程,它接受客户以及需要从该客户提取的一系列PropertyInfos。返回值是AnalysisCodeWithValues的序列;每个PropertyInfo一个AnalysisCodeWithValue,包含属性的名称和字符串值。
public static IEnumerable<AnalysisCodeWithValue> ExtractProperties(this Customer customer,
IEnumerable<PropertyInfo> properties)
{
// TODO: add Argument NULL checks
foreach (PropertyInfo property in properties)
{
yield return new AnalysisCodeWithValues()
{
Name = property.Name,
Value = property.GetValue(customer).ToString(),
};
}
}
显然,如果GetValue返回null,这将导致问题。您可以使用null条件运算符来防止出现问题:
Value = property.GetValue(customer)?.ToString() ?? String.Empty,
但是,既然你对空值不感兴趣,我会选择:
foreach (PropertyInfo property in properties)
{
string stringValue = property.GetValue(customer)?.ToString();
if (!String.IsNullOrEmpty(stringValue))
{
yield return new AnalysisCodeWithValues()
{
Name = property.Name,
Value = stringValue,
};
}
}
用法是:
Customer customer = ...
var allCustomerProperties = typeof(Customer).GetProperties()
.Where(property => property.CanRead);
var customerPropertyValues = customer.ExtractProperties(allCustomerProperties);
同样,如果您有一系列Customers和一系列要提取的属性:
var result = myCustomers.Select(customer => new
{
CustomerId = customer.Id,
AnalysisCodes = customer.ExtractProperties(allCustomerProperties)
.ToList();
}
如果您想要客户的大部分属性,这是一个很好的解决方案。您不能进行编译器无法检测到的任何输入错误,因为您没有键入属性的名称
如果您只想要客户的一些属性,这将无效。在那种情况下,我会去寻找提取价值的代表。额外奖励:您可以请求代理中的任何值,而不仅仅是属性。您可以调用任何函数,甚至可以组合属性。
缺点:您必须在最终结果中键入所需的每个值的名称。显然,因为你想创建自己的价值观。
输入:KeyValuePairs序列,其中Key是AnalysisCodeWithValue的名称,值是从Customer中提取值的委托。像这样:
var propertyToGet = new KeyValuePair<...>(
"AnalysisCode1",
customer => customer.AnalsysCode1);
或更难的价值:
var propertyToGet = new KeyValuePair<...>(
"Full name",
customer => customer.FirstName + " " + customer.LastName);
该功能将如下:
public static IEnumerable<AnalysisCodeWithValue> ExtractProperties(this Customer customer,
IEnumerable<KeyValuePair<string, Func<Customer, object>> delegates)
{
foreach (var requestedProperty in delegates)
{
var propertyValue = requestedProperty.Value(customer);
string stringValue = propertyValue?.ToString();
if (!String.IsNullOrEmpty(stringValue))
{
yield return new AnalysisCodeWithValues()
{
Name = requestedProperty.Key,
Value = stringValue,
};
}
}
}
用法:
var requestedProperties = new KeyValuePair<string, Func<Customer, object>>[]
{
new KeyValuePair<string, Func<Customer, object>>(
"Full customer name",
customer => customer.FirstName + " " + customer.LastName),
new KeyValuePair<string, Func<Customer, object>>(
"Birthday",
customer => customer.Birthday),
new KeyValuePair<string, Func<Customer, object>>(
{
"Certificates after Year 2000",
customer => customer.CalculateCertificates(new DateTime(2000, 1, 1,))
},
}
优点:您可以使用您喜欢的描述;你可以使用任何你喜欢的功能。如果输入错误,编译器将检测到它。
缺点:要输入更多代码。
由您来决定优势是否具有劣势