是否有任何替代方式:EF4 LINQ包含(字符串)替代硬编码的字符串?
Organizations.Include("Assets").Where(o => o.Id == id).Single()
我想看到的东西,如:
Organizations.Include(o => o.Assets).Where(o => o.Id == id).Single()
避免硬编码字符串“资产”。
是否有任何替代方式:EF4 LINQ包含(字符串)替代硬编码的字符串?
Organizations.Include("Assets").Where(o => o.Id == id).Single()
我想看到的东西,如:
Organizations.Include(o => o.Assets).Where(o => o.Id == id).Single()
避免硬编码字符串“资产”。
对于实体框架1.0,我为此创建了一些扩展方法。
public static class EntityFrameworkIncludeExtension
{
public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, StructuralObject>> fetch)
{
return src.Include(CreateFetchingStrategyDescription(fetch));
}
public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch)
{
return src.Include(CreateFetchingStrategyDescription(fetch));
}
public static ObjectQuery<T> Include<T, TFectchedCollection>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<TFectchedCollection>>> fetch)
{
return src.Include(CreateFetchingStrategyDescription(fetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, Object>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, Object>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
private static String CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(
Expression<Func<TFetchEntity, TFetchResult>> fetch)
{
fetch = (Expression<Func<TFetchEntity, TFetchResult>>)FixedWrappedMemberAcces.ForExpression(fetch);
if (fetch.Parameters.Count > 1)
throw new ArgumentException("CreateFetchingStrategyDescription support only " +
"one parameter in a dynamic expression!");
int dot = fetch.Body.ToString().IndexOf(".") + 1;
return fetch.Body.ToString().Remove(0, dot);
}
private static String CreateFetchingStrategyDescription<T>(Expression<Func<T, Object>> fetch)
{
return CreateFetchingStrategyDescription<T, Object>(fetch);
}
private static String CombineFetchingStrategies<T, TFetchedEntity>(
Expression<Func<T, Object>> fetch, Expression<Func<TFetchedEntity, Object>> secondFetch)
{
return CombineFetchingStrategies<T, Object, TFetchedEntity, Object>(fetch, secondFetch);
}
private static String CombineFetchingStrategies<TFetchEntity, TFetchResult, TFetchedEntity, TSecondFetchResult>(
Expression<Func<TFetchEntity, TFetchResult>> fetch, Expression<Func<TFetchedEntity, TSecondFetchResult>> secondFetch)
{
return CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(fetch) + "." +
CreateFetchingStrategyDescription<TFetchedEntity, TSecondFetchResult>(secondFetch);
}
}
用法:
Orders.Include(o => o.Product); // generates .Include("Product")
Orders.Include(o => o.Product.Category); // generates .Include("Product.Category")
Orders.Include(o => o.History); // a 1-* reference => .Include("History")
// fetch all the orders, and in the orders collection.
// also include the user reference so: .Include("History.User")
// but because history is an collection you cant write o => o.History.User,
// there is an overload which accepts a second parameter to describe the fetching
// inside the collection.
Orders.Include(o => o.History, h => h.User);
我还没有EF4.0测试这一点,但我期望它的工作。
这很容易做到,使用表达式:
public static class ObjectQueryExtensions
{
public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector)
{
MemberExpression memberExpr = selector.Body as MemberExpression;
if (memberExpr != null)
{
return objectQuery.Include(memberExpr.Member.Name);
}
throw new ArgumentException("The expression must be a MemberExpression", "selector");
}
}
你可以在你的问题
UPDATE
改进版本,它支持多个正是用它作为例子链式属性:
public static class ObjectQueryExtensions
{
public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector)
{
string propertyPath = GetPropertyPath(selector);
return objectQuery.Include(propertyPath);
}
public static string GetPropertyPath<T, TProperty>(Expression<Func<T, TProperty>> selector)
{
StringBuilder sb = new StringBuilder();
MemberExpression memberExpr = selector.Body as MemberExpression;
while (memberExpr != null)
{
string name = memberExpr.Member.Name;
if (sb.Length > 0)
name = name + ".";
sb.Insert(0, name);
if (memberExpr.Expression is ParameterExpression)
return sb.ToString();
memberExpr = memberExpr.Expression as MemberExpression;
}
throw new ArgumentException("The expression must be a MemberExpression", "selector");
}
}
实施例:
var query = X.Include(x => x.Foo.Bar.Baz) // equivalent to X.Include("Foo.Bar.Baz")
是的,我也是这样开始的,但是它的缺点是编译时会产生运行时异常。因为你不会限制TProperty的功能。 EF只在Include中喜欢它自己的属性,因为它不能将自己声明的属性映射到它的数据模型(至少在EF1.0中)。这就是为什么我包含所有重载,这些重载限制了表达式为包含提供编译时安全性。尽管LINQ中的所有其他表达式仍然可以生成运行时错误。 – 2010-04-06 16:54:59
同意......不幸的是,您不能在编译时检查*所有内容*。开发人员有责任确保表达式真正返回映射属性 – 2010-04-06 17:01:36
同意,这是一种折衷。 – 2010-04-06 17:08:25
好消息,EF4 CTP4目前支持此功能。
另一种选择是使用TT模板在类中包含内部部分类。
T4代码:
<#
region.Begin("Member Constants");
#>
public partial class <#=code.Escape(entity)#>Members
{
<#
foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity))
{
bool isForeignKey = entity.NavigationProperties.Any(np=>np.GetDependentProperties().Contains(edmProperty));
bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null);
bool generateAutomaticProperty = false;
#>
public const string <#=code.Escape(edmProperty)#> = "<#=code.Escape(edmProperty)#>";
<#
}
#>
}
<#
region.End();
#>
将产生类似:
#region Member Constants
public partial class ContactMembers
{
public const string ID = "ID";
public const string OriginalSourceID = "OriginalSourceID";
public const string EnabledInd = "EnabledInd";
public const string EffectiveDTM = "EffectiveDTM";
public const string EndDTM = "EndDTM";
public const string EnterDTM = "EnterDTM";
public const string EnterUserID = "EnterUserID";
public const string LastChgDTM = "LastChgDTM";
public const string LastChgUserID = "LastChgUserID";
}
#endregion
在EF 4.1,对于这个built-in extension method。
您必须在项目中引用“EntityFramework”程序集(其中EF 4.1存在)并使用System.Data.Entity。
using System.Data.Entity;
如果要包含嵌套的实体,你做这样的:如果
db.Customers.Include(c => c.Orders.Select(o => o.LineItems))
不知道这个工程的EF4.0实体(ObjectContext的基础)。
刚刚测试过它,它的工作原理!我有自我跟踪实体(因为我不能在我的WCF场景中使用DbContext POCO实体)。他们是基于ObjectContext的,我只需要添加EntityFramework Reference(通过NuGet),把你使用的行放在这里,我就可以使用扩展包含方法! – 2011-09-14 11:00:48
问题是,我绝对没有理由添加除此之外的EF 4.1引用编译时间检查我的加载...我猜是可以接受添加它,因为它只用于服务器端?也许我会找到其他我将使用的扩展方法。 – 2011-09-14 11:06:18
EF4.1扩展方法只是解析您提供的表达式,然后将其转换为基于字符串的Include调用。所以它有效地将上面的例子转换成'.Include(“Orders.LineItems”)'。如果你真的不想安装EF4.1,你或许可以找到其他人写了扩展方法来做同样的事情。在4.1之前,我写了自己的(借用其他例子),我可以告诉你它不是太好玩。 (表达式API是...奇怪。)我很高兴能够访问这个内置的方法。 – 2011-09-16 20:43:52
另一个解决方案是使用EntitySet.Name检索实体名称。
您的代码将是:
var context = new DBContext();
context.Organizations.Include(context.Assets.EntitySet.Name).Where(o => o.Id == id).Single()
它也应该适用于EF 4.0,但只有当你使用设计器生成的类。它不适用于POCO实体,因为它们不从'StructuralObject'继承。 – 2010-04-06 16:58:55
由于'FixedWrappedMemberAcces'是未知的,所以很遗憾不能编译(.Net 4)。 – 2010-09-16 07:56:22
啊,它是从我们的源复制和粘贴的受害者..事实是,你不需要这条线......它只是解决包裹字段的问题(请参阅http://landman-code.blogspot .com/2010/08/protection-your-entitycollections-from.html了解更多信息),但是你不需要这些工具。 – 2010-09-16 09:48:18